LCOV - code coverage report
Current view: top level - journal - journal-verify.c (source / functions) Hit Total Coverage
Test: systemd test coverage Lines: 358 682 52.5 %
Date: 2015-07-29 18:47:03 Functions: 13 13 100.0 %

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

Generated by: LCOV version 1.11