LCOV - code coverage report
Current view: top level - journal - journal-verify.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 378 718 52.6 %
Date: 2019-08-22 15:41:25 Functions: 13 13 100.0 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include <fcntl.h>
       4             : #include <stddef.h>
       5             : #include <sys/mman.h>
       6             : #include <unistd.h>
       7             : 
       8             : #include "alloc-util.h"
       9             : #include "compress.h"
      10             : #include "fd-util.h"
      11             : #include "fileio.h"
      12             : #include "fs-util.h"
      13             : #include "journal-authenticate.h"
      14             : #include "journal-def.h"
      15             : #include "journal-file.h"
      16             : #include "journal-verify.h"
      17             : #include "lookup3.h"
      18             : #include "macro.h"
      19             : #include "terminal-util.h"
      20             : #include "tmpfile-util.h"
      21             : #include "util.h"
      22             : 
      23        8451 : static void draw_progress(uint64_t p, usec_t *last_usec) {
      24             :         unsigned n, i, j, k;
      25             :         usec_t z, x;
      26             : 
      27        8451 :         if (!on_tty())
      28        8451 :                 return;
      29             : 
      30           0 :         z = now(CLOCK_MONOTONIC);
      31           0 :         x = *last_usec;
      32             : 
      33           0 :         if (x != 0 && x + 40 * USEC_PER_MSEC > z)
      34           0 :                 return;
      35             : 
      36           0 :         *last_usec = z;
      37             : 
      38           0 :         n = (3 * columns()) / 4;
      39           0 :         j = (n * (unsigned) p) / 65535ULL;
      40           0 :         k = n - j;
      41             : 
      42           0 :         fputs("\r", stdout);
      43           0 :         if (colors_enabled())
      44           0 :                 fputs("\x1B[?25l" ANSI_HIGHLIGHT_GREEN, stdout);
      45             : 
      46           0 :         for (i = 0; i < j; i++)
      47           0 :                 fputs("\xe2\x96\x88", stdout);
      48             : 
      49           0 :         fputs(ansi_normal(), stdout);
      50             : 
      51           0 :         for (i = 0; i < k; i++)
      52           0 :                 fputs("\xe2\x96\x91", stdout);
      53             : 
      54           0 :         printf(" %3"PRIu64"%%", 100U * p / 65535U);
      55             : 
      56           0 :         fputs("\r", stdout);
      57           0 :         if (colors_enabled())
      58           0 :                 fputs("\x1B[?25h", stdout);
      59             : 
      60           0 :         fflush(stdout);
      61             : }
      62             : 
      63        8451 : static uint64_t scale_progress(uint64_t scale, uint64_t p, uint64_t m) {
      64             :         /* Calculates scale * p / m, but handles m == 0 safely, and saturates.
      65             :          * Currently all callers use m >= 1, but we keep the check to be defensive.
      66             :          */
      67             : 
      68        8451 :         if (p >= m || m == 0) // lgtm[cpp/constant-comparison]
      69           1 :                 return scale;
      70             : 
      71        8450 :         return scale * p / m;
      72             : }
      73             : 
      74           1 : static void flush_progress(void) {
      75             :         unsigned n, i;
      76             : 
      77           1 :         if (!on_tty())
      78           1 :                 return;
      79             : 
      80           0 :         n = (3 * columns()) / 4;
      81             : 
      82           0 :         putchar('\r');
      83             : 
      84           0 :         for (i = 0; i < n + 5; i++)
      85           0 :                 putchar(' ');
      86             : 
      87           0 :         putchar('\r');
      88           0 :         fflush(stdout);
      89             : }
      90             : 
      91             : #define debug(_offset, _fmt, ...) do {                                  \
      92             :                 flush_progress();                                       \
      93             :                 log_debug(OFSfmt": " _fmt, _offset, ##__VA_ARGS__);     \
      94             :         } while (0)
      95             : 
      96             : #define warning(_offset, _fmt, ...) do {                                \
      97             :                 flush_progress();                                       \
      98             :                 log_warning(OFSfmt": " _fmt, _offset, ##__VA_ARGS__);   \
      99             :         } while (0)
     100             : 
     101             : #define error(_offset, _fmt, ...) do {                                  \
     102             :                 flush_progress();                                       \
     103             :                 log_error(OFSfmt": " _fmt, (uint64_t)_offset, ##__VA_ARGS__); \
     104             :         } while (0)
     105             : 
     106             : #define error_errno(_offset, error, _fmt, ...) do {               \
     107             :                 flush_progress();                                       \
     108             :                 log_error_errno(error, OFSfmt": " _fmt, (uint64_t)_offset, ##__VA_ARGS__); \
     109             :         } while (0)
     110             : 
     111        6396 : static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o) {
     112             :         uint64_t i;
     113             : 
     114        6396 :         assert(f);
     115        6396 :         assert(offset);
     116        6396 :         assert(o);
     117             : 
     118             :         /* This does various superficial tests about the length an
     119             :          * possible field values. It does not follow any references to
     120             :          * other objects. */
     121             : 
     122        6396 :         if ((o->object.flags & OBJECT_COMPRESSED_XZ) &&
     123           0 :             o->object.type != OBJECT_DATA) {
     124           0 :                 error(offset, "Found compressed object that isn't of type DATA, which is not allowed.");
     125           0 :                 return -EBADMSG;
     126             :         }
     127             : 
     128        6396 :         switch (o->object.type) {
     129             : 
     130          77 :         case OBJECT_DATA: {
     131             :                 uint64_t h1, h2;
     132             :                 int compression, r;
     133             : 
     134          77 :                 if (le64toh(o->data.entry_offset) == 0)
     135           0 :                         warning(offset, "Unused data (entry_offset==0)");
     136             : 
     137          77 :                 if ((le64toh(o->data.entry_offset) == 0) ^ (le64toh(o->data.n_entries) == 0)) {
     138           0 :                         error(offset, "Bad n_entries: %"PRIu64, le64toh(o->data.n_entries));
     139           0 :                         return -EBADMSG;
     140             :                 }
     141             : 
     142          77 :                 if (le64toh(o->object.size) - offsetof(DataObject, payload) <= 0) {
     143           0 :                         error(offset, "Bad object size (<= %zu): %"PRIu64,
     144             :                               offsetof(DataObject, payload),
     145             :                               le64toh(o->object.size));
     146           0 :                         return -EBADMSG;
     147             :                 }
     148             : 
     149          77 :                 h1 = le64toh(o->data.hash);
     150             : 
     151          77 :                 compression = o->object.flags & OBJECT_COMPRESSION_MASK;
     152          77 :                 if (compression) {
     153           0 :                         _cleanup_free_ void *b = NULL;
     154           0 :                         size_t alloc = 0, b_size;
     155             : 
     156           0 :                         r = decompress_blob(compression,
     157           0 :                                             o->data.payload,
     158           0 :                                             le64toh(o->object.size) - offsetof(Object, data.payload),
     159             :                                             &b, &alloc, &b_size, 0);
     160           0 :                         if (r < 0) {
     161           0 :                                 error_errno(offset, r, "%s decompression failed: %m",
     162             :                                             object_compressed_to_string(compression));
     163           0 :                                 return r;
     164             :                         }
     165             : 
     166           0 :                         h2 = hash64(b, b_size);
     167             :                 } else
     168          77 :                         h2 = hash64(o->data.payload, le64toh(o->object.size) - offsetof(Object, data.payload));
     169             : 
     170          77 :                 if (h1 != h2) {
     171           0 :                         error(offset, "Invalid hash (%08"PRIx64" vs. %08"PRIx64, h1, h2);
     172           0 :                         return -EBADMSG;
     173             :                 }
     174             : 
     175          77 :                 if (!VALID64(le64toh(o->data.next_hash_offset)) ||
     176          77 :                     !VALID64(le64toh(o->data.next_field_offset)) ||
     177          77 :                     !VALID64(le64toh(o->data.entry_offset)) ||
     178          77 :                     !VALID64(le64toh(o->data.entry_array_offset))) {
     179           0 :                         error(offset, "Invalid offset (next_hash_offset="OFSfmt", next_field_offset="OFSfmt", entry_offset="OFSfmt", entry_array_offset="OFSfmt,
     180             :                               le64toh(o->data.next_hash_offset),
     181             :                               le64toh(o->data.next_field_offset),
     182             :                               le64toh(o->data.entry_offset),
     183             :                               le64toh(o->data.entry_array_offset));
     184           0 :                         return -EBADMSG;
     185             :                 }
     186             : 
     187          77 :                 break;
     188             :         }
     189             : 
     190           1 :         case OBJECT_FIELD:
     191           1 :                 if (le64toh(o->object.size) - offsetof(FieldObject, payload) <= 0) {
     192           0 :                         error(offset,
     193             :                               "Bad field size (<= %zu): %"PRIu64,
     194             :                               offsetof(FieldObject, payload),
     195             :                               le64toh(o->object.size));
     196           0 :                         return -EBADMSG;
     197             :                 }
     198             : 
     199           1 :                 if (!VALID64(le64toh(o->field.next_hash_offset)) ||
     200           1 :                     !VALID64(le64toh(o->field.head_data_offset))) {
     201           0 :                         error(offset,
     202             :                               "Invalid offset (next_hash_offset="OFSfmt", head_data_offset="OFSfmt,
     203             :                               le64toh(o->field.next_hash_offset),
     204             :                               le64toh(o->field.head_data_offset));
     205           0 :                         return -EBADMSG;
     206             :                 }
     207           1 :                 break;
     208             : 
     209        6000 :         case OBJECT_ENTRY:
     210        6000 :                 if ((le64toh(o->object.size) - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0) {
     211           0 :                         error(offset,
     212             :                               "Bad entry size (<= %zu): %"PRIu64,
     213             :                               offsetof(EntryObject, items),
     214             :                               le64toh(o->object.size));
     215           0 :                         return -EBADMSG;
     216             :                 }
     217             : 
     218        6000 :                 if ((le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0) {
     219           0 :                         error(offset,
     220             :                               "Invalid number items in entry: %"PRIu64,
     221             :                               (le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem));
     222           0 :                         return -EBADMSG;
     223             :                 }
     224             : 
     225        6000 :                 if (le64toh(o->entry.seqnum) <= 0) {
     226           0 :                         error(offset,
     227             :                               "Invalid entry seqnum: %"PRIx64,
     228             :                               le64toh(o->entry.seqnum));
     229           0 :                         return -EBADMSG;
     230             :                 }
     231             : 
     232        6000 :                 if (!VALID_REALTIME(le64toh(o->entry.realtime))) {
     233           0 :                         error(offset,
     234             :                               "Invalid entry realtime timestamp: %"PRIu64,
     235             :                               le64toh(o->entry.realtime));
     236           0 :                         return -EBADMSG;
     237             :                 }
     238             : 
     239        6000 :                 if (!VALID_MONOTONIC(le64toh(o->entry.monotonic))) {
     240           0 :                         error(offset,
     241             :                               "Invalid entry monotonic timestamp: %"PRIu64,
     242             :                               le64toh(o->entry.monotonic));
     243           0 :                         return -EBADMSG;
     244             :                 }
     245             : 
     246       12000 :                 for (i = 0; i < journal_file_entry_n_items(o); i++) {
     247        6000 :                         if (le64toh(o->entry.items[i].object_offset) == 0 ||
     248        6000 :                             !VALID64(le64toh(o->entry.items[i].object_offset))) {
     249           0 :                                 error(offset,
     250             :                                       "Invalid entry item (%"PRIu64"/%"PRIu64" offset: "OFSfmt,
     251             :                                       i, journal_file_entry_n_items(o),
     252             :                                       le64toh(o->entry.items[i].object_offset));
     253           0 :                                 return -EBADMSG;
     254             :                         }
     255             :                 }
     256             : 
     257        6000 :                 break;
     258             : 
     259           2 :         case OBJECT_DATA_HASH_TABLE:
     260             :         case OBJECT_FIELD_HASH_TABLE:
     261           2 :                 if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) % sizeof(HashItem) != 0 ||
     262           2 :                     (le64toh(o->object.size) - offsetof(HashTableObject, items)) / sizeof(HashItem) <= 0) {
     263           0 :                         error(offset,
     264             :                               "Invalid %s hash table size: %"PRIu64,
     265             :                               o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field",
     266             :                               le64toh(o->object.size));
     267           0 :                         return -EBADMSG;
     268             :                 }
     269             : 
     270        2382 :                 for (i = 0; i < journal_file_hash_table_n_items(o); i++) {
     271        2380 :                         if (o->hash_table.items[i].head_hash_offset != 0 &&
     272          76 :                             !VALID64(le64toh(o->hash_table.items[i].head_hash_offset))) {
     273           0 :                                 error(offset,
     274             :                                       "Invalid %s hash table item (%"PRIu64"/%"PRIu64") head_hash_offset: "OFSfmt,
     275             :                                       o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field",
     276             :                                       i, journal_file_hash_table_n_items(o),
     277             :                                       le64toh(o->hash_table.items[i].head_hash_offset));
     278           0 :                                 return -EBADMSG;
     279             :                         }
     280        2380 :                         if (o->hash_table.items[i].tail_hash_offset != 0 &&
     281          76 :                             !VALID64(le64toh(o->hash_table.items[i].tail_hash_offset))) {
     282           0 :                                 error(offset,
     283             :                                       "Invalid %s hash table item (%"PRIu64"/%"PRIu64") tail_hash_offset: "OFSfmt,
     284             :                                       o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field",
     285             :                                       i, journal_file_hash_table_n_items(o),
     286             :                                       le64toh(o->hash_table.items[i].tail_hash_offset));
     287           0 :                                 return -EBADMSG;
     288             :                         }
     289             : 
     290        4760 :                         if ((o->hash_table.items[i].head_hash_offset != 0) !=
     291        2380 :                             (o->hash_table.items[i].tail_hash_offset != 0)) {
     292           0 :                                 error(offset,
     293             :                                       "Invalid %s hash table item (%"PRIu64"/%"PRIu64"): head_hash_offset="OFSfmt" tail_hash_offset="OFSfmt,
     294             :                                       o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field",
     295             :                                       i, journal_file_hash_table_n_items(o),
     296             :                                       le64toh(o->hash_table.items[i].head_hash_offset),
     297             :                                       le64toh(o->hash_table.items[i].tail_hash_offset));
     298           0 :                                 return -EBADMSG;
     299             :                         }
     300             :                 }
     301             : 
     302           2 :                 break;
     303             : 
     304         316 :         case OBJECT_ENTRY_ARRAY:
     305         316 :                 if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) % sizeof(le64_t) != 0 ||
     306         316 :                     (le64toh(o->object.size) - offsetof(EntryArrayObject, items)) / sizeof(le64_t) <= 0) {
     307           0 :                         error(offset,
     308             :                               "Invalid object entry array size: %"PRIu64,
     309             :                               le64toh(o->object.size));
     310           0 :                         return -EBADMSG;
     311             :                 }
     312             : 
     313         316 :                 if (!VALID64(le64toh(o->entry_array.next_entry_array_offset))) {
     314           0 :                         error(offset,
     315             :                               "Invalid object entry array next_entry_array_offset: "OFSfmt,
     316             :                               le64toh(o->entry_array.next_entry_array_offset));
     317           0 :                         return -EBADMSG;
     318             :                 }
     319             : 
     320       18724 :                 for (i = 0; i < journal_file_entry_array_n_items(o); i++)
     321       18408 :                         if (le64toh(o->entry_array.items[i]) != 0 &&
     322       11923 :                             !VALID64(le64toh(o->entry_array.items[i]))) {
     323           0 :                                 error(offset,
     324             :                                       "Invalid object entry array item (%"PRIu64"/%"PRIu64"): "OFSfmt,
     325             :                                       i, journal_file_entry_array_n_items(o),
     326             :                                       le64toh(o->entry_array.items[i]));
     327           0 :                                 return -EBADMSG;
     328             :                         }
     329             : 
     330         316 :                 break;
     331             : 
     332           0 :         case OBJECT_TAG:
     333           0 :                 if (le64toh(o->object.size) != sizeof(TagObject)) {
     334           0 :                         error(offset,
     335             :                               "Invalid object tag size: %"PRIu64,
     336             :                               le64toh(o->object.size));
     337           0 :                         return -EBADMSG;
     338             :                 }
     339             : 
     340           0 :                 if (!VALID_EPOCH(le64toh(o->tag.epoch))) {
     341           0 :                         error(offset,
     342             :                               "Invalid object tag epoch: %"PRIu64,
     343             :                               le64toh(o->tag.epoch));
     344           0 :                         return -EBADMSG;
     345             :                 }
     346             : 
     347           0 :                 break;
     348             :         }
     349             : 
     350        6396 :         return 0;
     351             : }
     352             : 
     353        6393 : static int write_uint64(int fd, uint64_t p) {
     354             :         ssize_t k;
     355             : 
     356        6393 :         k = write(fd, &p, sizeof(p));
     357        6393 :         if (k < 0)
     358           0 :                 return -errno;
     359        6393 :         if (k != sizeof(p))
     360           0 :                 return -EIO;
     361             : 
     362        6393 :         return 0;
     363             : }
     364             : 
     365       18393 : static int contains_uint64(MMapCache *m, MMapFileDescriptor *f, uint64_t n, uint64_t p) {
     366             :         uint64_t a, b;
     367             :         int r;
     368             : 
     369       18393 :         assert(m);
     370       18393 :         assert(f);
     371             : 
     372             :         /* Bisection ... */
     373             : 
     374       18393 :         a = 0; b = n;
     375      175079 :         while (a < b) {
     376             :                 uint64_t c, *z;
     377             : 
     378      175079 :                 c = (a + b) / 2;
     379             : 
     380      175079 :                 r = mmap_cache_get(m, f, PROT_READ|PROT_WRITE, 0, false, c * sizeof(uint64_t), sizeof(uint64_t), NULL, (void **) &z, NULL);
     381      175079 :                 if (r < 0)
     382       18393 :                         return r;
     383             : 
     384      175079 :                 if (*z == p)
     385       18393 :                         return 1;
     386             : 
     387      156686 :                 if (a + 1 >= b)
     388           0 :                         return 0;
     389             : 
     390      156686 :                 if (p < *z)
     391       74619 :                         b = c;
     392             :                 else
     393       82067 :                         a = c;
     394             :         }
     395             : 
     396           0 :         return 0;
     397             : }
     398             : 
     399        6000 : static int entry_points_to_data(
     400             :                 JournalFile *f,
     401             :                 MMapFileDescriptor *cache_entry_fd,
     402             :                 uint64_t n_entries,
     403             :                 uint64_t entry_p,
     404             :                 uint64_t data_p) {
     405             : 
     406             :         int r;
     407             :         uint64_t i, n, a;
     408             :         Object *o;
     409        6000 :         bool found = false;
     410             : 
     411        6000 :         assert(f);
     412        6000 :         assert(cache_entry_fd);
     413             : 
     414        6000 :         if (!contains_uint64(f->mmap, cache_entry_fd, n_entries, entry_p)) {
     415           0 :                 error(data_p, "Data object references invalid entry at "OFSfmt, entry_p);
     416           0 :                 return -EBADMSG;
     417             :         }
     418             : 
     419        6000 :         r = journal_file_move_to_object(f, OBJECT_ENTRY, entry_p, &o);
     420        6000 :         if (r < 0)
     421           0 :                 return r;
     422             : 
     423        6000 :         n = journal_file_entry_n_items(o);
     424        6000 :         for (i = 0; i < n; i++)
     425        6000 :                 if (le64toh(o->entry.items[i].object_offset) == data_p) {
     426        6000 :                         found = true;
     427        6000 :                         break;
     428             :                 }
     429             : 
     430        6000 :         if (!found) {
     431           0 :                 error(entry_p, "Data object at "OFSfmt" not referenced by linked entry", data_p);
     432           0 :                 return -EBADMSG;
     433             :         }
     434             : 
     435             :         /* Check if this entry is also in main entry array. Since the
     436             :          * main entry array has already been verified we can rely on
     437             :          * its consistency. */
     438             : 
     439        6000 :         i = 0;
     440        6000 :         n = le64toh(f->header->n_entries);
     441        6000 :         a = le64toh(f->header->entry_array_offset);
     442             : 
     443       43270 :         while (i < n) {
     444             :                 uint64_t m, u;
     445             : 
     446       43270 :                 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
     447       43270 :                 if (r < 0)
     448           0 :                         return r;
     449             : 
     450       43270 :                 m = journal_file_entry_array_n_items(o);
     451       43270 :                 u = MIN(n - i, m);
     452             : 
     453       43270 :                 if (entry_p <= le64toh(o->entry_array.items[u-1])) {
     454             :                         uint64_t x, y, z;
     455             : 
     456        6000 :                         x = 0;
     457        6000 :                         y = u;
     458             : 
     459       59401 :                         while (x < y) {
     460       59401 :                                 z = (x + y) / 2;
     461             : 
     462       59401 :                                 if (le64toh(o->entry_array.items[z]) == entry_p)
     463        6000 :                                         return 0;
     464             : 
     465       53401 :                                 if (x + 1 >= y)
     466           0 :                                         break;
     467             : 
     468       53401 :                                 if (entry_p < le64toh(o->entry_array.items[z]))
     469       25679 :                                         y = z;
     470             :                                 else
     471       27722 :                                         x = z;
     472             :                         }
     473             : 
     474           0 :                         error(entry_p, "Entry object doesn't exist in main entry array");
     475           0 :                         return -EBADMSG;
     476             :                 }
     477             : 
     478       37270 :                 i += u;
     479       37270 :                 a = le64toh(o->entry_array.next_entry_array_offset);
     480             :         }
     481             : 
     482           0 :         return 0;
     483             : }
     484             : 
     485          77 : static int verify_data(
     486             :                 JournalFile *f,
     487             :                 Object *o, uint64_t p,
     488             :                 MMapFileDescriptor *cache_entry_fd, uint64_t n_entries,
     489             :                 MMapFileDescriptor *cache_entry_array_fd, uint64_t n_entry_arrays) {
     490             : 
     491             :         uint64_t i, n, a, last, q;
     492             :         int r;
     493             : 
     494          77 :         assert(f);
     495          77 :         assert(o);
     496          77 :         assert(cache_entry_fd);
     497          77 :         assert(cache_entry_array_fd);
     498             : 
     499          77 :         n = le64toh(o->data.n_entries);
     500          77 :         a = le64toh(o->data.entry_array_offset);
     501             : 
     502             :         /* Entry array means at least two objects */
     503          77 :         if (a && n < 2) {
     504           0 :                 error(p, "Entry array present (entry_array_offset="OFSfmt", but n_entries=%"PRIu64")", a, n);
     505           0 :                 return -EBADMSG;
     506             :         }
     507             : 
     508          77 :         if (n == 0)
     509           0 :                 return 0;
     510             : 
     511             :         /* We already checked that earlier */
     512          77 :         assert(o->data.entry_offset);
     513             : 
     514          77 :         last = q = le64toh(o->data.entry_offset);
     515          77 :         r = entry_points_to_data(f, cache_entry_fd, n_entries, q, p);
     516          77 :         if (r < 0)
     517           0 :                 return r;
     518             : 
     519          77 :         i = 1;
     520         385 :         while (i < n) {
     521             :                 uint64_t next, m, j;
     522             : 
     523         308 :                 if (a == 0) {
     524           0 :                         error(p, "Array chain too short");
     525           0 :                         return -EBADMSG;
     526             :                 }
     527             : 
     528         308 :                 if (!contains_uint64(f->mmap, cache_entry_array_fd, n_entry_arrays, a)) {
     529           0 :                         error(p, "Invalid array offset "OFSfmt, a);
     530           0 :                         return -EBADMSG;
     531             :                 }
     532             : 
     533         308 :                 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
     534         308 :                 if (r < 0)
     535           0 :                         return r;
     536             : 
     537         308 :                 next = le64toh(o->entry_array.next_entry_array_offset);
     538         308 :                 if (next != 0 && next <= a) {
     539           0 :                         error(p, "Array chain has cycle (jumps back from "OFSfmt" to "OFSfmt")", a, next);
     540           0 :                         return -EBADMSG;
     541             :                 }
     542             : 
     543         308 :                 m = journal_file_entry_array_n_items(o);
     544        6231 :                 for (j = 0; i < n && j < m; i++, j++) {
     545             : 
     546        5923 :                         q = le64toh(o->entry_array.items[j]);
     547        5923 :                         if (q <= last) {
     548           0 :                                 error(p, "Data object's entry array not sorted");
     549           0 :                                 return -EBADMSG;
     550             :                         }
     551        5923 :                         last = q;
     552             : 
     553        5923 :                         r = entry_points_to_data(f, cache_entry_fd, n_entries, q, p);
     554        5923 :                         if (r < 0)
     555           0 :                                 return r;
     556             : 
     557             :                         /* Pointer might have moved, reposition */
     558        5923 :                         r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
     559        5923 :                         if (r < 0)
     560           0 :                                 return r;
     561             :                 }
     562             : 
     563         308 :                 a = next;
     564             :         }
     565             : 
     566          77 :         return 0;
     567             : }
     568             : 
     569           1 : static int verify_hash_table(
     570             :                 JournalFile *f,
     571             :                 MMapFileDescriptor *cache_data_fd, uint64_t n_data,
     572             :                 MMapFileDescriptor *cache_entry_fd, uint64_t n_entries,
     573             :                 MMapFileDescriptor *cache_entry_array_fd, uint64_t n_entry_arrays,
     574             :                 usec_t *last_usec,
     575             :                 bool show_progress) {
     576             : 
     577             :         uint64_t i, n;
     578             :         int r;
     579             : 
     580           1 :         assert(f);
     581           1 :         assert(cache_data_fd);
     582           1 :         assert(cache_entry_fd);
     583           1 :         assert(cache_entry_array_fd);
     584           1 :         assert(last_usec);
     585             : 
     586           1 :         n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
     587           1 :         if (n <= 0)
     588           0 :                 return 0;
     589             : 
     590           1 :         r = journal_file_map_data_hash_table(f);
     591           1 :         if (r < 0)
     592           0 :                 return log_error_errno(r, "Failed to map data hash table: %m");
     593             : 
     594        2048 :         for (i = 0; i < n; i++) {
     595        2047 :                 uint64_t last = 0, p;
     596             : 
     597        2047 :                 if (show_progress)
     598        2047 :                         draw_progress(0xC000 + scale_progress(0x3FFF, i, n), last_usec);
     599             : 
     600        2047 :                 p = le64toh(f->data_hash_table[i].head_hash_offset);
     601        2124 :                 while (p != 0) {
     602             :                         Object *o;
     603             :                         uint64_t next;
     604             : 
     605          77 :                         if (!contains_uint64(f->mmap, cache_data_fd, n_data, p)) {
     606           0 :                                 error(p, "Invalid data object at hash entry %"PRIu64" of %"PRIu64, i, n);
     607           0 :                                 return -EBADMSG;
     608             :                         }
     609             : 
     610          77 :                         r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
     611          77 :                         if (r < 0)
     612           0 :                                 return r;
     613             : 
     614          77 :                         next = le64toh(o->data.next_hash_offset);
     615          77 :                         if (next != 0 && next <= p) {
     616           0 :                                 error(p, "Hash chain has a cycle in hash entry %"PRIu64" of %"PRIu64, i, n);
     617           0 :                                 return -EBADMSG;
     618             :                         }
     619             : 
     620          77 :                         if (le64toh(o->data.hash) % n != i) {
     621           0 :                                 error(p, "Hash value mismatch in hash entry %"PRIu64" of %"PRIu64, i, n);
     622           0 :                                 return -EBADMSG;
     623             :                         }
     624             : 
     625          77 :                         r = verify_data(f, o, p, cache_entry_fd, n_entries, cache_entry_array_fd, n_entry_arrays);
     626          77 :                         if (r < 0)
     627           0 :                                 return r;
     628             : 
     629          77 :                         last = p;
     630          77 :                         p = next;
     631             :                 }
     632             : 
     633        2047 :                 if (last != le64toh(f->data_hash_table[i].tail_hash_offset)) {
     634           0 :                         error(p, "Tail hash pointer mismatch in hash table");
     635           0 :                         return -EBADMSG;
     636             :                 }
     637             :         }
     638             : 
     639           1 :         return 0;
     640             : }
     641             : 
     642        6000 : static int data_object_in_hash_table(JournalFile *f, uint64_t hash, uint64_t p) {
     643             :         uint64_t n, h, q;
     644             :         int r;
     645        6000 :         assert(f);
     646             : 
     647        6000 :         n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
     648        6000 :         if (n <= 0)
     649           0 :                 return 0;
     650             : 
     651        6000 :         r = journal_file_map_data_hash_table(f);
     652        6000 :         if (r < 0)
     653           0 :                 return log_error_errno(r, "Failed to map data hash table: %m");
     654             : 
     655        6000 :         h = hash % n;
     656             : 
     657        6000 :         q = le64toh(f->data_hash_table[h].head_hash_offset);
     658        6170 :         while (q != 0) {
     659             :                 Object *o;
     660             : 
     661        6170 :                 if (p == q)
     662        6000 :                         return 1;
     663             : 
     664         170 :                 r = journal_file_move_to_object(f, OBJECT_DATA, q, &o);
     665         170 :                 if (r < 0)
     666           0 :                         return r;
     667             : 
     668         170 :                 q = le64toh(o->data.next_hash_offset);
     669             :         }
     670             : 
     671           0 :         return 0;
     672             : }
     673             : 
     674        6000 : static int verify_entry(
     675             :                 JournalFile *f,
     676             :                 Object *o, uint64_t p,
     677             :                 MMapFileDescriptor *cache_data_fd, uint64_t n_data) {
     678             : 
     679             :         uint64_t i, n;
     680             :         int r;
     681             : 
     682        6000 :         assert(f);
     683        6000 :         assert(o);
     684        6000 :         assert(cache_data_fd);
     685             : 
     686        6000 :         n = journal_file_entry_n_items(o);
     687       12000 :         for (i = 0; i < n; i++) {
     688             :                 uint64_t q, h;
     689             :                 Object *u;
     690             : 
     691        6000 :                 q = le64toh(o->entry.items[i].object_offset);
     692        6000 :                 h = le64toh(o->entry.items[i].hash);
     693             : 
     694        6000 :                 if (!contains_uint64(f->mmap, cache_data_fd, n_data, q)) {
     695           0 :                         error(p, "Invalid data object of entry");
     696           0 :                         return -EBADMSG;
     697             :                 }
     698             : 
     699        6000 :                 r = journal_file_move_to_object(f, OBJECT_DATA, q, &u);
     700        6000 :                 if (r < 0)
     701           0 :                         return r;
     702             : 
     703        6000 :                 if (le64toh(u->data.hash) != h) {
     704           0 :                         error(p, "Hash mismatch for data object of entry");
     705           0 :                         return -EBADMSG;
     706             :                 }
     707             : 
     708        6000 :                 r = data_object_in_hash_table(f, h, q);
     709        6000 :                 if (r < 0)
     710           0 :                         return r;
     711        6000 :                 if (r == 0) {
     712           0 :                         error(p, "Data object missing from hash table");
     713           0 :                         return -EBADMSG;
     714             :                 }
     715             :         }
     716             : 
     717        6000 :         return 0;
     718             : }
     719             : 
     720           1 : static int verify_entry_array(
     721             :                 JournalFile *f,
     722             :                 MMapFileDescriptor *cache_data_fd, uint64_t n_data,
     723             :                 MMapFileDescriptor *cache_entry_fd, uint64_t n_entries,
     724             :                 MMapFileDescriptor *cache_entry_array_fd, uint64_t n_entry_arrays,
     725             :                 usec_t *last_usec,
     726             :                 bool show_progress) {
     727             : 
     728           1 :         uint64_t i = 0, a, n, last = 0;
     729             :         int r;
     730             : 
     731           1 :         assert(f);
     732           1 :         assert(cache_data_fd);
     733           1 :         assert(cache_entry_fd);
     734           1 :         assert(cache_entry_array_fd);
     735           1 :         assert(last_usec);
     736             : 
     737           1 :         n = le64toh(f->header->n_entries);
     738           1 :         a = le64toh(f->header->entry_array_offset);
     739           9 :         while (i < n) {
     740             :                 uint64_t next, m, j;
     741             :                 Object *o;
     742             : 
     743           8 :                 if (show_progress)
     744           8 :                         draw_progress(0x8000 + scale_progress(0x3FFF, i, n), last_usec);
     745             : 
     746           8 :                 if (a == 0) {
     747           0 :                         error(a, "Array chain too short at %"PRIu64" of %"PRIu64, i, n);
     748           0 :                         return -EBADMSG;
     749             :                 }
     750             : 
     751           8 :                 if (!contains_uint64(f->mmap, cache_entry_array_fd, n_entry_arrays, a)) {
     752           0 :                         error(a, "Invalid array %"PRIu64" of %"PRIu64, i, n);
     753           0 :                         return -EBADMSG;
     754             :                 }
     755             : 
     756           8 :                 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
     757           8 :                 if (r < 0)
     758           0 :                         return r;
     759             : 
     760           8 :                 next = le64toh(o->entry_array.next_entry_array_offset);
     761           8 :                 if (next != 0 && next <= a) {
     762           0 :                         error(a, "Array chain has cycle at %"PRIu64" of %"PRIu64" (jumps back from to "OFSfmt")", i, n, next);
     763           0 :                         return -EBADMSG;
     764             :                 }
     765             : 
     766           8 :                 m = journal_file_entry_array_n_items(o);
     767        6008 :                 for (j = 0; i < n && j < m; i++, j++) {
     768             :                         uint64_t p;
     769             : 
     770        6000 :                         p = le64toh(o->entry_array.items[j]);
     771        6000 :                         if (p <= last) {
     772           0 :                                 error(a, "Entry array not sorted at %"PRIu64" of %"PRIu64, i, n);
     773           0 :                                 return -EBADMSG;
     774             :                         }
     775        6000 :                         last = p;
     776             : 
     777        6000 :                         if (!contains_uint64(f->mmap, cache_entry_fd, n_entries, p)) {
     778           0 :                                 error(a, "Invalid array entry at %"PRIu64" of %"PRIu64, i, n);
     779           0 :                                 return -EBADMSG;
     780             :                         }
     781             : 
     782        6000 :                         r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
     783        6000 :                         if (r < 0)
     784           0 :                                 return r;
     785             : 
     786        6000 :                         r = verify_entry(f, o, p, cache_data_fd, n_data);
     787        6000 :                         if (r < 0)
     788           0 :                                 return r;
     789             : 
     790             :                         /* Pointer might have moved, reposition */
     791        6000 :                         r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
     792        6000 :                         if (r < 0)
     793           0 :                                 return r;
     794             :                 }
     795             : 
     796           8 :                 a = next;
     797             :         }
     798             : 
     799           1 :         return 0;
     800             : }
     801             : 
     802           1 : int journal_file_verify(
     803             :                 JournalFile *f,
     804             :                 const char *key,
     805             :                 usec_t *first_contained, usec_t *last_validated, usec_t *last_contained,
     806             :                 bool show_progress) {
     807             :         int r;
     808             :         Object *o;
     809           1 :         uint64_t p = 0, last_epoch = 0, last_tag_realtime = 0, last_sealed_realtime = 0;
     810             : 
     811           1 :         uint64_t entry_seqnum = 0, entry_monotonic = 0, entry_realtime = 0;
     812             :         sd_id128_t entry_boot_id;
     813           1 :         bool entry_seqnum_set = false, entry_monotonic_set = false, entry_realtime_set = false, found_main_entry_array = false;
     814           1 :         uint64_t n_weird = 0, n_objects = 0, n_entries = 0, n_data = 0, n_fields = 0, n_data_hash_tables = 0, n_field_hash_tables = 0, n_entry_arrays = 0, n_tags = 0;
     815           1 :         usec_t last_usec = 0;
     816           1 :         int data_fd = -1, entry_fd = -1, entry_array_fd = -1;
     817           1 :         MMapFileDescriptor *cache_data_fd = NULL, *cache_entry_fd = NULL, *cache_entry_array_fd = NULL;
     818             :         unsigned i;
     819           1 :         bool found_last = false;
     820           1 :         const char *tmp_dir = NULL;
     821             : 
     822             : #if HAVE_GCRYPT
     823           1 :         uint64_t last_tag = 0;
     824             : #endif
     825           1 :         assert(f);
     826             : 
     827           1 :         if (key) {
     828             : #if HAVE_GCRYPT
     829           0 :                 r = journal_file_parse_verification_key(f, key);
     830           0 :                 if (r < 0) {
     831           0 :                         log_error("Failed to parse seed.");
     832           0 :                         return r;
     833             :                 }
     834             : #else
     835             :                 return -EOPNOTSUPP;
     836             : #endif
     837           1 :         } else if (f->seal)
     838           0 :                 return -ENOKEY;
     839             : 
     840           1 :         r = var_tmp_dir(&tmp_dir);
     841           1 :         if (r < 0) {
     842           0 :                 log_error_errno(r, "Failed to determine temporary directory: %m");
     843           0 :                 goto fail;
     844             :         }
     845             : 
     846           1 :         data_fd = open_tmpfile_unlinkable(tmp_dir, O_RDWR | O_CLOEXEC);
     847           1 :         if (data_fd < 0) {
     848           0 :                 r = log_error_errno(data_fd, "Failed to create data file: %m");
     849           0 :                 goto fail;
     850             :         }
     851             : 
     852           1 :         entry_fd = open_tmpfile_unlinkable(tmp_dir, O_RDWR | O_CLOEXEC);
     853           1 :         if (entry_fd < 0) {
     854           0 :                 r = log_error_errno(entry_fd, "Failed to create entry file: %m");
     855           0 :                 goto fail;
     856             :         }
     857             : 
     858           1 :         entry_array_fd = open_tmpfile_unlinkable(tmp_dir, O_RDWR | O_CLOEXEC);
     859           1 :         if (entry_array_fd < 0) {
     860           0 :                 r = log_error_errno(entry_array_fd,
     861             :                                     "Failed to create entry array file: %m");
     862           0 :                 goto fail;
     863             :         }
     864             : 
     865           1 :         cache_data_fd = mmap_cache_add_fd(f->mmap, data_fd);
     866           1 :         if (!cache_data_fd) {
     867           0 :                 r = log_oom();
     868           0 :                 goto fail;
     869             :         }
     870             : 
     871           1 :         cache_entry_fd = mmap_cache_add_fd(f->mmap, entry_fd);
     872           1 :         if (!cache_entry_fd) {
     873           0 :                 r = log_oom();
     874           0 :                 goto fail;
     875             :         }
     876             : 
     877           1 :         cache_entry_array_fd = mmap_cache_add_fd(f->mmap, entry_array_fd);
     878           1 :         if (!cache_entry_array_fd) {
     879           0 :                 r = log_oom();
     880           0 :                 goto fail;
     881             :         }
     882             : 
     883           1 :         if (le32toh(f->header->compatible_flags) & ~HEADER_COMPATIBLE_SUPPORTED) {
     884           0 :                 log_error("Cannot verify file with unknown extensions.");
     885           0 :                 r = -EOPNOTSUPP;
     886           0 :                 goto fail;
     887             :         }
     888             : 
     889           8 :         for (i = 0; i < sizeof(f->header->reserved); i++)
     890           7 :                 if (f->header->reserved[i] != 0) {
     891           0 :                         error(offsetof(Header, reserved[i]), "Reserved field is non-zero");
     892           0 :                         r = -EBADMSG;
     893           0 :                         goto fail;
     894             :                 }
     895             : 
     896             :         /* First iteration: we go through all objects, verify the
     897             :          * superficial structure, headers, hashes. */
     898             : 
     899           1 :         p = le64toh(f->header->header_size);
     900             :         for (;;) {
     901             :                 /* Early exit if there are no objects in the file, at all */
     902        6396 :                 if (le64toh(f->header->tail_object_offset) == 0)
     903           0 :                         break;
     904             : 
     905        6396 :                 if (show_progress)
     906        6396 :                         draw_progress(scale_progress(0x7FFF, p, le64toh(f->header->tail_object_offset)), &last_usec);
     907             : 
     908        6396 :                 r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &o);
     909        6396 :                 if (r < 0) {
     910           0 :                         error(p, "Invalid object");
     911           0 :                         goto fail;
     912             :                 }
     913             : 
     914        6396 :                 if (p > le64toh(f->header->tail_object_offset)) {
     915           0 :                         error(offsetof(Header, tail_object_offset), "Invalid tail object pointer");
     916           0 :                         r = -EBADMSG;
     917           0 :                         goto fail;
     918             :                 }
     919             : 
     920        6396 :                 n_objects++;
     921             : 
     922        6396 :                 r = journal_file_object_verify(f, p, o);
     923        6396 :                 if (r < 0) {
     924           0 :                         error_errno(p, r, "Invalid object contents: %m");
     925           0 :                         goto fail;
     926             :                 }
     927             : 
     928        6396 :                 if ((o->object.flags & OBJECT_COMPRESSED_XZ) &&
     929           0 :                     (o->object.flags & OBJECT_COMPRESSED_LZ4)) {
     930           0 :                         error(p, "Objected with double compression");
     931           0 :                         r = -EINVAL;
     932           0 :                         goto fail;
     933             :                 }
     934             : 
     935        6396 :                 if ((o->object.flags & OBJECT_COMPRESSED_XZ) && !JOURNAL_HEADER_COMPRESSED_XZ(f->header)) {
     936           0 :                         error(p, "XZ compressed object in file without XZ compression");
     937           0 :                         r = -EBADMSG;
     938           0 :                         goto fail;
     939             :                 }
     940             : 
     941        6396 :                 if ((o->object.flags & OBJECT_COMPRESSED_LZ4) && !JOURNAL_HEADER_COMPRESSED_LZ4(f->header)) {
     942           0 :                         error(p, "LZ4 compressed object in file without LZ4 compression");
     943           0 :                         r = -EBADMSG;
     944           0 :                         goto fail;
     945             :                 }
     946             : 
     947        6396 :                 switch (o->object.type) {
     948             : 
     949          77 :                 case OBJECT_DATA:
     950          77 :                         r = write_uint64(data_fd, p);
     951          77 :                         if (r < 0)
     952           0 :                                 goto fail;
     953             : 
     954          77 :                         n_data++;
     955          77 :                         break;
     956             : 
     957           1 :                 case OBJECT_FIELD:
     958           1 :                         n_fields++;
     959           1 :                         break;
     960             : 
     961        6000 :                 case OBJECT_ENTRY:
     962        6000 :                         if (JOURNAL_HEADER_SEALED(f->header) && n_tags <= 0) {
     963           0 :                                 error(p, "First entry before first tag");
     964           0 :                                 r = -EBADMSG;
     965           0 :                                 goto fail;
     966             :                         }
     967             : 
     968        6000 :                         r = write_uint64(entry_fd, p);
     969        6000 :                         if (r < 0)
     970           0 :                                 goto fail;
     971             : 
     972        6000 :                         if (le64toh(o->entry.realtime) < last_tag_realtime) {
     973           0 :                                 error(p, "Older entry after newer tag");
     974           0 :                                 r = -EBADMSG;
     975           0 :                                 goto fail;
     976             :                         }
     977             : 
     978        6001 :                         if (!entry_seqnum_set &&
     979           1 :                             le64toh(o->entry.seqnum) != le64toh(f->header->head_entry_seqnum)) {
     980           0 :                                 error(p, "Head entry sequence number incorrect");
     981           0 :                                 r = -EBADMSG;
     982           0 :                                 goto fail;
     983             :                         }
     984             : 
     985       11999 :                         if (entry_seqnum_set &&
     986        5999 :                             entry_seqnum >= le64toh(o->entry.seqnum)) {
     987           0 :                                 error(p, "Entry sequence number out of synchronization");
     988           0 :                                 r = -EBADMSG;
     989           0 :                                 goto fail;
     990             :                         }
     991             : 
     992        6000 :                         entry_seqnum = le64toh(o->entry.seqnum);
     993        6000 :                         entry_seqnum_set = true;
     994             : 
     995        6000 :                         if (entry_monotonic_set &&
     996       11998 :                             sd_id128_equal(entry_boot_id, o->entry.boot_id) &&
     997        5999 :                             entry_monotonic > le64toh(o->entry.monotonic)) {
     998           0 :                                 error(p, "Entry timestamp out of synchronization");
     999           0 :                                 r = -EBADMSG;
    1000           0 :                                 goto fail;
    1001             :                         }
    1002             : 
    1003        6000 :                         entry_monotonic = le64toh(o->entry.monotonic);
    1004        6000 :                         entry_boot_id = o->entry.boot_id;
    1005        6000 :                         entry_monotonic_set = true;
    1006             : 
    1007        6001 :                         if (!entry_realtime_set &&
    1008           1 :                             le64toh(o->entry.realtime) != le64toh(f->header->head_entry_realtime)) {
    1009           0 :                                 error(p, "Head entry realtime timestamp incorrect");
    1010           0 :                                 r = -EBADMSG;
    1011           0 :                                 goto fail;
    1012             :                         }
    1013             : 
    1014        6000 :                         entry_realtime = le64toh(o->entry.realtime);
    1015        6000 :                         entry_realtime_set = true;
    1016             : 
    1017        6000 :                         n_entries++;
    1018        6000 :                         break;
    1019             : 
    1020           1 :                 case OBJECT_DATA_HASH_TABLE:
    1021           1 :                         if (n_data_hash_tables > 1) {
    1022           0 :                                 error(p, "More than one data hash table");
    1023           0 :                                 r = -EBADMSG;
    1024           0 :                                 goto fail;
    1025             :                         }
    1026             : 
    1027           1 :                         if (le64toh(f->header->data_hash_table_offset) != p + offsetof(HashTableObject, items) ||
    1028           1 :                             le64toh(f->header->data_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
    1029           0 :                                 error(p, "header fields for data hash table invalid");
    1030           0 :                                 r = -EBADMSG;
    1031           0 :                                 goto fail;
    1032             :                         }
    1033             : 
    1034           1 :                         n_data_hash_tables++;
    1035           1 :                         break;
    1036             : 
    1037           1 :                 case OBJECT_FIELD_HASH_TABLE:
    1038           1 :                         if (n_field_hash_tables > 1) {
    1039           0 :                                 error(p, "More than one field hash table");
    1040           0 :                                 r = -EBADMSG;
    1041           0 :                                 goto fail;
    1042             :                         }
    1043             : 
    1044           1 :                         if (le64toh(f->header->field_hash_table_offset) != p + offsetof(HashTableObject, items) ||
    1045           1 :                             le64toh(f->header->field_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
    1046           0 :                                 error(p, "Header fields for field hash table invalid");
    1047           0 :                                 r = -EBADMSG;
    1048           0 :                                 goto fail;
    1049             :                         }
    1050             : 
    1051           1 :                         n_field_hash_tables++;
    1052           1 :                         break;
    1053             : 
    1054         316 :                 case OBJECT_ENTRY_ARRAY:
    1055         316 :                         r = write_uint64(entry_array_fd, p);
    1056         316 :                         if (r < 0)
    1057           0 :                                 goto fail;
    1058             : 
    1059         316 :                         if (p == le64toh(f->header->entry_array_offset)) {
    1060           1 :                                 if (found_main_entry_array) {
    1061           0 :                                         error(p, "More than one main entry array");
    1062           0 :                                         r = -EBADMSG;
    1063           0 :                                         goto fail;
    1064             :                                 }
    1065             : 
    1066           1 :                                 found_main_entry_array = true;
    1067             :                         }
    1068             : 
    1069         316 :                         n_entry_arrays++;
    1070         316 :                         break;
    1071             : 
    1072           0 :                 case OBJECT_TAG:
    1073           0 :                         if (!JOURNAL_HEADER_SEALED(f->header)) {
    1074           0 :                                 error(p, "Tag object in file without sealing");
    1075           0 :                                 r = -EBADMSG;
    1076           0 :                                 goto fail;
    1077             :                         }
    1078             : 
    1079           0 :                         if (le64toh(o->tag.seqnum) != n_tags + 1) {
    1080           0 :                                 error(p, "Tag sequence number out of synchronization");
    1081           0 :                                 r = -EBADMSG;
    1082           0 :                                 goto fail;
    1083             :                         }
    1084             : 
    1085           0 :                         if (le64toh(o->tag.epoch) < last_epoch) {
    1086           0 :                                 error(p, "Epoch sequence out of synchronization");
    1087           0 :                                 r = -EBADMSG;
    1088           0 :                                 goto fail;
    1089             :                         }
    1090             : 
    1091             : #if HAVE_GCRYPT
    1092           0 :                         if (f->seal) {
    1093             :                                 uint64_t q, rt;
    1094             : 
    1095           0 :                                 debug(p, "Checking tag %"PRIu64"...", le64toh(o->tag.seqnum));
    1096             : 
    1097           0 :                                 rt = f->fss_start_usec + le64toh(o->tag.epoch) * f->fss_interval_usec;
    1098           0 :                                 if (entry_realtime_set && entry_realtime >= rt + f->fss_interval_usec) {
    1099           0 :                                         error(p, "tag/entry realtime timestamp out of synchronization");
    1100           0 :                                         r = -EBADMSG;
    1101           0 :                                         goto fail;
    1102             :                                 }
    1103             : 
    1104             :                                 /* OK, now we know the epoch. So let's now set
    1105             :                                  * it, and calculate the HMAC for everything
    1106             :                                  * since the last tag. */
    1107           0 :                                 r = journal_file_fsprg_seek(f, le64toh(o->tag.epoch));
    1108           0 :                                 if (r < 0)
    1109           0 :                                         goto fail;
    1110             : 
    1111           0 :                                 r = journal_file_hmac_start(f);
    1112           0 :                                 if (r < 0)
    1113           0 :                                         goto fail;
    1114             : 
    1115           0 :                                 if (last_tag == 0) {
    1116           0 :                                         r = journal_file_hmac_put_header(f);
    1117           0 :                                         if (r < 0)
    1118           0 :                                                 goto fail;
    1119             : 
    1120           0 :                                         q = le64toh(f->header->header_size);
    1121             :                                 } else
    1122           0 :                                         q = last_tag;
    1123             : 
    1124           0 :                                 while (q <= p) {
    1125           0 :                                         r = journal_file_move_to_object(f, OBJECT_UNUSED, q, &o);
    1126           0 :                                         if (r < 0)
    1127           0 :                                                 goto fail;
    1128             : 
    1129           0 :                                         r = journal_file_hmac_put_object(f, OBJECT_UNUSED, o, q);
    1130           0 :                                         if (r < 0)
    1131           0 :                                                 goto fail;
    1132             : 
    1133           0 :                                         q = q + ALIGN64(le64toh(o->object.size));
    1134             :                                 }
    1135             : 
    1136             :                                 /* Position might have changed, let's reposition things */
    1137           0 :                                 r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &o);
    1138           0 :                                 if (r < 0)
    1139           0 :                                         goto fail;
    1140             : 
    1141           0 :                                 if (memcmp(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH) != 0) {
    1142           0 :                                         error(p, "Tag failed verification");
    1143           0 :                                         r = -EBADMSG;
    1144           0 :                                         goto fail;
    1145             :                                 }
    1146             : 
    1147           0 :                                 f->hmac_running = false;
    1148           0 :                                 last_tag_realtime = rt;
    1149           0 :                                 last_sealed_realtime = entry_realtime;
    1150             :                         }
    1151             : 
    1152           0 :                         last_tag = p + ALIGN64(le64toh(o->object.size));
    1153             : #endif
    1154             : 
    1155           0 :                         last_epoch = le64toh(o->tag.epoch);
    1156             : 
    1157           0 :                         n_tags++;
    1158           0 :                         break;
    1159             : 
    1160           0 :                 default:
    1161           0 :                         n_weird++;
    1162             :                 }
    1163             : 
    1164        6396 :                 if (p == le64toh(f->header->tail_object_offset)) {
    1165           1 :                         found_last = true;
    1166           1 :                         break;
    1167             :                 }
    1168             : 
    1169        6395 :                 p = p + ALIGN64(le64toh(o->object.size));
    1170             :         };
    1171             : 
    1172           1 :         if (!found_last && le64toh(f->header->tail_object_offset) != 0) {
    1173           0 :                 error(le64toh(f->header->tail_object_offset), "Tail object pointer dead");
    1174           0 :                 r = -EBADMSG;
    1175           0 :                 goto fail;
    1176             :         }
    1177             : 
    1178           1 :         if (n_objects != le64toh(f->header->n_objects)) {
    1179           0 :                 error(offsetof(Header, n_objects), "Object number mismatch");
    1180           0 :                 r = -EBADMSG;
    1181           0 :                 goto fail;
    1182             :         }
    1183             : 
    1184           1 :         if (n_entries != le64toh(f->header->n_entries)) {
    1185           0 :                 error(offsetof(Header, n_entries), "Entry number mismatch");
    1186           0 :                 r = -EBADMSG;
    1187           0 :                 goto fail;
    1188             :         }
    1189             : 
    1190           2 :         if (JOURNAL_HEADER_CONTAINS(f->header, n_data) &&
    1191           1 :             n_data != le64toh(f->header->n_data)) {
    1192           0 :                 error(offsetof(Header, n_data), "Data number mismatch");
    1193           0 :                 r = -EBADMSG;
    1194           0 :                 goto fail;
    1195             :         }
    1196             : 
    1197           2 :         if (JOURNAL_HEADER_CONTAINS(f->header, n_fields) &&
    1198           1 :             n_fields != le64toh(f->header->n_fields)) {
    1199           0 :                 error(offsetof(Header, n_fields), "Field number mismatch");
    1200           0 :                 r = -EBADMSG;
    1201           0 :                 goto fail;
    1202             :         }
    1203             : 
    1204           2 :         if (JOURNAL_HEADER_CONTAINS(f->header, n_tags) &&
    1205           1 :             n_tags != le64toh(f->header->n_tags)) {
    1206           0 :                 error(offsetof(Header, n_tags), "Tag number mismatch");
    1207           0 :                 r = -EBADMSG;
    1208           0 :                 goto fail;
    1209             :         }
    1210             : 
    1211           2 :         if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays) &&
    1212           1 :             n_entry_arrays != le64toh(f->header->n_entry_arrays)) {
    1213           0 :                 error(offsetof(Header, n_entry_arrays), "Entry array number mismatch");
    1214           0 :                 r = -EBADMSG;
    1215           0 :                 goto fail;
    1216             :         }
    1217             : 
    1218           1 :         if (!found_main_entry_array && le64toh(f->header->entry_array_offset) != 0) {
    1219           0 :                 error(0, "Missing entry array");
    1220           0 :                 r = -EBADMSG;
    1221           0 :                 goto fail;
    1222             :         }
    1223             : 
    1224           2 :         if (entry_seqnum_set &&
    1225           1 :             entry_seqnum != le64toh(f->header->tail_entry_seqnum)) {
    1226           0 :                 error(offsetof(Header, tail_entry_seqnum), "Invalid tail seqnum");
    1227           0 :                 r = -EBADMSG;
    1228           0 :                 goto fail;
    1229             :         }
    1230             : 
    1231           1 :         if (entry_monotonic_set &&
    1232           2 :             (sd_id128_equal(entry_boot_id, f->header->boot_id) &&
    1233           1 :              entry_monotonic != le64toh(f->header->tail_entry_monotonic))) {
    1234           0 :                 error(0, "Invalid tail monotonic timestamp");
    1235           0 :                 r = -EBADMSG;
    1236           0 :                 goto fail;
    1237             :         }
    1238             : 
    1239           1 :         if (entry_realtime_set && entry_realtime != le64toh(f->header->tail_entry_realtime)) {
    1240           0 :                 error(0, "Invalid tail realtime timestamp");
    1241           0 :                 r = -EBADMSG;
    1242           0 :                 goto fail;
    1243             :         }
    1244             : 
    1245             :         /* Second iteration: we follow all objects referenced from the
    1246             :          * two entry points: the object hash table and the entry
    1247             :          * array. We also check that everything referenced (directly
    1248             :          * or indirectly) in the data hash table also exists in the
    1249             :          * entry array, and vice versa. Note that we do not care for
    1250             :          * unreferenced objects. We only care that everything that is
    1251             :          * referenced is consistent. */
    1252             : 
    1253           1 :         r = verify_entry_array(f,
    1254             :                                cache_data_fd, n_data,
    1255             :                                cache_entry_fd, n_entries,
    1256             :                                cache_entry_array_fd, n_entry_arrays,
    1257             :                                &last_usec,
    1258             :                                show_progress);
    1259           1 :         if (r < 0)
    1260           0 :                 goto fail;
    1261             : 
    1262           1 :         r = verify_hash_table(f,
    1263             :                               cache_data_fd, n_data,
    1264             :                               cache_entry_fd, n_entries,
    1265             :                               cache_entry_array_fd, n_entry_arrays,
    1266             :                               &last_usec,
    1267             :                               show_progress);
    1268           1 :         if (r < 0)
    1269           0 :                 goto fail;
    1270             : 
    1271           1 :         if (show_progress)
    1272           1 :                 flush_progress();
    1273             : 
    1274           1 :         mmap_cache_free_fd(f->mmap, cache_data_fd);
    1275           1 :         mmap_cache_free_fd(f->mmap, cache_entry_fd);
    1276           1 :         mmap_cache_free_fd(f->mmap, cache_entry_array_fd);
    1277             : 
    1278           1 :         safe_close(data_fd);
    1279           1 :         safe_close(entry_fd);
    1280           1 :         safe_close(entry_array_fd);
    1281             : 
    1282           1 :         if (first_contained)
    1283           1 :                 *first_contained = le64toh(f->header->head_entry_realtime);
    1284           1 :         if (last_validated)
    1285           1 :                 *last_validated = last_sealed_realtime;
    1286           1 :         if (last_contained)
    1287           1 :                 *last_contained = le64toh(f->header->tail_entry_realtime);
    1288             : 
    1289           1 :         return 0;
    1290             : 
    1291           0 : fail:
    1292           0 :         if (show_progress)
    1293           0 :                 flush_progress();
    1294             : 
    1295           0 :         log_error("File corruption detected at %s:"OFSfmt" (of %llu bytes, %"PRIu64"%%).",
    1296             :                   f->path,
    1297             :                   p,
    1298             :                   (unsigned long long) f->last_stat.st_size,
    1299             :                   100 * p / f->last_stat.st_size);
    1300             : 
    1301           0 :         if (data_fd >= 0)
    1302           0 :                 safe_close(data_fd);
    1303             : 
    1304           0 :         if (entry_fd >= 0)
    1305           0 :                 safe_close(entry_fd);
    1306             : 
    1307           0 :         if (entry_array_fd >= 0)
    1308           0 :                 safe_close(entry_array_fd);
    1309             : 
    1310           0 :         if (cache_data_fd)
    1311           0 :                 mmap_cache_free_fd(f->mmap, cache_data_fd);
    1312             : 
    1313           0 :         if (cache_entry_fd)
    1314           0 :                 mmap_cache_free_fd(f->mmap, cache_entry_fd);
    1315             : 
    1316           0 :         if (cache_entry_array_fd)
    1317           0 :                 mmap_cache_free_fd(f->mmap, cache_entry_array_fd);
    1318             : 
    1319           0 :         return r;
    1320             : }

Generated by: LCOV version 1.14