journal-verify.c revision bca9e39dfadaefc4b02c0dd378adc3d6221071de
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering This file is part of systemd.
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering Copyright 2012 Lennart 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 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 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/>.
b68fa010f72599e6da5822feda5ae3a47a4e63d8Simon Peetersstatic void draw_progress(uint64_t p, usec_t *last_usec) {
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering unsigned n, i, j, k;
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering if (x != 0 && x + 40 * USEC_PER_MSEC > z)
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering j = (n * (unsigned) p) / 65535ULL;
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering fputs("\r\x1B[?25l" ANSI_HIGHLIGHT_GREEN_ON, stdout);
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering for (i = 0; i < j; i++)
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering for (i = 0; i < k; i++)
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering printf(" %3"PRIu64"%%", 100U * p / 65535U);
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poetteringstatic uint64_t scale_progress(uint64_t scale, uint64_t p, uint64_t m) {
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering /* Calculates scale * p / m, but handles m == 0 safely, and saturates */
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering if (p >= m || m == 0)
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering return scale * p / m;
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poetteringstatic void flush_progress(void) {
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering unsigned n, i;
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering for (i = 0; i < n + 5; i++)
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering log_debug(OFSfmt": " _fmt, _offset, ##__VA_ARGS__); \
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering log_warning(OFSfmt": " _fmt, _offset, ##__VA_ARGS__); \
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering log_error(OFSfmt": " _fmt, (uint64_t)_offset, ##__VA_ARGS__); \
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poetteringstatic int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o) {
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. */
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering if ((o->object.flags & OBJECT_COMPRESSED_XZ) &&
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering error(offset, "Found compressed object that isn't of type DATA, which is not allowed.");
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering warning(offset, "unused data (entry_offset==0)");
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 if (le64toh(o->object.size) - offsetof(DataObject, payload) <= 0) {
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering error(offset, "bad object size (<= %zu): %"PRIu64,
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering compression = o->object.flags & OBJECT_COMPRESSION_MASK;
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering le64toh(o->object.size) - offsetof(Object, data.payload),
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering error(offset, "%s decompression failed: %s",
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering object_compressed_to_string(compression), strerror(-r));
3e09eb5c83e56bc0184bd9d9c44f76047464f77cAndreas Henriksson h2 = hash64(o->data.payload, le64toh(o->object.size) - offsetof(Object, data.payload));
ccd06097c79218f7d5ea4c21721bbcbc7c467dcaZbigniew Jędrzejewski-Szmek error(offset, "invalid hash (%08"PRIx64" vs. %08"PRIx64, h1, h2);
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering if (!VALID64(o->data.next_hash_offset) ||
ccd06097c79218f7d5ea4c21721bbcbc7c467dcaZbigniew Jędrzejewski-Szmek !VALID64(o->data.next_field_offset) ||
0bee65f0622c4faa8ac8ae771cc0c8a936dfa284Lennart Poettering error(offset, "invalid offset (next_hash_offset="OFSfmt", next_field_offset="OFSfmt", entry_offset="OFSfmt", entry_array_offset="OFSfmt,
0bee65f0622c4faa8ac8ae771cc0c8a936dfa284Lennart Poettering if (le64toh(o->object.size) - offsetof(FieldObject, payload) <= 0) {
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering if (!VALID64(o->field.next_hash_offset) ||
e1d758033dc7e101ab32323a0f1649d8daf56a22Ronny Chevalier "invalid offset (next_hash_offset="OFSfmt", head_data_offset="OFSfmt,
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering if ((le64toh(o->object.size) - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0) {
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering if ((le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0) {
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering "invalid number items in entry: %"PRIu64,
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering (le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem));
return -EBADMSG;
return -EBADMSG;
return -EBADMSG;
for (i = 0; i < journal_file_entry_n_items(o); i++) {
i, journal_file_entry_n_items(o),
return -EBADMSG;
case OBJECT_DATA_HASH_TABLE:
case OBJECT_FIELD_HASH_TABLE:
return -EBADMSG;
for (i = 0; i < journal_file_hash_table_n_items(o); i++) {
return -EBADMSG;
return -EBADMSG;
"invalid %s hash table item (%"PRIu64"/%"PRIu64"): head_hash_offset="OFSfmt" tail_hash_offset="OFSfmt,
return -EBADMSG;
case OBJECT_ENTRY_ARRAY:
return -EBADMSG;
return -EBADMSG;
for (i = 0; i < journal_file_entry_array_n_items(o); i++)
return -EBADMSG;
case OBJECT_TAG:
return -EBADMSG;
return -EBADMSG;
ssize_t k;
return -errno;
return -EIO;
uint64_t a, b;
assert(m);
uint64_t c, *z;
r = mmap_cache_get(m, fd, PROT_READ|PROT_WRITE, 0, false, c * sizeof(uint64_t), sizeof(uint64_t), NULL, (void **) &z);
static int entry_points_to_data(
JournalFile *f,
int entry_fd,
uint64_t i, n, a;
Object *o;
bool found = false;
assert(f);
return -EBADMSG;
n = journal_file_entry_n_items(o);
found = true;
if (!found) {
return -EBADMSG;
uint64_t m, u;
m = journal_file_entry_array_n_items(o);
u = MIN(n - i, m);
uint64_t x, y, z;
return -EBADMSG;
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);
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,
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) {
r = -errno;
goto fail;
if (entry_fd < 0) {
r = -errno;
goto fail;
if (entry_array_fd < 0) {
r = -errno;
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) {