Branch data 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 : 33804 : static void draw_progress(uint64_t p, usec_t *last_usec) {
24 : : unsigned n, i, j, k;
25 : : usec_t z, x;
26 : :
27 [ + - ]: 33804 : if (!on_tty())
28 : 33804 : 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 : 33804 : 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 [ + + - + ]: 33804 : if (p >= m || m == 0) // lgtm[cpp/constant-comparison]
69 : 4 : return scale;
70 : :
71 : 33800 : return scale * p / m;
72 : : }
73 : :
74 : 4 : static void flush_progress(void) {
75 : : unsigned n, i;
76 : :
77 [ + - ]: 4 : if (!on_tty())
78 : 4 : 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 : 25584 : static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o) {
112 : : uint64_t i;
113 : :
114 [ - + ]: 25584 : assert(f);
115 [ - + ]: 25584 : assert(offset);
116 [ - + ]: 25584 : 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 [ - + ]: 25584 : 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 [ + + + + : 25584 : switch (o->object.type) {
+ - - ]
129 : :
130 : 308 : case OBJECT_DATA: {
131 : : uint64_t h1, h2;
132 : : int compression, r;
133 : :
134 [ - + ]: 308 : if (le64toh(o->data.entry_offset) == 0)
135 [ # # ]: 0 : warning(offset, "Unused data (entry_offset==0)");
136 : :
137 [ - + ]: 308 : 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 [ - + ]: 308 : 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 : 308 : h1 = le64toh(o->data.hash);
150 : :
151 : 308 : compression = o->object.flags & OBJECT_COMPRESSION_MASK;
152 [ - + ]: 308 : 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 : 308 : h2 = hash64(o->data.payload, le64toh(o->object.size) - offsetof(Object, data.payload));
169 : :
170 [ - + ]: 308 : if (h1 != h2) {
171 [ # # ]: 0 : error(offset, "Invalid hash (%08"PRIx64" vs. %08"PRIx64, h1, h2);
172 : 0 : return -EBADMSG;
173 : : }
174 : :
175 [ + - ]: 308 : if (!VALID64(le64toh(o->data.next_hash_offset)) ||
176 [ + - ]: 308 : !VALID64(le64toh(o->data.next_field_offset)) ||
177 [ + - ]: 308 : !VALID64(le64toh(o->data.entry_offset)) ||
178 [ - + ]: 308 : !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 : 308 : break;
188 : : }
189 : :
190 : 4 : case OBJECT_FIELD:
191 [ - + ]: 4 : 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 [ + - ]: 4 : if (!VALID64(le64toh(o->field.next_hash_offset)) ||
200 [ - + ]: 4 : !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 : 4 : break;
208 : :
209 : 24000 : case OBJECT_ENTRY:
210 [ - + ]: 24000 : 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 [ - + ]: 24000 : 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 [ - + ]: 24000 : 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 [ - + ]: 24000 : 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 [ - + ]: 24000 : 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 [ + + ]: 48000 : for (i = 0; i < journal_file_entry_n_items(o); i++) {
247 [ + - ]: 24000 : if (le64toh(o->entry.items[i].object_offset) == 0 ||
248 [ - + ]: 24000 : !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 : 24000 : break;
258 : :
259 : 8 : case OBJECT_DATA_HASH_TABLE:
260 : : case OBJECT_FIELD_HASH_TABLE:
261 [ + - ]: 8 : if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) % sizeof(HashItem) != 0 ||
262 [ - + ]: 8 : (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 [ + + ]: 9528 : for (i = 0; i < journal_file_hash_table_n_items(o); i++) {
271 [ + + ]: 9520 : if (o->hash_table.items[i].head_hash_offset != 0 &&
272 [ - + ]: 304 : !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 [ + + ]: 9520 : if (o->hash_table.items[i].tail_hash_offset != 0 &&
281 [ - + ]: 304 : !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 : 19040 : if ((o->hash_table.items[i].head_hash_offset != 0) !=
291 [ - + ]: 9520 : (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 : 8 : break;
303 : :
304 : 1264 : case OBJECT_ENTRY_ARRAY:
305 [ + - ]: 1264 : if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) % sizeof(le64_t) != 0 ||
306 [ - + ]: 1264 : (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 [ - + ]: 1264 : 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 [ + + ]: 74896 : for (i = 0; i < journal_file_entry_array_n_items(o); i++)
321 [ + + ]: 73632 : if (le64toh(o->entry_array.items[i]) != 0 &&
322 [ - + ]: 47692 : !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 : 1264 : 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 : 25584 : return 0;
351 : : }
352 : :
353 : 25572 : static int write_uint64(int fd, uint64_t p) {
354 : : ssize_t k;
355 : :
356 : 25572 : k = write(fd, &p, sizeof(p));
357 [ - + ]: 25572 : if (k < 0)
358 : 0 : return -errno;
359 [ - + ]: 25572 : if (k != sizeof(p))
360 : 0 : return -EIO;
361 : :
362 : 25572 : return 0;
363 : : }
364 : :
365 : 73572 : static int contains_uint64(MMapCache *m, MMapFileDescriptor *f, uint64_t n, uint64_t p) {
366 : : uint64_t a, b;
367 : : int r;
368 : :
369 [ - + ]: 73572 : assert(m);
370 [ - + ]: 73572 : assert(f);
371 : :
372 : : /* Bisection ... */
373 : :
374 : 73572 : a = 0; b = n;
375 [ + - ]: 700316 : while (a < b) {
376 : : uint64_t c, *z;
377 : :
378 : 700316 : c = (a + b) / 2;
379 : :
380 : 700316 : r = mmap_cache_get(m, f, PROT_READ|PROT_WRITE, 0, false, c * sizeof(uint64_t), sizeof(uint64_t), NULL, (void **) &z, NULL);
381 [ - + ]: 700316 : if (r < 0)
382 : 73572 : return r;
383 : :
384 [ + + ]: 700316 : if (*z == p)
385 : 73572 : return 1;
386 : :
387 [ - + ]: 626744 : if (a + 1 >= b)
388 : 0 : return 0;
389 : :
390 [ + + ]: 626744 : if (p < *z)
391 : 298476 : b = c;
392 : : else
393 : 328268 : a = c;
394 : : }
395 : :
396 : 0 : return 0;
397 : : }
398 : :
399 : 24000 : 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 : 24000 : bool found = false;
410 : :
411 [ - + ]: 24000 : assert(f);
412 [ - + ]: 24000 : assert(cache_entry_fd);
413 : :
414 [ - + ]: 24000 : 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 : 24000 : r = journal_file_move_to_object(f, OBJECT_ENTRY, entry_p, &o);
420 [ - + ]: 24000 : if (r < 0)
421 : 0 : return r;
422 : :
423 : 24000 : n = journal_file_entry_n_items(o);
424 [ + - ]: 24000 : for (i = 0; i < n; i++)
425 [ + - ]: 24000 : if (le64toh(o->entry.items[i].object_offset) == data_p) {
426 : 24000 : found = true;
427 : 24000 : break;
428 : : }
429 : :
430 [ - + ]: 24000 : 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 : 24000 : i = 0;
440 : 24000 : n = le64toh(f->header->n_entries);
441 : 24000 : a = le64toh(f->header->entry_array_offset);
442 : :
443 [ + - ]: 173080 : while (i < n) {
444 : : uint64_t m, u;
445 : :
446 : 173080 : r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
447 [ - + ]: 173080 : if (r < 0)
448 : 0 : return r;
449 : :
450 : 173080 : m = journal_file_entry_array_n_items(o);
451 : 173080 : u = MIN(n - i, m);
452 : :
453 [ + + ]: 173080 : if (entry_p <= le64toh(o->entry_array.items[u-1])) {
454 : : uint64_t x, y, z;
455 : :
456 : 24000 : x = 0;
457 : 24000 : y = u;
458 : :
459 [ + - ]: 237604 : while (x < y) {
460 : 237604 : z = (x + y) / 2;
461 : :
462 [ + + ]: 237604 : if (le64toh(o->entry_array.items[z]) == entry_p)
463 : 24000 : return 0;
464 : :
465 [ - + ]: 213604 : if (x + 1 >= y)
466 : 0 : break;
467 : :
468 [ + + ]: 213604 : if (entry_p < le64toh(o->entry_array.items[z]))
469 : 102716 : y = z;
470 : : else
471 : 110888 : 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 : 149080 : i += u;
479 : 149080 : a = le64toh(o->entry_array.next_entry_array_offset);
480 : : }
481 : :
482 : 0 : return 0;
483 : : }
484 : :
485 : 308 : 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 [ - + ]: 308 : assert(f);
495 [ - + ]: 308 : assert(o);
496 [ - + ]: 308 : assert(cache_entry_fd);
497 [ - + ]: 308 : assert(cache_entry_array_fd);
498 : :
499 : 308 : n = le64toh(o->data.n_entries);
500 : 308 : a = le64toh(o->data.entry_array_offset);
501 : :
502 : : /* Entry array means at least two objects */
503 [ + - - + ]: 308 : 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 [ - + ]: 308 : if (n == 0)
509 : 0 : return 0;
510 : :
511 : : /* We already checked that earlier */
512 [ - + ]: 308 : assert(o->data.entry_offset);
513 : :
514 : 308 : last = q = le64toh(o->data.entry_offset);
515 : 308 : r = entry_points_to_data(f, cache_entry_fd, n_entries, q, p);
516 [ - + ]: 308 : if (r < 0)
517 : 0 : return r;
518 : :
519 : 308 : i = 1;
520 [ + + ]: 1540 : while (i < n) {
521 : : uint64_t next, m, j;
522 : :
523 [ - + ]: 1232 : if (a == 0) {
524 [ # # ]: 0 : error(p, "Array chain too short");
525 : 0 : return -EBADMSG;
526 : : }
527 : :
528 [ - + ]: 1232 : 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 : 1232 : r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
534 [ - + ]: 1232 : if (r < 0)
535 : 0 : return r;
536 : :
537 : 1232 : next = le64toh(o->entry_array.next_entry_array_offset);
538 [ + + - + ]: 1232 : 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 : 1232 : m = journal_file_entry_array_n_items(o);
544 [ + + + + ]: 24924 : for (j = 0; i < n && j < m; i++, j++) {
545 : :
546 : 23692 : q = le64toh(o->entry_array.items[j]);
547 [ - + ]: 23692 : if (q <= last) {
548 [ # # ]: 0 : error(p, "Data object's entry array not sorted");
549 : 0 : return -EBADMSG;
550 : : }
551 : 23692 : last = q;
552 : :
553 : 23692 : r = entry_points_to_data(f, cache_entry_fd, n_entries, q, p);
554 [ - + ]: 23692 : if (r < 0)
555 : 0 : return r;
556 : :
557 : : /* Pointer might have moved, reposition */
558 : 23692 : r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
559 [ - + ]: 23692 : if (r < 0)
560 : 0 : return r;
561 : : }
562 : :
563 : 1232 : a = next;
564 : : }
565 : :
566 : 308 : return 0;
567 : : }
568 : :
569 : 4 : 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 [ - + ]: 4 : assert(f);
581 [ - + ]: 4 : assert(cache_data_fd);
582 [ - + ]: 4 : assert(cache_entry_fd);
583 [ - + ]: 4 : assert(cache_entry_array_fd);
584 [ - + ]: 4 : assert(last_usec);
585 : :
586 : 4 : n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
587 [ - + ]: 4 : if (n <= 0)
588 : 0 : return 0;
589 : :
590 : 4 : r = journal_file_map_data_hash_table(f);
591 [ - + ]: 4 : if (r < 0)
592 [ # # ]: 0 : return log_error_errno(r, "Failed to map data hash table: %m");
593 : :
594 [ + + ]: 8192 : for (i = 0; i < n; i++) {
595 : 8188 : uint64_t last = 0, p;
596 : :
597 [ + - ]: 8188 : if (show_progress)
598 : 8188 : draw_progress(0xC000 + scale_progress(0x3FFF, i, n), last_usec);
599 : :
600 : 8188 : p = le64toh(f->data_hash_table[i].head_hash_offset);
601 [ + + ]: 8496 : while (p != 0) {
602 : : Object *o;
603 : : uint64_t next;
604 : :
605 [ - + ]: 308 : 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 : 308 : r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
611 [ - + ]: 308 : if (r < 0)
612 : 0 : return r;
613 : :
614 : 308 : next = le64toh(o->data.next_hash_offset);
615 [ + + - + ]: 308 : 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 [ - + ]: 308 : 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 : 308 : r = verify_data(f, o, p, cache_entry_fd, n_entries, cache_entry_array_fd, n_entry_arrays);
626 [ - + ]: 308 : if (r < 0)
627 : 0 : return r;
628 : :
629 : 308 : last = p;
630 : 308 : p = next;
631 : : }
632 : :
633 [ - + ]: 8188 : 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 : 4 : return 0;
640 : : }
641 : :
642 : 24000 : 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 [ - + ]: 24000 : assert(f);
646 : :
647 : 24000 : n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
648 [ - + ]: 24000 : if (n <= 0)
649 : 0 : return 0;
650 : :
651 : 24000 : r = journal_file_map_data_hash_table(f);
652 [ - + ]: 24000 : if (r < 0)
653 [ # # ]: 0 : return log_error_errno(r, "Failed to map data hash table: %m");
654 : :
655 : 24000 : h = hash % n;
656 : :
657 : 24000 : q = le64toh(f->data_hash_table[h].head_hash_offset);
658 [ + - ]: 24680 : while (q != 0) {
659 : : Object *o;
660 : :
661 [ + + ]: 24680 : if (p == q)
662 : 24000 : return 1;
663 : :
664 : 680 : r = journal_file_move_to_object(f, OBJECT_DATA, q, &o);
665 [ - + ]: 680 : if (r < 0)
666 : 0 : return r;
667 : :
668 : 680 : q = le64toh(o->data.next_hash_offset);
669 : : }
670 : :
671 : 0 : return 0;
672 : : }
673 : :
674 : 24000 : 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 [ - + ]: 24000 : assert(f);
683 [ - + ]: 24000 : assert(o);
684 [ - + ]: 24000 : assert(cache_data_fd);
685 : :
686 : 24000 : n = journal_file_entry_n_items(o);
687 [ + + ]: 48000 : for (i = 0; i < n; i++) {
688 : : uint64_t q, h;
689 : : Object *u;
690 : :
691 : 24000 : q = le64toh(o->entry.items[i].object_offset);
692 : 24000 : h = le64toh(o->entry.items[i].hash);
693 : :
694 [ - + ]: 24000 : 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 : 24000 : r = journal_file_move_to_object(f, OBJECT_DATA, q, &u);
700 [ - + ]: 24000 : if (r < 0)
701 : 0 : return r;
702 : :
703 [ - + ]: 24000 : if (le64toh(u->data.hash) != h) {
704 [ # # ]: 0 : error(p, "Hash mismatch for data object of entry");
705 : 0 : return -EBADMSG;
706 : : }
707 : :
708 : 24000 : r = data_object_in_hash_table(f, h, q);
709 [ - + ]: 24000 : if (r < 0)
710 : 0 : return r;
711 [ - + ]: 24000 : if (r == 0) {
712 [ # # ]: 0 : error(p, "Data object missing from hash table");
713 : 0 : return -EBADMSG;
714 : : }
715 : : }
716 : :
717 : 24000 : return 0;
718 : : }
719 : :
720 : 4 : 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 : 4 : uint64_t i = 0, a, n, last = 0;
729 : : int r;
730 : :
731 [ - + ]: 4 : assert(f);
732 [ - + ]: 4 : assert(cache_data_fd);
733 [ - + ]: 4 : assert(cache_entry_fd);
734 [ - + ]: 4 : assert(cache_entry_array_fd);
735 [ - + ]: 4 : assert(last_usec);
736 : :
737 : 4 : n = le64toh(f->header->n_entries);
738 : 4 : a = le64toh(f->header->entry_array_offset);
739 [ + + ]: 36 : while (i < n) {
740 : : uint64_t next, m, j;
741 : : Object *o;
742 : :
743 [ + - ]: 32 : if (show_progress)
744 : 32 : draw_progress(0x8000 + scale_progress(0x3FFF, i, n), last_usec);
745 : :
746 [ - + ]: 32 : if (a == 0) {
747 [ # # ]: 0 : error(a, "Array chain too short at %"PRIu64" of %"PRIu64, i, n);
748 : 0 : return -EBADMSG;
749 : : }
750 : :
751 [ - + ]: 32 : 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 : 32 : r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
757 [ - + ]: 32 : if (r < 0)
758 : 0 : return r;
759 : :
760 : 32 : next = le64toh(o->entry_array.next_entry_array_offset);
761 [ + + - + ]: 32 : 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 : 32 : m = journal_file_entry_array_n_items(o);
767 [ + + + + ]: 24032 : for (j = 0; i < n && j < m; i++, j++) {
768 : : uint64_t p;
769 : :
770 : 24000 : p = le64toh(o->entry_array.items[j]);
771 [ - + ]: 24000 : if (p <= last) {
772 [ # # ]: 0 : error(a, "Entry array not sorted at %"PRIu64" of %"PRIu64, i, n);
773 : 0 : return -EBADMSG;
774 : : }
775 : 24000 : last = p;
776 : :
777 [ - + ]: 24000 : 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 : 24000 : r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
783 [ - + ]: 24000 : if (r < 0)
784 : 0 : return r;
785 : :
786 : 24000 : r = verify_entry(f, o, p, cache_data_fd, n_data);
787 [ - + ]: 24000 : if (r < 0)
788 : 0 : return r;
789 : :
790 : : /* Pointer might have moved, reposition */
791 : 24000 : r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
792 [ - + ]: 24000 : if (r < 0)
793 : 0 : return r;
794 : : }
795 : :
796 : 32 : a = next;
797 : : }
798 : :
799 : 4 : return 0;
800 : : }
801 : :
802 : 4 : 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 : 4 : uint64_t p = 0, last_epoch = 0, last_tag_realtime = 0, last_sealed_realtime = 0;
810 : :
811 : 4 : uint64_t entry_seqnum = 0, entry_monotonic = 0, entry_realtime = 0;
812 : : sd_id128_t entry_boot_id;
813 : 4 : bool entry_seqnum_set = false, entry_monotonic_set = false, entry_realtime_set = false, found_main_entry_array = false;
814 : 4 : 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 : 4 : usec_t last_usec = 0;
816 : 4 : int data_fd = -1, entry_fd = -1, entry_array_fd = -1;
817 : 4 : MMapFileDescriptor *cache_data_fd = NULL, *cache_entry_fd = NULL, *cache_entry_array_fd = NULL;
818 : : unsigned i;
819 : 4 : bool found_last = false;
820 : 4 : const char *tmp_dir = NULL;
821 : :
822 : : #if HAVE_GCRYPT
823 : 4 : uint64_t last_tag = 0;
824 : : #endif
825 [ - + ]: 4 : assert(f);
826 : :
827 [ - + ]: 4 : 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 [ - + ]: 4 : } else if (f->seal)
838 : 0 : return -ENOKEY;
839 : :
840 : 4 : r = var_tmp_dir(&tmp_dir);
841 [ - + ]: 4 : if (r < 0) {
842 [ # # ]: 0 : log_error_errno(r, "Failed to determine temporary directory: %m");
843 : 0 : goto fail;
844 : : }
845 : :
846 : 4 : data_fd = open_tmpfile_unlinkable(tmp_dir, O_RDWR | O_CLOEXEC);
847 [ - + ]: 4 : 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 : 4 : entry_fd = open_tmpfile_unlinkable(tmp_dir, O_RDWR | O_CLOEXEC);
853 [ - + ]: 4 : 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 : 4 : entry_array_fd = open_tmpfile_unlinkable(tmp_dir, O_RDWR | O_CLOEXEC);
859 [ - + ]: 4 : 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 : 4 : cache_data_fd = mmap_cache_add_fd(f->mmap, data_fd);
866 [ - + ]: 4 : if (!cache_data_fd) {
867 : 0 : r = log_oom();
868 : 0 : goto fail;
869 : : }
870 : :
871 : 4 : cache_entry_fd = mmap_cache_add_fd(f->mmap, entry_fd);
872 [ - + ]: 4 : if (!cache_entry_fd) {
873 : 0 : r = log_oom();
874 : 0 : goto fail;
875 : : }
876 : :
877 : 4 : cache_entry_array_fd = mmap_cache_add_fd(f->mmap, entry_array_fd);
878 [ - + ]: 4 : if (!cache_entry_array_fd) {
879 : 0 : r = log_oom();
880 : 0 : goto fail;
881 : : }
882 : :
883 [ - + ]: 4 : 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 [ + + ]: 32 : for (i = 0; i < sizeof(f->header->reserved); i++)
890 [ - + ]: 28 : 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 : 4 : p = le64toh(f->header->header_size);
900 : : for (;;) {
901 : : /* Early exit if there are no objects in the file, at all */
902 [ - + ]: 25584 : if (le64toh(f->header->tail_object_offset) == 0)
903 : 0 : break;
904 : :
905 [ + - ]: 25584 : if (show_progress)
906 : 25584 : draw_progress(scale_progress(0x7FFF, p, le64toh(f->header->tail_object_offset)), &last_usec);
907 : :
908 : 25584 : r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &o);
909 [ - + ]: 25584 : if (r < 0) {
910 [ # # ]: 0 : error(p, "Invalid object");
911 : 0 : goto fail;
912 : : }
913 : :
914 [ - + ]: 25584 : 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 : 25584 : n_objects++;
921 : :
922 : 25584 : r = journal_file_object_verify(f, p, o);
923 [ - + ]: 25584 : if (r < 0) {
924 [ # # ]: 0 : error_errno(p, r, "Invalid object contents: %m");
925 : 0 : goto fail;
926 : : }
927 : :
928 [ - + ]: 25584 : 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 [ - + # # ]: 25584 : 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 [ - + # # ]: 25584 : 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 [ + + + + : 25584 : switch (o->object.type) {
+ + - - ]
948 : :
949 : 308 : case OBJECT_DATA:
950 : 308 : r = write_uint64(data_fd, p);
951 [ - + ]: 308 : if (r < 0)
952 : 0 : goto fail;
953 : :
954 : 308 : n_data++;
955 : 308 : break;
956 : :
957 : 4 : case OBJECT_FIELD:
958 : 4 : n_fields++;
959 : 4 : break;
960 : :
961 : 24000 : case OBJECT_ENTRY:
962 [ - + # # ]: 24000 : 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 : 24000 : r = write_uint64(entry_fd, p);
969 [ - + ]: 24000 : if (r < 0)
970 : 0 : goto fail;
971 : :
972 [ - + ]: 24000 : 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 [ + + - + ]: 24004 : if (!entry_seqnum_set &&
979 : 4 : 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 [ + + - + ]: 47996 : if (entry_seqnum_set &&
986 : 23996 : 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 : 24000 : entry_seqnum = le64toh(o->entry.seqnum);
993 : 24000 : entry_seqnum_set = true;
994 : :
995 [ + + ]: 24000 : if (entry_monotonic_set &&
996 [ + - - + ]: 47992 : sd_id128_equal(entry_boot_id, o->entry.boot_id) &&
997 : 23996 : 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 : 24000 : entry_monotonic = le64toh(o->entry.monotonic);
1004 : 24000 : entry_boot_id = o->entry.boot_id;
1005 : 24000 : entry_monotonic_set = true;
1006 : :
1007 [ + + - + ]: 24004 : if (!entry_realtime_set &&
1008 : 4 : 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 : 24000 : entry_realtime = le64toh(o->entry.realtime);
1015 : 24000 : entry_realtime_set = true;
1016 : :
1017 : 24000 : n_entries++;
1018 : 24000 : break;
1019 : :
1020 : 4 : case OBJECT_DATA_HASH_TABLE:
1021 [ - + ]: 4 : 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 [ + - ]: 4 : if (le64toh(f->header->data_hash_table_offset) != p + offsetof(HashTableObject, items) ||
1028 [ - + ]: 4 : 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 : 4 : n_data_hash_tables++;
1035 : 4 : break;
1036 : :
1037 : 4 : case OBJECT_FIELD_HASH_TABLE:
1038 [ - + ]: 4 : 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 [ + - ]: 4 : if (le64toh(f->header->field_hash_table_offset) != p + offsetof(HashTableObject, items) ||
1045 [ - + ]: 4 : 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 : 4 : n_field_hash_tables++;
1052 : 4 : break;
1053 : :
1054 : 1264 : case OBJECT_ENTRY_ARRAY:
1055 : 1264 : r = write_uint64(entry_array_fd, p);
1056 [ - + ]: 1264 : if (r < 0)
1057 : 0 : goto fail;
1058 : :
1059 [ + + ]: 1264 : if (p == le64toh(f->header->entry_array_offset)) {
1060 [ - + ]: 4 : 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 : 4 : found_main_entry_array = true;
1067 : : }
1068 : :
1069 : 1264 : n_entry_arrays++;
1070 : 1264 : 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 [ + + ]: 25584 : if (p == le64toh(f->header->tail_object_offset)) {
1165 : 4 : found_last = true;
1166 : 4 : break;
1167 : : }
1168 : :
1169 : 25580 : p = p + ALIGN64(le64toh(o->object.size));
1170 : : };
1171 : :
1172 [ - + # # ]: 4 : 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 [ - + ]: 4 : 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 [ - + ]: 4 : 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 [ + - - + ]: 8 : if (JOURNAL_HEADER_CONTAINS(f->header, n_data) &&
1191 : 4 : 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 [ + - - + ]: 8 : if (JOURNAL_HEADER_CONTAINS(f->header, n_fields) &&
1198 : 4 : 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 [ + - - + ]: 8 : if (JOURNAL_HEADER_CONTAINS(f->header, n_tags) &&
1205 : 4 : 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 [ + - - + ]: 8 : if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays) &&
1212 : 4 : 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 [ - + # # ]: 4 : 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 [ + - - + ]: 8 : if (entry_seqnum_set &&
1225 : 4 : 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 [ + - ]: 4 : if (entry_monotonic_set &&
1232 [ + - - + ]: 8 : (sd_id128_equal(entry_boot_id, f->header->boot_id) &&
1233 : 4 : 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 [ + - - + ]: 4 : 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 : 4 : 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 [ - + ]: 4 : if (r < 0)
1260 : 0 : goto fail;
1261 : :
1262 : 4 : 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 [ - + ]: 4 : if (r < 0)
1269 : 0 : goto fail;
1270 : :
1271 [ + - ]: 4 : if (show_progress)
1272 : 4 : flush_progress();
1273 : :
1274 : 4 : mmap_cache_free_fd(f->mmap, cache_data_fd);
1275 : 4 : mmap_cache_free_fd(f->mmap, cache_entry_fd);
1276 : 4 : mmap_cache_free_fd(f->mmap, cache_entry_array_fd);
1277 : :
1278 : 4 : safe_close(data_fd);
1279 : 4 : safe_close(entry_fd);
1280 : 4 : safe_close(entry_array_fd);
1281 : :
1282 [ + - ]: 4 : if (first_contained)
1283 : 4 : *first_contained = le64toh(f->header->head_entry_realtime);
1284 [ + - ]: 4 : if (last_validated)
1285 : 4 : *last_validated = last_sealed_realtime;
1286 [ + - ]: 4 : if (last_contained)
1287 : 4 : *last_contained = le64toh(f->header->tail_entry_realtime);
1288 : :
1289 : 4 : 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 : : }
|