journal-verify.c revision e8c108ca9f11a382742f212f5b42a02536b3d40f
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen This file is part of systemd.
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen Copyright 2012 Lennart Poettering
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen systemd is free software; you can redistribute it and/or modify it
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen under the terms of the GNU Lesser General Public License as published by
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen the Free Software Foundation; either version 2.1 of the License, or
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen (at your option) any later version.
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen systemd is distributed in the hope that it will be useful, but
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen WITHOUT ANY WARRANTY; without even the implied warranty of
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen Lesser General Public License for more details.
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen You should have received a copy of the GNU Lesser General Public License
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen along with systemd; If not, see <http://www.gnu.org/licenses/>.
07630cea1f3a845c09309f197ac7c4f11edd3b62Lennart Poetteringstatic void draw_progress(uint64_t p, usec_t *last_usec) {
07630cea1f3a845c09309f197ac7c4f11edd3b62Lennart Poettering unsigned n, i, j, k;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen j = (n * (unsigned) p) / 65535ULL;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen fputs("\r\x1B[?25l" ANSI_HIGHLIGHT_GREEN_ON, stdout);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen for (i = 0; i < j; i++)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen for (i = 0; i < k; i++)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersenstatic void flush_progress(void) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen unsigned n, i;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen for (i = 0; i < n + 5; i++)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen log_debug(OFSfmt": " _fmt, _offset, ##__VA_ARGS__); \
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen log_warning(OFSfmt": " _fmt, _offset, ##__VA_ARGS__); \
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen log_error(OFSfmt": " _fmt, (uint64_t)_offset, ##__VA_ARGS__); \
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersenstatic int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen /* This does various superficial tests about the length an
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * possible field values. It does not follow any references to
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * other objects. */
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if ((o->object.flags & OBJECT_COMPRESSED_XZ) &&
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen warning(offset, "unused data (entry_offset==0)");
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if ((le64toh(o->data.entry_offset) == 0) ^ (le64toh(o->data.n_entries) == 0)) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen error(offset, "bad n_entries: %"PRIu64, o->data.n_entries);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (le64toh(o->object.size) - offsetof(DataObject, payload) <= 0) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen error(offset, "bad object size (<= %zu): %"PRIu64,
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen compression = o->object.flags & OBJECT_COMPRESSION_MASK;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen le64toh(o->object.size) - offsetof(Object, data.payload),
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen object_compressed_to_string(compression), strerror(-r));
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen h2 = hash64(o->data.payload, le64toh(o->object.size) - offsetof(Object, data.payload));
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen error(offset, "invalid hash (%08"PRIx64" vs. %08"PRIx64, h1, h2);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen error(offset, "invalid offset (next_hash_offset="OFSfmt", next_field_offset="OFSfmt", entry_offset="OFSfmt", entry_array_offset="OFSfmt,
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (le64toh(o->object.size) - offsetof(FieldObject, payload) <= 0) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen "invalid offset (next_hash_offset="OFSfmt", head_data_offset="OFSfmt,
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if ((le64toh(o->object.size) - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if ((le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen (le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem));
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (!VALID_REALTIME(le64toh(o->entry.realtime))) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (!VALID_MONOTONIC(le64toh(o->entry.monotonic))) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen for (i = 0; i < journal_file_entry_n_items(o); i++) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen "invalid entry item (%"PRIu64"/%"PRIu64" offset: "OFSfmt,
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) % sizeof(HashItem) != 0 ||
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen (le64toh(o->object.size) - offsetof(HashTableObject, items)) / sizeof(HashItem) <= 0) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field",
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen for (i = 0; i < journal_file_hash_table_n_items(o); i++) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (o->hash_table.items[i].head_hash_offset != 0 &&
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen !VALID64(le64toh(o->hash_table.items[i].head_hash_offset))) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen "invalid %s hash table item (%"PRIu64"/%"PRIu64") head_hash_offset: "OFSfmt,
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field",
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen le64toh(o->hash_table.items[i].head_hash_offset));
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (o->hash_table.items[i].tail_hash_offset != 0 &&
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen !VALID64(le64toh(o->hash_table.items[i].tail_hash_offset))) {
52efd56a6369e19c2400a42981a197cd2eef924aLennart Poettering "invalid %s hash table item (%"PRIu64"/%"PRIu64") tail_hash_offset: "OFSfmt,
52efd56a6369e19c2400a42981a197cd2eef924aLennart Poettering o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field",
52efd56a6369e19c2400a42981a197cd2eef924aLennart Poettering le64toh(o->hash_table.items[i].tail_hash_offset));
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if ((o->hash_table.items[i].head_hash_offset != 0) !=
4afd3348c7506dd1d36305b7bcb9feb8952b9d6bLennart Poettering (o->hash_table.items[i].tail_hash_offset != 0)) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen "invalid %s hash table item (%"PRIu64"/%"PRIu64"): head_hash_offset="OFSfmt" tail_hash_offset="OFSfmt,
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field",
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen le64toh(o->hash_table.items[i].head_hash_offset),
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen le64toh(o->hash_table.items[i].tail_hash_offset));
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) % sizeof(le64_t) != 0 ||
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen (le64toh(o->object.size) - offsetof(EntryArrayObject, items)) / sizeof(le64_t) <= 0) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (!VALID64(o->entry_array.next_entry_array_offset)) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen "invalid object entry array next_entry_array_offset: "OFSfmt,
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen for (i = 0; i < journal_file_entry_array_n_items(o); i++)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen "invalid object entry array item (%"PRIu64"/%"PRIu64"): "OFSfmt,
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (le64toh(o->object.size) != sizeof(TagObject)) {
1fa2f38f0f011010bf57522b42fcc168856a7003Zbigniew Jędrzejewski-Szmek "invalid object tag size: %"PRIu64,
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (k != sizeof(p))
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersenstatic int contains_uint64(MMapCache *m, int fd, uint64_t n, uint64_t p) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen /* Bisection ... */
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen a = 0; b = n;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen while (a < b) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen c = (a + b) / 2;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = mmap_cache_get(m, fd, PROT_READ|PROT_WRITE, 0, false, c * sizeof(uint64_t), sizeof(uint64_t), NULL, (void **) &z);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (a + 1 >= b)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen bool found = false;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (!contains_uint64(f->mmap, entry_fd, n_entries, entry_p)) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen "data object references invalid entry at "OFSfmt, entry_p);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = journal_file_move_to_object(f, OBJECT_ENTRY, entry_p, &o);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen for (i = 0; i < n; i++)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (le64toh(o->entry.items[i].object_offset) == data_p) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen "data object at "OFSfmt" not referenced by linked entry", data_p);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen /* Check if this entry is also in main entry array. Since the
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * main entry array has already been verified we can rely on
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen * its consistency.*/
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen while (i < n) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen u = MIN(n - i, m);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (entry_p <= le64toh(o->entry_array.items[u-1])) {
8927b1dad2d4a7330174cb924090b4635a2547fbDavid Herrmann while (x < y) {
8927b1dad2d4a7330174cb924090b4635a2547fbDavid Herrmann z = (x + y) / 2;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (le64toh(o->entry_array.items[z]) == entry_p)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (x + 1 >= y)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (entry_p < le64toh(o->entry_array.items[z]))
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen error(entry_p, "entry object doesn't exist in main entry array");
static int verify_data(
JournalFile *f,
assert(f);
assert(o);
error(p,
return -EBADMSG;
return -EBADMSG;
return -EBADMSG;
a, next);
return -EBADMSG;
m = journal_file_entry_array_n_items(o);
if (q <= last) {
return -EBADMSG;
last = q;
a = next;
static int verify_hash_table(
JournalFile *f,
bool show_progress) {
uint64_t i, n;
assert(f);
if (show_progress)
Object *o;
return -EBADMSG;
return -EBADMSG;
return -EBADMSG;
last = p;
p = next;
return -EBADMSG;
uint64_t n, h, q;
assert(f);
h = hash % n;
Object *o;
static int verify_entry(
JournalFile *f,
uint64_t i, n;
assert(f);
assert(o);
n = journal_file_entry_n_items(o);
uint64_t q, h;
Object *u;
return -EBADMSG;
return -EBADMSG;
r = data_object_in_hash_table(f, h, q);
return -EBADMSG;
static int verify_entry_array(
JournalFile *f,
bool show_progress) {
assert(f);
Object *o;
if (show_progress)
return -EBADMSG;
return -EBADMSG;
error(a,
i, n, next);
return -EBADMSG;
m = journal_file_entry_array_n_items(o);
uint64_t p;
if (p <= last) {
return -EBADMSG;
last = p;
return -EBADMSG;
a = next;
int journal_file_verify(
JournalFile *f,
const char *key,
bool show_progress) {
Object *o;
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;
bool found_last = false;
#ifdef HAVE_GCRYPT
assert(f);
if (key) {
#ifdef HAVE_GCRYPT
return -ENOTSUP;
} else if (f->seal)
return -ENOKEY;
if (data_fd < 0) {
r = -errno;
goto fail;
if (entry_fd < 0) {
r = -errno;
goto fail;
if (entry_array_fd < 0) {
r = -errno;
goto fail;
r = -ENOTSUP;
goto fail;
r = -EBADMSG;
goto fail;
if (show_progress)
goto fail;
r = -EBADMSG;
goto fail;
found_last = true;
n_objects ++;
r = journal_file_object_verify(f, p, o);
goto fail;
r = -EINVAL;
goto fail;
r = -EBADMSG;
goto fail;
r = -EBADMSG;
goto fail;
case OBJECT_DATA:
goto fail;
n_data++;
case OBJECT_FIELD:
n_fields++;
case OBJECT_ENTRY:
r = -EBADMSG;
goto fail;
goto fail;
r = -EBADMSG;
goto fail;
if (!entry_seqnum_set &&
r = -EBADMSG;
goto fail;
if (entry_seqnum_set &&
r = -EBADMSG;
goto fail;
entry_seqnum_set = true;
if (entry_monotonic_set &&
r = -EBADMSG;
goto fail;
entry_monotonic_set = true;
if (!entry_realtime_set &&
r = -EBADMSG;
goto fail;
entry_realtime_set = true;
n_entries ++;
case OBJECT_DATA_HASH_TABLE:
r = -EBADMSG;
goto fail;
le64toh(f->header->data_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
r = -EBADMSG;
goto fail;
case OBJECT_FIELD_HASH_TABLE:
r = -EBADMSG;
goto fail;
le64toh(f->header->field_hash_table_size) != le64toh(o->object.size) - offsetof(HashTableObject, items)) {
r = -EBADMSG;
goto fail;
case OBJECT_ENTRY_ARRAY:
goto fail;
if (found_main_entry_array) {
r = -EBADMSG;
goto fail;
found_main_entry_array = true;
case OBJECT_TAG:
r = -EBADMSG;
goto fail;
r = -EBADMSG;
goto fail;
r = -EBADMSG;
goto fail;
#ifdef HAVE_GCRYPT
if (f->seal) {
r = -EBADMSG;
goto fail;
goto fail;
r = journal_file_hmac_start(f);
goto fail;
if (last_tag == 0) {
r = journal_file_hmac_put_header(f);
goto fail;
q = last_tag;
goto fail;
goto fail;
goto fail;
r = -EBADMSG;
goto fail;
f->hmac_running = false;
n_tags ++;
n_weird ++;
if (!found_last) {
r = -EBADMSG;
goto fail;
r = -EBADMSG;
goto fail;
r = -EBADMSG;
goto fail;
r = -EBADMSG;
goto fail;
r = -EBADMSG;
goto fail;
r = -EBADMSG;
goto fail;
r = -EBADMSG;
goto fail;
r = -EBADMSG;
goto fail;
r = -EBADMSG;
goto fail;
if (!found_main_entry_array) {
r = -EBADMSG;
goto fail;
if (entry_seqnum_set &&
r = -EBADMSG;
goto fail;
if (entry_monotonic_set &&
r = -EBADMSG;
goto fail;
r = -EBADMSG;
goto fail;
r = verify_entry_array(f,
goto fail;
r = verify_hash_table(f,
goto fail;
if (show_progress)
if (first_contained)
if (last_validated)
if (last_contained)
fail:
if (show_progress)
f->path,
if (data_fd >= 0) {
if (entry_fd >= 0) {
if (entry_array_fd >= 0) {