journal-verify.c revision 76ef789d264f9eb7d7624b994aa6eead1dacfac4
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering This file is part of systemd.
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering Copyright 2012 Lennart Poettering
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering systemd is free software; you can redistribute it and/or modify it
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering under the terms of the GNU Lesser General Public License as published by
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering (at your option) any later version.
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering systemd is distributed in the hope that it will be useful, but
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering Lesser General Public License for more details.
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering You should have received a copy of the GNU Lesser General Public License
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstatic void draw_progress(uint64_t p, usec_t *last_usec) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering unsigned n, i, j, k;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (x != 0 && x + 40 * USEC_PER_MSEC > z)
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering j = (n * (unsigned) p) / 65535ULL;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering fputs("\r\x1B[?25l" ANSI_HIGHLIGHT_GREEN_ON, stdout);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering for (i = 0; i < j; i++)
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering for (i = 0; i < k; i++)
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering printf(" %3"PRIu64"%%", 100U * p / 65535U);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poetteringstatic uint64_t scale_progress(uint64_t scale, uint64_t p, uint64_t m) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering /* Calculates scale * p / m, but handles m == 0 safely, and saturates */
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (p >= m || m == 0)
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering return scale * p / m;
cc3773810855956bad92337cee8fa193584ab62eLennart Poetteringstatic void flush_progress(void) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering unsigned n, i;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering for (i = 0; i < n + 5; i++)
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering log_debug(OFSfmt": " _fmt, _offset, ##__VA_ARGS__); \
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering log_warning(OFSfmt": " _fmt, _offset, ##__VA_ARGS__); \
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering log_error(OFSfmt": " _fmt, (uint64_t)_offset, ##__VA_ARGS__); \
cc3773810855956bad92337cee8fa193584ab62eLennart Poetteringstatic int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering /* This does various superficial tests about the length an
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering * possible field values. It does not follow any references to
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering * other objects. */
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if ((o->object.flags & OBJECT_COMPRESSED_XZ) &&
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering error(offset, "Found compressed object that isn't of type DATA, which is not allowed.");
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering warning(offset, "Unused data (entry_offset==0)");
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if ((le64toh(o->data.entry_offset) == 0) ^ (le64toh(o->data.n_entries) == 0)) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering error(offset, "Bad n_entries: %"PRIu64, o->data.n_entries);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (le64toh(o->object.size) - offsetof(DataObject, payload) <= 0) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering error(offset, "Bad object size (<= %zu): %"PRIu64,
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering compression = o->object.flags & OBJECT_COMPRESSION_MASK;
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering le64toh(o->object.size) - offsetof(Object, data.payload),
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering error(offset, "%s decompression failed: %s",
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering object_compressed_to_string(compression), strerror(-r));
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering h2 = hash64(o->data.payload, le64toh(o->object.size) - offsetof(Object, data.payload));
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering error(offset, "Invalid hash (%08"PRIx64" vs. %08"PRIx64, h1, h2);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (!VALID64(o->data.next_hash_offset) ||
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering error(offset, "Invalid offset (next_hash_offset="OFSfmt", next_field_offset="OFSfmt", entry_offset="OFSfmt", entry_array_offset="OFSfmt,
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering if (le64toh(o->object.size) - offsetof(FieldObject, payload) <= 0) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (!VALID64(o->field.next_hash_offset) ||
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering "Invalid offset (next_hash_offset="OFSfmt", head_data_offset="OFSfmt,
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if ((le64toh(o->object.size) - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0) {
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering if ((le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0) {
a34faf579d2be139b0b9e8cd0c73ad4d918ef736Lukas Nykryn (le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem));
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (!VALID_REALTIME(le64toh(o->entry.realtime))) {
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering "Invalid entry realtime timestamp: %"PRIu64,
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (!VALID_MONOTONIC(le64toh(o->entry.monotonic))) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering "Invalid entry monotonic timestamp: %"PRIu64,
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering for (i = 0; i < journal_file_entry_n_items(o); i++) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (o->entry.items[i].object_offset == 0 ||
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering !VALID64(o->entry.items[i].object_offset)) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering "Invalid entry item (%"PRIu64"/%"PRIu64" offset: "OFSfmt,
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) % sizeof(HashItem) != 0 ||
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering (le64toh(o->object.size) - offsetof(HashTableObject, items)) / sizeof(HashItem) <= 0) {
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field",
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering for (i = 0; i < journal_file_hash_table_n_items(o); i++) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (o->hash_table.items[i].head_hash_offset != 0 &&
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering !VALID64(le64toh(o->hash_table.items[i].head_hash_offset))) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering "Invalid %s hash table item (%"PRIu64"/%"PRIu64") head_hash_offset: "OFSfmt,
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field",
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering le64toh(o->hash_table.items[i].head_hash_offset));
d2e54fae5ca7a0f71b5ac8b356a589ff0a09ea0aKay Sievers if (o->hash_table.items[i].tail_hash_offset != 0 &&
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering !VALID64(le64toh(o->hash_table.items[i].tail_hash_offset))) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering "Invalid %s hash table item (%"PRIu64"/%"PRIu64") tail_hash_offset: "OFSfmt,
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field",
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering le64toh(o->hash_table.items[i].tail_hash_offset));
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if ((o->hash_table.items[i].head_hash_offset != 0) !=
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering (o->hash_table.items[i].tail_hash_offset != 0)) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering "Invalid %s hash table item (%"PRIu64"/%"PRIu64"): head_hash_offset="OFSfmt" tail_hash_offset="OFSfmt,
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field",
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering le64toh(o->hash_table.items[i].head_hash_offset),
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering le64toh(o->hash_table.items[i].tail_hash_offset));
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) % sizeof(le64_t) != 0 ||
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering (le64toh(o->object.size) - offsetof(EntryArrayObject, items)) / sizeof(le64_t) <= 0) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering "Invalid object entry array size: %"PRIu64,
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (!VALID64(o->entry_array.next_entry_array_offset)) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering "Invalid object entry array next_entry_array_offset: "OFSfmt,
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering for (i = 0; i < journal_file_entry_array_n_items(o); i++)
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering if (le64toh(o->entry_array.items[i]) != 0 &&
cc3773810855956bad92337cee8fa193584ab62eLennart Poettering !VALID64(le64toh(o->entry_array.items[i]))) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering "Invalid object entry array item (%"PRIu64"/%"PRIu64"): "OFSfmt,
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (le64toh(o->object.size) != sizeof(TagObject)) {
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poetteringstatic int write_uint64(int fd, uint64_t p) {
2c4f86c1298f402220965682ab0e7729e150a562Lennart Poettering if (k != sizeof(p))
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poetteringstatic int contains_uint64(MMapCache *m, int fd, uint64_t n, uint64_t p) {
85a428c69465b047731b6abb5005f01824f1444eLennart Poettering /* Bisection ... */
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering while (a < b) {
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering c = (a + b) / 2;
c7b5eb98e8eeafe63a079ee3c51e9670872437aeLennart Poettering r = mmap_cache_get(m, fd, PROT_READ|PROT_WRITE, 0, false, c * sizeof(uint64_t), sizeof(uint64_t), NULL, (void **) &z);
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering if (a + 1 >= b)
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering if (!contains_uint64(f->mmap, entry_fd, n_entries, entry_p)) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering error(data_p, "Data object references invalid entry at "OFSfmt, entry_p);
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering r = journal_file_move_to_object(f, OBJECT_ENTRY, entry_p, &o);
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering for (i = 0; i < n; i++)
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering if (le64toh(o->entry.items[i].object_offset) == data_p) {
8e7fd6ade44ce5dde0867ba748c7978ed1206865Lennart Poettering error(entry_p, "Data object at "OFSfmt" not referenced by linked entry", data_p);
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering /* Check if this entry is also in main entry array. Since the
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering * main entry array has already been verified we can rely on
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering * its consistency. */
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering a = le64toh(f->header->entry_array_offset);
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering while (i < n) {
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
641906e9366891e0ad3e6e38b7396a427678c4cfThomas Hindoe Paaboel Andersen if (entry_p <= le64toh(o->entry_array.items[u-1])) {
8e7fd6ade44ce5dde0867ba748c7978ed1206865Lennart Poettering z = (x + y) / 2;
beaafb2ea6be591882aef21fe19b88e3b2461087Lennart Poettering if (le64toh(o->entry_array.items[z]) == entry_p)
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (x + 1 >= y)
f8e2fb7b14e53f5a4bcfd66d26910af1dee185c6Lennart Poettering if (entry_p < le64toh(o->entry_array.items[z]))
eecd1362f7f4de432483b5d77c56726c3621a83aLennart Poettering error(entry_p, "Entry object doesn't exist in main entry array");
static int verify_data(
JournalFile *f,
assert(f);
assert(o);
return -EBADMSG;
return -EBADMSG;
return -EBADMSG;
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);
r = journal_file_map_data_hash_table(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);
r = journal_file_map_data_hash_table(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, "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);
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 -EOPNOTSUPP;
} else if (f->seal)
return -ENOKEY;
if (data_fd < 0) {
goto fail;
if (entry_fd < 0) {
goto fail;
if (entry_array_fd < 0) {
goto fail;
r = -EOPNOTSUPP;
goto fail;
r = -EBADMSG;
goto fail;
if (show_progress)
goto fail;
r = -EBADMSG;
goto fail;
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 ++;
found_last = true;
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 (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) {