journal-verify.c revision bca9e39dfadaefc4b02c0dd378adc3d6221071de
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering/***
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering This file is part of systemd.
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering Copyright 2012 Lennart Poettering
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering systemd is free software; you can redistribute it and/or modify it
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering under the terms of the GNU Lesser General Public License as published by
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering the Free Software Foundation; either version 2.1 of the License, or
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering (at your option) any later version.
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering systemd is distributed in the hope that it will be useful, but
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering Lesser General Public License for more details.
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering You should have received a copy of the GNU Lesser General Public License
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering***/
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering#include <unistd.h>
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering#include <sys/mman.h>
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering#include <fcntl.h>
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering#include <stddef.h>
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering#include "util.h"
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering#include "macro.h"
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering#include "journal-def.h"
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering#include "journal-file.h"
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering#include "journal-authenticate.h"
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering#include "journal-verify.h"
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering#include "lookup3.h"
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering#include "compress.h"
31885cd5e38ec9807a6a7ab32660cf8c2fcf48f7Zbigniew Jędrzejewski-Szmek#include "terminal-util.h"
b68fa010f72599e6da5822feda5ae3a47a4e63d8Simon Peeters
b68fa010f72599e6da5822feda5ae3a47a4e63d8Simon Peetersstatic void draw_progress(uint64_t p, usec_t *last_usec) {
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering unsigned n, i, j, k;
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering usec_t z, x;
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering if (!on_tty())
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering return;
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering z = now(CLOCK_MONOTONIC);
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering x = *last_usec;
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering if (x != 0 && x + 40 * USEC_PER_MSEC > z)
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering return;
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering *last_usec = z;
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering n = (3 * columns()) / 4;
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering j = (n * (unsigned) p) / 65535ULL;
5841bd803f1b651c0d70c6ae114630723a76d1daZbigniew Jędrzejewski-Szmek k = n - j;
5841bd803f1b651c0d70c6ae114630723a76d1daZbigniew Jędrzejewski-Szmek
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering fputs("\r\x1B[?25l" ANSI_HIGHLIGHT_GREEN_ON, stdout);
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering for (i = 0; i < j; i++)
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering fputs("\xe2\x96\x88", stdout);
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering fputs(ANSI_HIGHLIGHT_OFF, stdout);
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering for (i = 0; i < k; i++)
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering fputs("\xe2\x96\x91", stdout);
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering printf(" %3"PRIu64"%%", 100U * p / 65535U);
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering fputs("\r\x1B[?25h", stdout);
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering fflush(stdout);
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering}
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poetteringstatic uint64_t scale_progress(uint64_t scale, uint64_t p, uint64_t m) {
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering /* Calculates scale * p / m, but handles m == 0 safely, and saturates */
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering if (p >= m || m == 0)
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering return scale;
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering return scale * p / m;
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering}
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poetteringstatic void flush_progress(void) {
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering unsigned n, i;
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering if (!on_tty())
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering return;
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering n = (3 * columns()) / 4;
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering putchar('\r');
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering for (i = 0; i < n + 5; i++)
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering putchar(' ');
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering putchar('\r');
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering fflush(stdout);
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering}
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering#define debug(_offset, _fmt, ...) do{ \
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering flush_progress(); \
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering log_debug(OFSfmt": " _fmt, _offset, ##__VA_ARGS__); \
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering } while(0)
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering#define warning(_offset, _fmt, ...) do{ \
56f64d95763a799ba4475daf44d8e9f72a1bd474Michal Schmidt flush_progress(); \
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering log_warning(OFSfmt": " _fmt, _offset, ##__VA_ARGS__); \
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering } while(0)
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering#define error(_offset, _fmt, ...) do{ \
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering flush_progress(); \
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering log_error(OFSfmt": " _fmt, (uint64_t)_offset, ##__VA_ARGS__); \
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering } while(0)
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poetteringstatic int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o) {
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering uint64_t i;
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering assert(f);
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering assert(offset);
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering assert(o);
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering /* This does various superficial tests about the length an
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering * possible field values. It does not follow any references to
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering * other objects. */
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering if ((o->object.flags & OBJECT_COMPRESSED_XZ) &&
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering o->object.type != OBJECT_DATA) {
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering error(offset, "Found compressed object that isn't of type DATA, which is not allowed.");
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering return -EBADMSG;
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering }
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering switch (o->object.type) {
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering case OBJECT_DATA: {
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering uint64_t h1, h2;
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering int compression, r;
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering if (le64toh(o->data.entry_offset) == 0)
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering warning(offset, "unused data (entry_offset==0)");
56f64d95763a799ba4475daf44d8e9f72a1bd474Michal Schmidt
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering if ((le64toh(o->data.entry_offset) == 0) ^ (le64toh(o->data.n_entries) == 0)) {
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering error(offset, "bad n_entries: %"PRIu64, o->data.n_entries);
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering return -EBADMSG;
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering }
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering if (le64toh(o->object.size) - offsetof(DataObject, payload) <= 0) {
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering error(offset, "bad object size (<= %zu): %"PRIu64,
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering offsetof(DataObject, payload),
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering le64toh(o->object.size));
0bee65f0622c4faa8ac8ae771cc0c8a936dfa284Lennart Poettering return -EBADMSG;
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering }
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering h1 = le64toh(o->data.hash);
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering compression = o->object.flags & OBJECT_COMPRESSION_MASK;
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering if (compression) {
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering _cleanup_free_ void *b = NULL;
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering size_t alloc = 0, b_size;
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering r = decompress_blob(compression,
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering o->data.payload,
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering le64toh(o->object.size) - offsetof(Object, data.payload),
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering &b, &alloc, &b_size, 0);
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering if (r < 0) {
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering error(offset, "%s decompression failed: %s",
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering object_compressed_to_string(compression), strerror(-r));
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering return r;
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering }
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering h2 = hash64(b, b_size);
df758e98754016119a9c8d49213a636a80ffab22Kay Sievers } else
3e09eb5c83e56bc0184bd9d9c44f76047464f77cAndreas Henriksson h2 = hash64(o->data.payload, le64toh(o->object.size) - offsetof(Object, data.payload));
df758e98754016119a9c8d49213a636a80ffab22Kay Sievers
df758e98754016119a9c8d49213a636a80ffab22Kay Sievers if (h1 != h2) {
ccd06097c79218f7d5ea4c21721bbcbc7c467dcaZbigniew Jędrzejewski-Szmek error(offset, "invalid hash (%08"PRIx64" vs. %08"PRIx64, h1, h2);
df758e98754016119a9c8d49213a636a80ffab22Kay Sievers return -EBADMSG;
df758e98754016119a9c8d49213a636a80ffab22Kay Sievers }
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering if (!VALID64(o->data.next_hash_offset) ||
ccd06097c79218f7d5ea4c21721bbcbc7c467dcaZbigniew Jędrzejewski-Szmek !VALID64(o->data.next_field_offset) ||
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering !VALID64(o->data.entry_offset) ||
56f64d95763a799ba4475daf44d8e9f72a1bd474Michal Schmidt !VALID64(o->data.entry_array_offset)) {
0bee65f0622c4faa8ac8ae771cc0c8a936dfa284Lennart Poettering error(offset, "invalid offset (next_hash_offset="OFSfmt", next_field_offset="OFSfmt", entry_offset="OFSfmt", entry_array_offset="OFSfmt,
0bee65f0622c4faa8ac8ae771cc0c8a936dfa284Lennart Poettering o->data.next_hash_offset,
0bee65f0622c4faa8ac8ae771cc0c8a936dfa284Lennart Poettering o->data.next_field_offset,
0bee65f0622c4faa8ac8ae771cc0c8a936dfa284Lennart Poettering o->data.entry_offset,
0bee65f0622c4faa8ac8ae771cc0c8a936dfa284Lennart Poettering o->data.entry_array_offset);
0bee65f0622c4faa8ac8ae771cc0c8a936dfa284Lennart Poettering return -EBADMSG;
0bee65f0622c4faa8ac8ae771cc0c8a936dfa284Lennart Poettering }
0bee65f0622c4faa8ac8ae771cc0c8a936dfa284Lennart Poettering
0bee65f0622c4faa8ac8ae771cc0c8a936dfa284Lennart Poettering break;
0bee65f0622c4faa8ac8ae771cc0c8a936dfa284Lennart Poettering }
0bee65f0622c4faa8ac8ae771cc0c8a936dfa284Lennart Poettering
0bee65f0622c4faa8ac8ae771cc0c8a936dfa284Lennart Poettering case OBJECT_FIELD:
0bee65f0622c4faa8ac8ae771cc0c8a936dfa284Lennart Poettering if (le64toh(o->object.size) - offsetof(FieldObject, payload) <= 0) {
0bee65f0622c4faa8ac8ae771cc0c8a936dfa284Lennart Poettering error(offset,
0bee65f0622c4faa8ac8ae771cc0c8a936dfa284Lennart Poettering "bad field size (<= %zu): %"PRIu64,
0bee65f0622c4faa8ac8ae771cc0c8a936dfa284Lennart Poettering offsetof(FieldObject, payload),
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering le64toh(o->object.size));
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering return -EBADMSG;
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering }
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering if (!VALID64(o->field.next_hash_offset) ||
6301a98cdf26dc073f5317506c806bfa69f74cc8Lennart Poettering !VALID64(o->field.head_data_offset)) {
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering error(offset,
e1d758033dc7e101ab32323a0f1649d8daf56a22Ronny Chevalier "invalid offset (next_hash_offset="OFSfmt", head_data_offset="OFSfmt,
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering o->field.next_hash_offset,
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering o->field.head_data_offset);
d5099efc47d4e6ac60816b5381a5f607ab03f06eMichal Schmidt return -EBADMSG;
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering }
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering break;
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering case OBJECT_ENTRY:
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering if ((le64toh(o->object.size) - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0) {
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering error(offset,
56f64d95763a799ba4475daf44d8e9f72a1bd474Michal Schmidt "bad entry size (<= %zu): %"PRIu64,
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering offsetof(EntryObject, items),
0bee65f0622c4faa8ac8ae771cc0c8a936dfa284Lennart Poettering le64toh(o->object.size));
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering return -EBADMSG;
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering }
56f64d95763a799ba4475daf44d8e9f72a1bd474Michal Schmidt
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering if ((le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0) {
3d141780b8d509e4a6c3083de207cd84cbce187bLennart Poettering error(offset,
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering "invalid number items in entry: %"PRIu64,
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering (le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem));
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering return -EBADMSG;
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering }
if (le64toh(o->entry.seqnum) <= 0) {
error(offset,
"invalid entry seqnum: %"PRIx64,
le64toh(o->entry.seqnum));
return -EBADMSG;
}
if (!VALID_REALTIME(le64toh(o->entry.realtime))) {
error(offset,
"invalid entry realtime timestamp: %"PRIu64,
le64toh(o->entry.realtime));
return -EBADMSG;
}
if (!VALID_MONOTONIC(le64toh(o->entry.monotonic))) {
error(offset,
"invalid entry monotonic timestamp: %"PRIu64,
le64toh(o->entry.monotonic));
return -EBADMSG;
}
for (i = 0; i < journal_file_entry_n_items(o); i++) {
if (o->entry.items[i].object_offset == 0 ||
!VALID64(o->entry.items[i].object_offset)) {
error(offset,
"invalid entry item (%"PRIu64"/%"PRIu64" offset: "OFSfmt,
i, journal_file_entry_n_items(o),
o->entry.items[i].object_offset);
return -EBADMSG;
}
}
break;
case OBJECT_DATA_HASH_TABLE:
case OBJECT_FIELD_HASH_TABLE:
if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) % sizeof(HashItem) != 0 ||
(le64toh(o->object.size) - offsetof(HashTableObject, items)) / sizeof(HashItem) <= 0) {
error(offset,
"invalid %s hash table size: %"PRIu64,
o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field",
le64toh(o->object.size));
return -EBADMSG;
}
for (i = 0; i < journal_file_hash_table_n_items(o); i++) {
if (o->hash_table.items[i].head_hash_offset != 0 &&
!VALID64(le64toh(o->hash_table.items[i].head_hash_offset))) {
error(offset,
"invalid %s hash table item (%"PRIu64"/%"PRIu64") head_hash_offset: "OFSfmt,
o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field",
i, journal_file_hash_table_n_items(o),
le64toh(o->hash_table.items[i].head_hash_offset));
return -EBADMSG;
}
if (o->hash_table.items[i].tail_hash_offset != 0 &&
!VALID64(le64toh(o->hash_table.items[i].tail_hash_offset))) {
error(offset,
"invalid %s hash table item (%"PRIu64"/%"PRIu64") tail_hash_offset: "OFSfmt,
o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field",
i, journal_file_hash_table_n_items(o),
le64toh(o->hash_table.items[i].tail_hash_offset));
return -EBADMSG;
}
if ((o->hash_table.items[i].head_hash_offset != 0) !=
(o->hash_table.items[i].tail_hash_offset != 0)) {
error(offset,
"invalid %s hash table item (%"PRIu64"/%"PRIu64"): head_hash_offset="OFSfmt" tail_hash_offset="OFSfmt,
o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field",
i, journal_file_hash_table_n_items(o),
le64toh(o->hash_table.items[i].head_hash_offset),
le64toh(o->hash_table.items[i].tail_hash_offset));
return -EBADMSG;
}
}
break;
case OBJECT_ENTRY_ARRAY:
if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) % sizeof(le64_t) != 0 ||
(le64toh(o->object.size) - offsetof(EntryArrayObject, items)) / sizeof(le64_t) <= 0) {
error(offset,
"invalid object entry array size: %"PRIu64,
le64toh(o->object.size));
return -EBADMSG;
}
if (!VALID64(o->entry_array.next_entry_array_offset)) {
error(offset,
"invalid object entry array next_entry_array_offset: "OFSfmt,
o->entry_array.next_entry_array_offset);
return -EBADMSG;
}
for (i = 0; i < journal_file_entry_array_n_items(o); i++)
if (le64toh(o->entry_array.items[i]) != 0 &&
!VALID64(le64toh(o->entry_array.items[i]))) {
error(offset,
"invalid object entry array item (%"PRIu64"/%"PRIu64"): "OFSfmt,
i, journal_file_entry_array_n_items(o),
le64toh(o->entry_array.items[i]));
return -EBADMSG;
}
break;
case OBJECT_TAG:
if (le64toh(o->object.size) != sizeof(TagObject)) {
error(offset,
"invalid object tag size: %"PRIu64,
le64toh(o->object.size));
return -EBADMSG;
}
if (!VALID_EPOCH(o->tag.epoch)) {
error(offset,
"invalid object tag epoch: %"PRIu64,
o->tag.epoch);
return -EBADMSG;
}
break;
}
return 0;
}
static int write_uint64(int fd, uint64_t p) {
ssize_t k;
k = write(fd, &p, sizeof(p));
if (k < 0)
return -errno;
if (k != sizeof(p))
return -EIO;
return 0;
}
static int contains_uint64(MMapCache *m, int fd, uint64_t n, uint64_t p) {
uint64_t a, b;
int r;
assert(m);
assert(fd >= 0);
/* Bisection ... */
a = 0; b = n;
while (a < b) {
uint64_t c, *z;
c = (a + b) / 2;
r = mmap_cache_get(m, fd, PROT_READ|PROT_WRITE, 0, false, c * sizeof(uint64_t), sizeof(uint64_t), NULL, (void **) &z);
if (r < 0)
return r;
if (*z == p)
return 1;
if (a + 1 >= b)
return 0;
if (p < *z)
b = c;
else
a = c;
}
return 0;
}
static int entry_points_to_data(
JournalFile *f,
int entry_fd,
uint64_t n_entries,
uint64_t entry_p,
uint64_t data_p) {
int r;
uint64_t i, n, a;
Object *o;
bool found = false;
assert(f);
assert(entry_fd >= 0);
if (!contains_uint64(f->mmap, entry_fd, n_entries, entry_p)) {
error(data_p,
"data object references invalid entry at "OFSfmt, entry_p);
return -EBADMSG;
}
r = journal_file_move_to_object(f, OBJECT_ENTRY, entry_p, &o);
if (r < 0)
return r;
n = journal_file_entry_n_items(o);
for (i = 0; i < n; i++)
if (le64toh(o->entry.items[i].object_offset) == data_p) {
found = true;
break;
}
if (!found) {
error(entry_p,
"data object at "OFSfmt" not referenced by linked entry", data_p);
return -EBADMSG;
}
/* Check if this entry is also in main entry array. Since the
* main entry array has already been verified we can rely on
* its consistency. */
i = 0;
n = le64toh(f->header->n_entries);
a = le64toh(f->header->entry_array_offset);
while (i < n) {
uint64_t m, u;
r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
if (r < 0)
return r;
m = journal_file_entry_array_n_items(o);
u = MIN(n - i, m);
if (entry_p <= le64toh(o->entry_array.items[u-1])) {
uint64_t x, y, z;
x = 0;
y = u;
while (x < y) {
z = (x + y) / 2;
if (le64toh(o->entry_array.items[z]) == entry_p)
return 0;
if (x + 1 >= y)
break;
if (entry_p < le64toh(o->entry_array.items[z]))
y = z;
else
x = z;
}
error(entry_p, "entry object doesn't exist in main entry array");
return -EBADMSG;
}
i += u;
a = le64toh(o->entry_array.next_entry_array_offset);
}
return 0;
}
static int verify_data(
JournalFile *f,
Object *o, uint64_t p,
int entry_fd, uint64_t n_entries,
int entry_array_fd, uint64_t n_entry_arrays) {
uint64_t i, n, a, last, q;
int r;
assert(f);
assert(o);
assert(entry_fd >= 0);
assert(entry_array_fd >= 0);
n = le64toh(o->data.n_entries);
a = le64toh(o->data.entry_array_offset);
/* Entry array means at least two objects */
if (a && n < 2) {
error(p,
"entry array present (entry_array_offset="OFSfmt", but n_entries=%"PRIu64")",
a, n);
return -EBADMSG;
}
if (n == 0)
return 0;
/* We already checked that earlier */
assert(o->data.entry_offset);
last = q = le64toh(o->data.entry_offset);
r = entry_points_to_data(f, entry_fd, n_entries, q, p);
if (r < 0)
return r;
i = 1;
while (i < n) {
uint64_t next, m, j;
if (a == 0) {
error(p, "array chain too short");
return -EBADMSG;
}
if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) {
error(p, "invalid array offset "OFSfmt, a);
return -EBADMSG;
}
r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
if (r < 0)
return r;
next = le64toh(o->entry_array.next_entry_array_offset);
if (next != 0 && next <= a) {
error(p, "array chain has cycle (jumps back from "OFSfmt" to "OFSfmt")",
a, next);
return -EBADMSG;
}
m = journal_file_entry_array_n_items(o);
for (j = 0; i < n && j < m; i++, j++) {
q = le64toh(o->entry_array.items[j]);
if (q <= last) {
error(p, "data object's entry array not sorted");
return -EBADMSG;
}
last = q;
r = entry_points_to_data(f, entry_fd, n_entries, q, p);
if (r < 0)
return r;
/* Pointer might have moved, reposition */
r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
if (r < 0)
return r;
}
a = next;
}
return 0;
}
static int verify_hash_table(
JournalFile *f,
int data_fd, uint64_t n_data,
int entry_fd, uint64_t n_entries,
int entry_array_fd, uint64_t n_entry_arrays,
usec_t *last_usec,
bool show_progress) {
uint64_t i, n;
int r;
assert(f);
assert(data_fd >= 0);
assert(entry_fd >= 0);
assert(entry_array_fd >= 0);
assert(last_usec);
n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
if (n <= 0)
return 0;
r = journal_file_map_data_hash_table(f);
if (r < 0)
return log_error_errno(r, "Failed to map data hash table: %m");
for (i = 0; i < n; i++) {
uint64_t last = 0, p;
if (show_progress)
draw_progress(0xC000 + scale_progress(0x3FFF, i, n), last_usec);
p = le64toh(f->data_hash_table[i].head_hash_offset);
while (p != 0) {
Object *o;
uint64_t next;
if (!contains_uint64(f->mmap, data_fd, n_data, p)) {
error(p, "invalid data object at hash entry %"PRIu64" of %"PRIu64,
i, n);
return -EBADMSG;
}
r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
if (r < 0)
return r;
next = le64toh(o->data.next_hash_offset);
if (next != 0 && next <= p) {
error(p, "hash chain has a cycle in hash entry %"PRIu64" of %"PRIu64,
i, n);
return -EBADMSG;
}
if (le64toh(o->data.hash) % n != i) {
error(p, "hash value mismatch in hash entry %"PRIu64" of %"PRIu64,
i, n);
return -EBADMSG;
}
r = verify_data(f, o, p, entry_fd, n_entries, entry_array_fd, n_entry_arrays);
if (r < 0)
return r;
last = p;
p = next;
}
if (last != le64toh(f->data_hash_table[i].tail_hash_offset)) {
error(p, "tail hash pointer mismatch in hash table");
return -EBADMSG;
}
}
return 0;
}
static int data_object_in_hash_table(JournalFile *f, uint64_t hash, uint64_t p) {
uint64_t n, h, q;
int r;
assert(f);
n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
if (n <= 0)
return 0;
r = journal_file_map_data_hash_table(f);
if (r < 0)
return log_error_errno(r, "Failed to map data hash table: %m");
h = hash % n;
q = le64toh(f->data_hash_table[h].head_hash_offset);
while (q != 0) {
Object *o;
if (p == q)
return 1;
r = journal_file_move_to_object(f, OBJECT_DATA, q, &o);
if (r < 0)
return r;
q = le64toh(o->data.next_hash_offset);
}
return 0;
}
static int verify_entry(
JournalFile *f,
Object *o, uint64_t p,
int data_fd, uint64_t n_data) {
uint64_t i, n;
int r;
assert(f);
assert(o);
assert(data_fd >= 0);
n = journal_file_entry_n_items(o);
for (i = 0; i < n; i++) {
uint64_t q, h;
Object *u;
q = le64toh(o->entry.items[i].object_offset);
h = le64toh(o->entry.items[i].hash);
if (!contains_uint64(f->mmap, data_fd, n_data, q)) {
error(p, "invalid data object of entry");
return -EBADMSG;
}
r = journal_file_move_to_object(f, OBJECT_DATA, q, &u);
if (r < 0)
return r;
if (le64toh(u->data.hash) != h) {
error(p, "hash mismatch for data object of entry");
return -EBADMSG;
}
r = data_object_in_hash_table(f, h, q);
if (r < 0)
return r;
if (r == 0) {
error(p, "data object missing from hash table");
return -EBADMSG;
}
}
return 0;
}
static int verify_entry_array(
JournalFile *f,
int data_fd, uint64_t n_data,
int entry_fd, uint64_t n_entries,
int entry_array_fd, uint64_t n_entry_arrays,
usec_t *last_usec,
bool show_progress) {
uint64_t i = 0, a, n, last = 0;
int r;
assert(f);
assert(data_fd >= 0);
assert(entry_fd >= 0);
assert(entry_array_fd >= 0);
assert(last_usec);
n = le64toh(f->header->n_entries);
a = le64toh(f->header->entry_array_offset);
while (i < n) {
uint64_t next, m, j;
Object *o;
if (show_progress)
draw_progress(0x8000 + scale_progress(0x3FFF, i, n), last_usec);
if (a == 0) {
error(a, "array chain too short at %"PRIu64" of %"PRIu64, i, n);
return -EBADMSG;
}
if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) {
error(a, "invalid array %"PRIu64" of %"PRIu64, i, n);
return -EBADMSG;
}
r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
if (r < 0)
return r;
next = le64toh(o->entry_array.next_entry_array_offset);
if (next != 0 && next <= a) {
error(a,
"array chain has cycle at %"PRIu64" of %"PRIu64" (jumps back from to "OFSfmt")",
i, n, next);
return -EBADMSG;
}
m = journal_file_entry_array_n_items(o);
for (j = 0; i < n && j < m; i++, j++) {
uint64_t p;
p = le64toh(o->entry_array.items[j]);
if (p <= last) {
error(a, "entry array not sorted at %"PRIu64" of %"PRIu64,
i, n);
return -EBADMSG;
}
last = p;
if (!contains_uint64(f->mmap, entry_fd, n_entries, p)) {
error(a, "invalid array entry at %"PRIu64" of %"PRIu64,
i, n);
return -EBADMSG;
}
r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
if (r < 0)
return r;
r = verify_entry(f, o, p, data_fd, n_data);
if (r < 0)
return r;
/* Pointer might have moved, reposition */
r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
if (r < 0)
return r;
}
a = next;
}
return 0;
}
int journal_file_verify(
JournalFile *f,
const char *key,
usec_t *first_contained, usec_t *last_validated, usec_t *last_contained,
bool show_progress) {
int r;
Object *o;
uint64_t p = 0, last_epoch = 0, last_tag_realtime = 0, last_sealed_realtime = 0;
uint64_t entry_seqnum = 0, entry_monotonic = 0, entry_realtime = 0;
sd_id128_t entry_boot_id;
bool entry_seqnum_set = false, entry_monotonic_set = false, entry_realtime_set = false, found_main_entry_array = false;
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;
usec_t last_usec = 0;
int data_fd = -1, entry_fd = -1, entry_array_fd = -1;
unsigned i;
bool found_last = false;
#ifdef HAVE_GCRYPT
uint64_t last_tag = 0;
#endif
assert(f);
if (key) {
#ifdef HAVE_GCRYPT
r = journal_file_parse_verification_key(f, key);
if (r < 0) {
log_error("Failed to parse seed.");
return r;
}
#else
return -EOPNOTSUPP;
#endif
} else if (f->seal)
return -ENOKEY;
data_fd = open_tmpfile("/var/tmp", O_RDWR | O_CLOEXEC);
if (data_fd < 0) {
log_error_errno(errno, "Failed to create data file: %m");
r = -errno;
goto fail;
}
entry_fd = open_tmpfile("/var/tmp", O_RDWR | O_CLOEXEC);
if (entry_fd < 0) {
log_error_errno(errno, "Failed to create entry file: %m");
r = -errno;
goto fail;
}
entry_array_fd = open_tmpfile("/var/tmp", O_RDWR | O_CLOEXEC);
if (entry_array_fd < 0) {
log_error_errno(errno, "Failed to create entry array file: %m");
r = -errno;
goto fail;
}
if (le32toh(f->header->compatible_flags) & ~HEADER_COMPATIBLE_SUPPORTED) {
log_error("Cannot verify file with unknown extensions.");
r = -EOPNOTSUPP;
goto fail;
}
for (i = 0; i < sizeof(f->header->reserved); i++)
if (f->header->reserved[i] != 0) {
error(offsetof(Header, reserved[i]), "reserved field is non-zero");
r = -EBADMSG;
goto fail;
}
/* First iteration: we go through all objects, verify the
* superficial structure, headers, hashes. */
p = le64toh(f->header->header_size);
for (;;) {
/* Early exit if there are no objects in the file, at all */
if (le64toh(f->header->tail_object_offset) == 0)
break;
if (show_progress)
draw_progress(scale_progress(0x7FFF, p, le64toh(f->header->tail_object_offset)), &last_usec);
r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &o);
if (r < 0) {
error(p, "invalid object");
goto fail;
}
if (p > le64toh(f->header->tail_object_offset)) {
error(offsetof(Header, tail_object_offset), "invalid tail object pointer");
r = -EBADMSG;
goto fail;
}
n_objects ++;
r = journal_file_object_verify(f, p, o);
if (r < 0) {
error(p, "invalid object contents: %s", strerror(-r));
goto fail;
}
if ((o->object.flags & OBJECT_COMPRESSED_XZ) &&
(o->object.flags & OBJECT_COMPRESSED_LZ4)) {
error(p, "objected with double compression");
r = -EINVAL;
goto fail;
}
if ((o->object.flags & OBJECT_COMPRESSED_XZ) && !JOURNAL_HEADER_COMPRESSED_XZ(f->header)) {
error(p, "XZ compressed object in file without XZ compression");
r = -EBADMSG;
goto fail;
}
if ((o->object.flags & OBJECT_COMPRESSED_LZ4) && !JOURNAL_HEADER_COMPRESSED_LZ4(f->header)) {
error(p, "LZ4 compressed object in file without LZ4 compression");
r = -EBADMSG;
goto fail;
}
switch (o->object.type) {
case OBJECT_DATA:
r = write_uint64(data_fd, p);
if (r < 0)
goto fail;
n_data++;
break;
case OBJECT_FIELD:
n_fields++;
break;
case OBJECT_ENTRY:
if (JOURNAL_HEADER_SEALED(f->header) && n_tags <= 0) {
error(p, "first entry before first tag");
r = -EBADMSG;
goto fail;
}
r = write_uint64(entry_fd, p);
if (r < 0)
goto fail;
if (le64toh(o->entry.realtime) < last_tag_realtime) {
error(p, "older entry after newer tag");
r = -EBADMSG;
goto fail;
}
if (!entry_seqnum_set &&
le64toh(o->entry.seqnum) != le64toh(f->header->head_entry_seqnum)) {
error(p, "head entry sequence number incorrect");
r = -EBADMSG;
goto fail;
}
if (entry_seqnum_set &&
entry_seqnum >= le64toh(o->entry.seqnum)) {
error(p, "entry sequence number out of synchronization");
r = -EBADMSG;
goto fail;
}
entry_seqnum = le64toh(o->entry.seqnum);
entry_seqnum_set = true;
if (entry_monotonic_set &&
sd_id128_equal(entry_boot_id, o->entry.boot_id) &&
entry_monotonic > le64toh(o->entry.monotonic)) {
error(p, "entry timestamp out of synchronization");
r = -EBADMSG;
goto fail;
}
entry_monotonic = le64toh(o->entry.monotonic);
entry_boot_id = o->entry.boot_id;
entry_monotonic_set = true;
if (!entry_realtime_set &&
le64toh(o->entry.realtime) != le64toh(f->header->head_entry_realtime)) {
error(p, "head entry realtime timestamp incorrect");
r = -EBADMSG;
goto fail;
}
entry_realtime = le64toh(o->entry.realtime);
entry_realtime_set = true;
n_entries ++;
break;
case OBJECT_DATA_HASH_TABLE:
if (n_data_hash_tables > 1) {
error(p, "more than one data hash table");
r = -EBADMSG;
goto fail;
}
if (le64toh(f->header->data_hash_table_offset) != p + offsetof(HashTableObject, items) ||
le64toh(f->header->data_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
error(p, "header fields for data hash table invalid");
r = -EBADMSG;
goto fail;
}
n_data_hash_tables++;
break;
case OBJECT_FIELD_HASH_TABLE:
if (n_field_hash_tables > 1) {
error(p, "more than one field hash table");
r = -EBADMSG;
goto fail;
}
if (le64toh(f->header->field_hash_table_offset) != p + offsetof(HashTableObject, items) ||
le64toh(f->header->field_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
error(p, "header fields for field hash table invalid");
r = -EBADMSG;
goto fail;
}
n_field_hash_tables++;
break;
case OBJECT_ENTRY_ARRAY:
r = write_uint64(entry_array_fd, p);
if (r < 0)
goto fail;
if (p == le64toh(f->header->entry_array_offset)) {
if (found_main_entry_array) {
error(p, "more than one main entry array");
r = -EBADMSG;
goto fail;
}
found_main_entry_array = true;
}
n_entry_arrays++;
break;
case OBJECT_TAG:
if (!JOURNAL_HEADER_SEALED(f->header)) {
error(p, "tag object in file without sealing");
r = -EBADMSG;
goto fail;
}
if (le64toh(o->tag.seqnum) != n_tags + 1) {
error(p, "tag sequence number out of synchronization");
r = -EBADMSG;
goto fail;
}
if (le64toh(o->tag.epoch) < last_epoch) {
error(p, "epoch sequence out of synchronization");
r = -EBADMSG;
goto fail;
}
#ifdef HAVE_GCRYPT
if (f->seal) {
uint64_t q, rt;
debug(p, "checking tag %"PRIu64"...", le64toh(o->tag.seqnum));
rt = f->fss_start_usec + o->tag.epoch * f->fss_interval_usec;
if (entry_realtime_set && entry_realtime >= rt + f->fss_interval_usec) {
error(p, "tag/entry realtime timestamp out of synchronization");
r = -EBADMSG;
goto fail;
}
/* OK, now we know the epoch. So let's now set
* it, and calculate the HMAC for everything
* since the last tag. */
r = journal_file_fsprg_seek(f, le64toh(o->tag.epoch));
if (r < 0)
goto fail;
r = journal_file_hmac_start(f);
if (r < 0)
goto fail;
if (last_tag == 0) {
r = journal_file_hmac_put_header(f);
if (r < 0)
goto fail;
q = le64toh(f->header->header_size);
} else
q = last_tag;
while (q <= p) {
r = journal_file_move_to_object(f, OBJECT_UNUSED, q, &o);
if (r < 0)
goto fail;
r = journal_file_hmac_put_object(f, OBJECT_UNUSED, o, q);
if (r < 0)
goto fail;
q = q + ALIGN64(le64toh(o->object.size));
}
/* Position might have changed, let's reposition things */
r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &o);
if (r < 0)
goto fail;
if (memcmp(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH) != 0) {
error(p, "tag failed verification");
r = -EBADMSG;
goto fail;
}
f->hmac_running = false;
last_tag_realtime = rt;
last_sealed_realtime = entry_realtime;
}
last_tag = p + ALIGN64(le64toh(o->object.size));
#endif
last_epoch = le64toh(o->tag.epoch);
n_tags ++;
break;
default:
n_weird ++;
}
if (p == le64toh(f->header->tail_object_offset)) {
found_last = true;
break;
}
p = p + ALIGN64(le64toh(o->object.size));
};
if (!found_last && le64toh(f->header->tail_object_offset) != 0) {
error(le64toh(f->header->tail_object_offset), "tail object pointer dead");
r = -EBADMSG;
goto fail;
}
if (n_objects != le64toh(f->header->n_objects)) {
error(offsetof(Header, n_objects), "object number mismatch");
r = -EBADMSG;
goto fail;
}
if (n_entries != le64toh(f->header->n_entries)) {
error(offsetof(Header, n_entries), "entry number mismatch");
r = -EBADMSG;
goto fail;
}
if (JOURNAL_HEADER_CONTAINS(f->header, n_data) &&
n_data != le64toh(f->header->n_data)) {
error(offsetof(Header, n_data), "data number mismatch");
r = -EBADMSG;
goto fail;
}
if (JOURNAL_HEADER_CONTAINS(f->header, n_fields) &&
n_fields != le64toh(f->header->n_fields)) {
error(offsetof(Header, n_fields), "field number mismatch");
r = -EBADMSG;
goto fail;
}
if (JOURNAL_HEADER_CONTAINS(f->header, n_tags) &&
n_tags != le64toh(f->header->n_tags)) {
error(offsetof(Header, n_tags), "tag number mismatch");
r = -EBADMSG;
goto fail;
}
if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays) &&
n_entry_arrays != le64toh(f->header->n_entry_arrays)) {
error(offsetof(Header, n_entry_arrays), "entry array number mismatch");
r = -EBADMSG;
goto fail;
}
if (!found_main_entry_array && le64toh(f->header->entry_array_offset) != 0) {
error(0, "missing entry array");
r = -EBADMSG;
goto fail;
}
if (entry_seqnum_set &&
entry_seqnum != le64toh(f->header->tail_entry_seqnum)) {
error(offsetof(Header, tail_entry_seqnum), "invalid tail seqnum");
r = -EBADMSG;
goto fail;
}
if (entry_monotonic_set &&
(!sd_id128_equal(entry_boot_id, f->header->boot_id) ||
entry_monotonic != le64toh(f->header->tail_entry_monotonic))) {
error(0, "invalid tail monotonic timestamp");
r = -EBADMSG;
goto fail;
}
if (entry_realtime_set && entry_realtime != le64toh(f->header->tail_entry_realtime)) {
error(0, "invalid tail realtime timestamp");
r = -EBADMSG;
goto fail;
}
/* Second iteration: we follow all objects referenced from the
* two entry points: the object hash table and the entry
* array. We also check that everything referenced (directly
* or indirectly) in the data hash table also exists in the
* entry array, and vice versa. Note that we do not care for
* unreferenced objects. We only care that everything that is
* referenced is consistent. */
r = verify_entry_array(f,
data_fd, n_data,
entry_fd, n_entries,
entry_array_fd, n_entry_arrays,
&last_usec,
show_progress);
if (r < 0)
goto fail;
r = verify_hash_table(f,
data_fd, n_data,
entry_fd, n_entries,
entry_array_fd, n_entry_arrays,
&last_usec,
show_progress);
if (r < 0)
goto fail;
if (show_progress)
flush_progress();
mmap_cache_close_fd(f->mmap, data_fd);
mmap_cache_close_fd(f->mmap, entry_fd);
mmap_cache_close_fd(f->mmap, entry_array_fd);
safe_close(data_fd);
safe_close(entry_fd);
safe_close(entry_array_fd);
if (first_contained)
*first_contained = le64toh(f->header->head_entry_realtime);
if (last_validated)
*last_validated = last_sealed_realtime;
if (last_contained)
*last_contained = le64toh(f->header->tail_entry_realtime);
return 0;
fail:
if (show_progress)
flush_progress();
log_error("File corruption detected at %s:"OFSfmt" (of %llu bytes, %"PRIu64"%%).",
f->path,
p,
(unsigned long long) f->last_stat.st_size,
100 * p / f->last_stat.st_size);
if (data_fd >= 0) {
mmap_cache_close_fd(f->mmap, data_fd);
safe_close(data_fd);
}
if (entry_fd >= 0) {
mmap_cache_close_fd(f->mmap, entry_fd);
safe_close(entry_fd);
}
if (entry_array_fd >= 0) {
mmap_cache_close_fd(f->mmap, entry_array_fd);
safe_close(entry_array_fd);
}
return r;
}