journal: move several tests to libsystemd/sd-journal

This also renames test-journal-xyz for journald (rather than sd-journal)
to test-journald-xyz.
This commit is contained in:
Yu Watanabe
2023-10-02 10:52:45 +09:00
parent f7b349e88d
commit 1d3ef11dfe
10 changed files with 14 additions and 22 deletions
+2 -22
View File
@@ -103,11 +103,7 @@ executables += [
],
},
journal_test_template + {
'sources' : files('test-journal-append.c'),
'type' : 'manual',
},
journal_test_template + {
'sources' : files('test-journal-config.c'),
'sources' : files('test-journald-config.c'),
'dependencies' : [
liblz4,
libselinux,
@@ -115,16 +111,7 @@ executables += [
],
},
journal_test_template + {
'sources' : files('test-journal-flush.c'),
},
journal_test_template + {
'sources' : files('test-journal-interleaving.c'),
},
journal_test_template + {
'sources' : files('test-journal-stream.c'),
},
journal_test_template + {
'sources' : files('test-journal-syslog.c'),
'sources' : files('test-journald-syslog.c'),
'dependencies' : [
liblz4,
libselinux,
@@ -132,13 +119,6 @@ executables += [
threads,
],
},
journal_test_template + {
'sources' : files('test-journal-verify.c'),
'timeout' : 90,
},
journal_test_template + {
'sources' : files('test-journal.c'),
},
journal_fuzz_template + {
'sources' : files(
'fuzz-journald-audit.c',
-269
View File
@@ -1,269 +0,0 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <fcntl.h>
#include <getopt.h>
#include <stdio.h>
#include <unistd.h>
#include "chattr-util.h"
#include "fd-util.h"
#include "fs-util.h"
#include "io-util.h"
#include "journal-file-util.h"
#include "log.h"
#include "mmap-cache.h"
#include "parse-util.h"
#include "random-util.h"
#include "rm-rf.h"
#include "strv.h"
#include "terminal-util.h"
#include "tests.h"
#include "tmpfile-util.h"
static int journal_append_message(JournalFile *mj, const char *message) {
struct iovec iovec;
struct dual_timestamp ts;
assert(mj);
assert(message);
dual_timestamp_get(&ts);
iovec = IOVEC_MAKE_STRING(message);
return journal_file_append_entry(
mj,
&ts,
/* boot_id= */ NULL,
&iovec,
/* n_iovec= */ 1,
/* seqnum= */ NULL,
/* seqnum_id= */ NULL,
/* ret_object= */ NULL,
/* ret_offset= */ NULL);
}
static int journal_corrupt_and_append(uint64_t start_offset, uint64_t step) {
_cleanup_(mmap_cache_unrefp) MMapCache *mmap_cache = NULL;
_cleanup_(rm_rf_physical_and_freep) char *tempdir = NULL;
_cleanup_(journal_file_offline_closep) JournalFile *mj = NULL;
uint64_t start, end;
int r;
mmap_cache = mmap_cache_new();
assert_se(mmap_cache);
/* journal_file_open() requires a valid machine id */
if (sd_id128_get_machine(NULL) < 0)
return log_tests_skipped("No valid machine ID found");
assert_se(mkdtemp_malloc("/tmp/journal-append-XXXXXX", &tempdir) >= 0);
assert_se(chdir(tempdir) >= 0);
(void) chattr_path(tempdir, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
log_debug("Opening journal %s/system.journal", tempdir);
r = journal_file_open(
/* fd= */ -1,
"system.journal",
O_RDWR|O_CREAT,
JOURNAL_COMPRESS,
0644,
/* compress_threshold_bytes= */ UINT64_MAX,
/* metrics= */ NULL,
mmap_cache,
/* template= */ NULL,
&mj);
if (r < 0)
return log_error_errno(r, "Failed to open the journal: %m");
assert_se(mj);
/* Add a couple of initial messages */
for (int i = 0; i < 10; i++) {
_cleanup_free_ char *message = NULL;
assert_se(asprintf(&message, "MESSAGE=Initial message %d", i) >= 0);
r = journal_append_message(mj, message);
if (r < 0)
return log_error_errno(r, "Failed to write to the journal: %m");
}
start = start_offset == UINT64_MAX ? random_u64() % mj->last_stat.st_size : start_offset;
end = (uint64_t) mj->last_stat.st_size;
/* Print the initial offset at which we start flipping bits, which can be
* later used to reproduce a potential fail */
log_info("Start offset: %" PRIu64 ", corrupt-step: %" PRIu64, start, step);
fflush(stdout);
if (start >= end)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Start offset >= journal size, can't continue");
for (uint64_t offset = start; offset < end; offset += step) {
_cleanup_free_ char *message = NULL;
uint8_t b;
/* Flip a bit in the journal file */
r = pread(mj->fd, &b, 1, offset);
assert_se(r == 1);
b |= 0x1;
r = pwrite(mj->fd, &b, 1, offset);
assert_se(r == 1);
/* Close and reopen the journal to flush all caches and remap
* the corrupted journal */
mj = journal_file_offline_close(mj);
r = journal_file_open(
/* fd= */ -1,
"system.journal",
O_RDWR|O_CREAT,
JOURNAL_COMPRESS,
0644,
/* compress_threshold_bytes= */ UINT64_MAX,
/* metrics= */ NULL,
mmap_cache,
/* template= */ NULL,
&mj);
if (r < 0) {
/* The corrupted journal might get rejected during reopening
* if it's corrupted enough (especially its header), so
* treat this as a success if it doesn't crash */
log_info_errno(r, "Failed to reopen the journal: %m");
break;
}
/* Try to write something to the (possibly corrupted) journal */
assert_se(asprintf(&message, "MESSAGE=Hello world %" PRIu64, offset) >= 0);
r = journal_append_message(mj, message);
if (r < 0) {
/* We care only about crashes or sanitizer errors,
* failed write without any crash is a success */
log_info_errno(r, "Failed to write to the journal: %m");
break;
}
}
return 0;
}
int main(int argc, char *argv[]) {
uint64_t start_offset = UINT64_MAX;
uint64_t iterations = 100;
uint64_t iteration_step = 1;
uint64_t corrupt_step = 31;
bool sequential = false, run_one = false;
int c, r;
test_setup_logging(LOG_DEBUG);
enum {
ARG_START_OFFSET = 0x1000,
ARG_ITERATIONS,
ARG_ITERATION_STEP,
ARG_CORRUPT_STEP,
ARG_SEQUENTIAL,
ARG_RUN_ONE,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "start-offset", required_argument, NULL, ARG_START_OFFSET },
{ "iterations", required_argument, NULL, ARG_ITERATIONS },
{ "iteration-step", required_argument, NULL, ARG_ITERATION_STEP },
{ "corrupt-step", required_argument, NULL, ARG_CORRUPT_STEP },
{ "sequential", no_argument, NULL, ARG_SEQUENTIAL },
{ "run-one", required_argument, NULL, ARG_RUN_ONE },
{}
};
assert_se(argc >= 0);
assert_se(argv);
while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
switch (c) {
case 'h':
printf("Syntax:\n"
" %s [OPTION...]\n"
"Options:\n"
" --start-offset=OFFSET Offset at which to start corrupting the journal\n"
" (default: random offset is picked, unless\n"
" --sequential is used - in that case we use 0 + iteration)\n"
" --iterations=ITER Number of iterations to perform before exiting\n"
" (default: 100)\n"
" --iteration-step=STEP Iteration step (default: 1)\n"
" --corrupt-step=STEP Corrupt every n-th byte starting from OFFSET (default: 31)\n"
" --sequential Go through offsets sequentially instead of picking\n"
" a random one on each iteration. If set, we go through\n"
" offsets <0; ITER), or <OFFSET, ITER) if --start-offset=\n"
" is set (default: false)\n"
" --run-one=OFFSET Single shot mode for reproducing issues. Takes the same\n"
" offset as --start-offset= and does only one iteration\n"
, program_invocation_short_name);
return 0;
case ARG_START_OFFSET:
r = safe_atou64(optarg, &start_offset);
if (r < 0)
return log_error_errno(r, "Invalid starting offset: %m");
break;
case ARG_ITERATIONS:
r = safe_atou64(optarg, &iterations);
if (r < 0)
return log_error_errno(r, "Invalid value for iterations: %m");
break;
case ARG_CORRUPT_STEP:
r = safe_atou64(optarg, &corrupt_step);
if (r < 0)
return log_error_errno(r, "Invalid value for corrupt-step: %m");
break;
case ARG_ITERATION_STEP:
r = safe_atou64(optarg, &iteration_step);
if (r < 0)
return log_error_errno(r, "Invalid value for iteration-step: %m");
break;
case ARG_SEQUENTIAL:
sequential = true;
break;
case ARG_RUN_ONE:
r = safe_atou64(optarg, &start_offset);
if (r < 0)
return log_error_errno(r, "Invalid offset: %m");
run_one = true;
break;
case '?':
return -EINVAL;
default:
assert_not_reached();
}
if (run_one)
/* Reproducer mode */
return journal_corrupt_and_append(start_offset, corrupt_step);
for (uint64_t i = 0; i < iterations; i++) {
uint64_t offset = UINT64_MAX;
log_info("Iteration #%" PRIu64 ", step: %" PRIu64, i, iteration_step);
if (sequential)
offset = (start_offset == UINT64_MAX ? 0 : start_offset) + i * iteration_step;
r = journal_corrupt_and_append(offset, corrupt_step);
if (r < 0)
return EXIT_FAILURE;
if (r > 0)
/* Reached the end of the journal file */
break;
}
return EXIT_SUCCESS;
}
-82
View File
@@ -1,82 +0,0 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <fcntl.h>
#include <unistd.h>
#include "sd-journal.h"
#include "alloc-util.h"
#include "chattr-util.h"
#include "journal-file-util.h"
#include "journal-internal.h"
#include "macro.h"
#include "path-util.h"
#include "rm-rf.h"
#include "string-util.h"
#include "tmpfile-util.h"
static void test_journal_flush(int argc, char *argv[]) {
_cleanup_(mmap_cache_unrefp) MMapCache *m = NULL;
_cleanup_free_ char *fn = NULL;
_cleanup_(rm_rf_physical_and_freep) char *dn = NULL;
JournalFile *new_journal = NULL;
sd_journal *j = NULL;
unsigned n = 0;
int r;
assert_se(m = mmap_cache_new());
assert_se(mkdtemp_malloc("/var/tmp/test-journal-flush.XXXXXX", &dn) >= 0);
(void) chattr_path(dn, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
assert_se(fn = path_join(dn, "test.journal"));
r = journal_file_open(-1, fn, O_CREAT|O_RDWR, 0, 0644, 0, NULL, m, NULL, &new_journal);
assert_se(r >= 0);
if (argc > 1)
r = sd_journal_open_files(&j, (const char **) strv_skip(argv, 1), 0);
else
r = sd_journal_open(&j, 0);
assert_se(r == 0);
sd_journal_set_data_threshold(j, 0);
SD_JOURNAL_FOREACH(j) {
Object *o;
JournalFile *f;
f = j->current_file;
assert_se(f && f->current_offset > 0);
r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
if (r < 0)
log_error_errno(r, "journal_file_move_to_object failed: %m");
assert_se(r >= 0);
r = journal_file_copy_entry(f, new_journal, o, f->current_offset, NULL, NULL);
if (r < 0)
log_warning_errno(r, "journal_file_copy_entry failed: %m");
assert_se(r >= 0 ||
IN_SET(r, -EBADMSG, /* corrupted file */
-EPROTONOSUPPORT, /* unsupported compression */
-EIO, /* file rotated */
-EREMCHG)); /* clock rollback */
if (++n >= 10000)
break;
}
sd_journal_close(j);
(void) journal_file_offline_close(new_journal);
}
int main(int argc, char *argv[]) {
assert_se(setenv("SYSTEMD_JOURNAL_COMPACT", "0", 1) >= 0);
test_journal_flush(argc, argv);
assert_se(setenv("SYSTEMD_JOURNAL_COMPACT", "1", 1) >= 0);
test_journal_flush(argc, argv);
return 0;
}
-559
View File
@@ -1,559 +0,0 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <fcntl.h>
#include <unistd.h>
#include "sd-id128.h"
#include "sd-journal.h"
#include "alloc-util.h"
#include "chattr-util.h"
#include "io-util.h"
#include "journal-file-util.h"
#include "journal-vacuum.h"
#include "log.h"
#include "logs-show.h"
#include "parse-util.h"
#include "rm-rf.h"
#include "tests.h"
/* This program tests skipping around in a multi-file journal. */
static bool arg_keep = false;
static dual_timestamp previous_ts = {};
_noreturn_ static void log_assert_errno(const char *text, int error, const char *file, unsigned line, const char *func) {
log_internal(LOG_CRIT, error, file, line, func,
"'%s' failed at %s:%u (%s): %m", text, file, line, func);
abort();
}
#define assert_ret(expr) \
do { \
int _r_ = (expr); \
if (_unlikely_(_r_ < 0)) \
log_assert_errno(#expr, -_r_, PROJECT_FILE, __LINE__, __func__); \
} while (false)
static JournalFile *test_open_internal(const char *name, JournalFileFlags flags) {
_cleanup_(mmap_cache_unrefp) MMapCache *m = NULL;
JournalFile *f;
m = mmap_cache_new();
assert_se(m != NULL);
assert_ret(journal_file_open(-1, name, O_RDWR|O_CREAT, flags, 0644, UINT64_MAX, NULL, m, NULL, &f));
return f;
}
static JournalFile *test_open(const char *name) {
return test_open_internal(name, JOURNAL_COMPRESS);
}
static JournalFile *test_open_strict(const char *name) {
return test_open_internal(name, JOURNAL_COMPRESS | JOURNAL_STRICT_ORDER);
}
static void test_close(JournalFile *f) {
(void) journal_file_offline_close(f);
}
static void append_number(JournalFile *f, int n, const sd_id128_t *boot_id, uint64_t *seqnum) {
_cleanup_free_ char *p = NULL, *q = NULL;
dual_timestamp ts;
struct iovec iovec[2];
size_t n_iov = 0;
dual_timestamp_get(&ts);
if (ts.monotonic <= previous_ts.monotonic)
ts.monotonic = previous_ts.monotonic + 1;
if (ts.realtime <= previous_ts.realtime)
ts.realtime = previous_ts.realtime + 1;
previous_ts = ts;
assert_se(asprintf(&p, "NUMBER=%d", n) >= 0);
iovec[n_iov++] = IOVEC_MAKE_STRING(p);
if (boot_id) {
assert_se(q = strjoin("_BOOT_ID=", SD_ID128_TO_STRING(*boot_id)));
iovec[n_iov++] = IOVEC_MAKE_STRING(q);
}
assert_ret(journal_file_append_entry(f, &ts, boot_id, iovec, n_iov, seqnum, NULL, NULL, NULL));
}
static void append_unreferenced_data(JournalFile *f, const sd_id128_t *boot_id) {
_cleanup_free_ char *q = NULL;
dual_timestamp ts;
struct iovec iovec;
assert(boot_id);
ts.monotonic = usec_sub_unsigned(previous_ts.monotonic, 10);
ts.realtime = usec_sub_unsigned(previous_ts.realtime, 10);
assert_se(q = strjoin("_BOOT_ID=", SD_ID128_TO_STRING(*boot_id)));
iovec = IOVEC_MAKE_STRING(q);
assert_se(journal_file_append_entry(f, &ts, boot_id, &iovec, 1, NULL, NULL, NULL, NULL) == -EREMCHG);
}
static void test_check_number(sd_journal *j, int n) {
sd_id128_t boot_id;
const void *d;
_cleanup_free_ char *k = NULL;
size_t l;
int x;
assert_se(sd_journal_get_monotonic_usec(j, NULL, &boot_id) >= 0);
assert_ret(sd_journal_get_data(j, "NUMBER", &d, &l));
assert_se(k = strndup(d, l));
printf("%s %s (expected=%i)\n", SD_ID128_TO_STRING(boot_id), k, n);
assert_se(safe_atoi(k + STRLEN("NUMBER="), &x) >= 0);
assert_se(n == x);
}
static void test_check_numbers_down(sd_journal *j, int count) {
int i;
for (i = 1; i <= count; i++) {
int r;
test_check_number(j, i);
assert_ret(r = sd_journal_next(j));
if (i == count)
assert_se(r == 0);
else
assert_se(r == 1);
}
}
static void test_check_numbers_up(sd_journal *j, int count) {
for (int i = count; i >= 1; i--) {
int r;
test_check_number(j, i);
assert_ret(r = sd_journal_previous(j));
if (i == 1)
assert_se(r == 0);
else
assert_se(r == 1);
}
}
static void setup_sequential(void) {
JournalFile *f1, *f2, *f3;
sd_id128_t id;
f1 = test_open("one.journal");
f2 = test_open("two.journal");
f3 = test_open("three.journal");
assert_se(sd_id128_randomize(&id) >= 0);
log_info("boot_id: %s", SD_ID128_TO_STRING(id));
append_number(f1, 1, &id, NULL);
append_number(f1, 2, &id, NULL);
append_number(f1, 3, &id, NULL);
append_number(f2, 4, &id, NULL);
assert_se(sd_id128_randomize(&id) >= 0);
log_info("boot_id: %s", SD_ID128_TO_STRING(id));
append_number(f2, 5, &id, NULL);
append_number(f2, 6, &id, NULL);
append_number(f3, 7, &id, NULL);
append_number(f3, 8, &id, NULL);
assert_se(sd_id128_randomize(&id) >= 0);
log_info("boot_id: %s", SD_ID128_TO_STRING(id));
append_number(f3, 9, &id, NULL);
test_close(f1);
test_close(f2);
test_close(f3);
}
static void setup_interleaved(void) {
JournalFile *f1, *f2, *f3;
sd_id128_t id;
f1 = test_open("one.journal");
f2 = test_open("two.journal");
f3 = test_open("three.journal");
assert_se(sd_id128_randomize(&id) >= 0);
log_info("boot_id: %s", SD_ID128_TO_STRING(id));
append_number(f1, 1, &id, NULL);
append_number(f2, 2, &id, NULL);
append_number(f3, 3, &id, NULL);
append_number(f1, 4, &id, NULL);
append_number(f2, 5, &id, NULL);
append_number(f3, 6, &id, NULL);
append_number(f1, 7, &id, NULL);
append_number(f2, 8, &id, NULL);
append_number(f3, 9, &id, NULL);
test_close(f1);
test_close(f2);
test_close(f3);
}
static void setup_unreferenced_data(void) {
JournalFile *f1, *f2, *f3;
sd_id128_t id;
/* For issue #29275. */
f1 = test_open_strict("one.journal");
f2 = test_open_strict("two.journal");
f3 = test_open_strict("three.journal");
assert_se(sd_id128_randomize(&id) >= 0);
log_info("boot_id: %s", SD_ID128_TO_STRING(id));
append_number(f1, 1, &id, NULL);
append_number(f1, 2, &id, NULL);
append_number(f1, 3, &id, NULL);
assert_se(sd_id128_randomize(&id) >= 0);
log_info("boot_id: %s", SD_ID128_TO_STRING(id));
append_unreferenced_data(f1, &id);
append_number(f2, 4, &id, NULL);
append_number(f2, 5, &id, NULL);
append_number(f2, 6, &id, NULL);
assert_se(sd_id128_randomize(&id) >= 0);
log_info("boot_id: %s", SD_ID128_TO_STRING(id));
append_unreferenced_data(f2, &id);
append_number(f3, 7, &id, NULL);
append_number(f3, 8, &id, NULL);
append_number(f3, 9, &id, NULL);
test_close(f1);
test_close(f2);
test_close(f3);
}
static void mkdtemp_chdir_chattr(char *path) {
assert_se(mkdtemp(path));
assert_se(chdir(path) >= 0);
/* Speed up things a bit on btrfs, ensuring that CoW is turned off for all files created in our
* directory during the test run */
(void) chattr_path(path, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
}
static void test_skip_one(void (*setup)(void)) {
char t[] = "/var/tmp/journal-skip-XXXXXX";
sd_journal *j;
int r;
mkdtemp_chdir_chattr(t);
setup();
/* Seek to head, iterate down. */
assert_ret(sd_journal_open_directory(&j, t, 0));
assert_ret(sd_journal_seek_head(j));
assert_se(sd_journal_next(j) == 1); /* pointing to the first entry */
test_check_numbers_down(j, 9);
sd_journal_close(j);
/* Seek to head, iterate down. */
assert_ret(sd_journal_open_directory(&j, t, 0));
assert_ret(sd_journal_seek_head(j));
assert_se(sd_journal_next(j) == 1); /* pointing to the first entry */
assert_se(sd_journal_previous(j) == 0); /* no-op */
test_check_numbers_down(j, 9);
sd_journal_close(j);
/* Seek to head twice, iterate down. */
assert_ret(sd_journal_open_directory(&j, t, 0));
assert_ret(sd_journal_seek_head(j));
assert_se(sd_journal_next(j) == 1); /* pointing to the first entry */
assert_ret(sd_journal_seek_head(j));
assert_se(sd_journal_next(j) == 1); /* pointing to the first entry */
test_check_numbers_down(j, 9);
sd_journal_close(j);
/* Seek to head, move to previous, then iterate down. */
assert_ret(sd_journal_open_directory(&j, t, 0));
assert_ret(sd_journal_seek_head(j));
assert_se(sd_journal_previous(j) == 0); /* no-op */
assert_se(sd_journal_next(j) == 1); /* pointing to the first entry */
test_check_numbers_down(j, 9);
sd_journal_close(j);
/* Seek to head, walk several steps, then iterate down. */
assert_ret(sd_journal_open_directory(&j, t, 0));
assert_ret(sd_journal_seek_head(j));
assert_se(sd_journal_previous(j) == 0); /* no-op */
assert_se(sd_journal_previous(j) == 0); /* no-op */
assert_se(sd_journal_previous(j) == 0); /* no-op */
assert_se(sd_journal_next(j) == 1); /* pointing to the first entry */
assert_se(sd_journal_previous(j) == 0); /* no-op */
assert_se(sd_journal_previous(j) == 0); /* no-op */
test_check_numbers_down(j, 9);
sd_journal_close(j);
/* Seek to tail, iterate up. */
assert_ret(sd_journal_open_directory(&j, t, 0));
assert_ret(sd_journal_seek_tail(j));
assert_se(sd_journal_previous(j) == 1); /* pointing to the last entry */
test_check_numbers_up(j, 9);
sd_journal_close(j);
/* Seek to tail twice, iterate up. */
assert_ret(sd_journal_open_directory(&j, t, 0));
assert_ret(sd_journal_seek_tail(j));
assert_se(sd_journal_previous(j) == 1); /* pointing to the last entry */
assert_ret(sd_journal_seek_tail(j));
assert_se(sd_journal_previous(j) == 1); /* pointing to the last entry */
test_check_numbers_up(j, 9);
sd_journal_close(j);
/* Seek to tail, move to next, then iterate up. */
assert_ret(sd_journal_open_directory(&j, t, 0));
assert_ret(sd_journal_seek_tail(j));
assert_se(sd_journal_next(j) == 0); /* no-op */
assert_se(sd_journal_previous(j) == 1); /* pointing to the last entry */
test_check_numbers_up(j, 9);
sd_journal_close(j);
/* Seek to tail, walk several steps, then iterate up. */
assert_ret(sd_journal_open_directory(&j, t, 0));
assert_ret(sd_journal_seek_tail(j));
assert_se(sd_journal_next(j) == 0); /* no-op */
assert_se(sd_journal_next(j) == 0); /* no-op */
assert_se(sd_journal_next(j) == 0); /* no-op */
assert_se(sd_journal_previous(j) == 1); /* pointing to the last entry. */
assert_se(sd_journal_next(j) == 0); /* no-op */
assert_se(sd_journal_next(j) == 0); /* no-op */
test_check_numbers_up(j, 9);
sd_journal_close(j);
/* Seek to tail, skip to head, iterate down. */
assert_ret(sd_journal_open_directory(&j, t, 0));
assert_ret(sd_journal_seek_tail(j));
assert_se(sd_journal_previous_skip(j, 9) == 9); /* pointing to the first entry. */
test_check_numbers_down(j, 9);
sd_journal_close(j);
/* Seek to tail, skip to head in a more complex way, then iterate down. */
assert_ret(sd_journal_open_directory(&j, t, 0));
assert_ret(sd_journal_seek_tail(j));
assert_se(sd_journal_next(j) == 0);
assert_se(sd_journal_previous_skip(j, 4) == 4);
assert_se(sd_journal_previous_skip(j, 5) == 5);
assert_se(sd_journal_previous(j) == 0);
assert_se(sd_journal_previous_skip(j, 5) == 0);
assert_se(sd_journal_next(j) == 1);
assert_se(sd_journal_previous_skip(j, 5) == 1);
assert_se(sd_journal_next(j) == 1);
assert_se(sd_journal_next(j) == 1);
assert_se(sd_journal_previous(j) == 1);
assert_se(sd_journal_next(j) == 1);
assert_se(sd_journal_next(j) == 1);
assert_se(sd_journal_previous_skip(j, 5) == 3);
test_check_numbers_down(j, 9);
sd_journal_close(j);
/* Seek to head, skip to tail, iterate up. */
assert_ret(sd_journal_open_directory(&j, t, 0));
assert_ret(sd_journal_seek_head(j));
assert_se(sd_journal_next_skip(j, 9) == 9);
test_check_numbers_up(j, 9);
sd_journal_close(j);
/* Seek to head, skip to tail in a more complex way, then iterate up. */
assert_ret(sd_journal_open_directory(&j, t, 0));
assert_ret(sd_journal_seek_head(j));
assert_se(sd_journal_previous(j) == 0);
assert_se(sd_journal_next_skip(j, 4) == 4);
assert_se(sd_journal_next_skip(j, 5) == 5);
assert_se(sd_journal_next(j) == 0);
assert_se(sd_journal_next_skip(j, 5) == 0);
assert_se(sd_journal_previous(j) == 1);
assert_se(sd_journal_next_skip(j, 5) == 1);
assert_se(sd_journal_previous(j) == 1);
assert_se(sd_journal_previous(j) == 1);
assert_se(sd_journal_next(j) == 1);
assert_se(sd_journal_previous(j) == 1);
assert_se(sd_journal_previous(j) == 1);
assert_se(r = sd_journal_next_skip(j, 5) == 3);
test_check_numbers_up(j, 9);
sd_journal_close(j);
log_info("Done...");
if (arg_keep)
log_info("Not removing %s", t);
else {
journal_directory_vacuum(".", 3000000, 0, 0, NULL, true);
assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
}
puts("------------------------------------------------------------");
}
TEST(skip) {
test_skip_one(setup_sequential);
test_skip_one(setup_interleaved);
}
static void test_boot_id_one(void (*setup)(void), size_t n_boots_expected) {
char t[] = "/var/tmp/journal-boot-id-XXXXXX";
sd_journal *j;
_cleanup_free_ BootId *boots = NULL;
size_t n_boots;
mkdtemp_chdir_chattr(t);
setup();
assert_ret(sd_journal_open_directory(&j, t, 0));
assert_se(journal_get_boots(j, &boots, &n_boots) >= 0);
assert_se(boots);
assert_se(n_boots == n_boots_expected);
sd_journal_close(j);
FOREACH_ARRAY(b, boots, n_boots) {
assert_ret(sd_journal_open_directory(&j, t, 0));
assert_se(journal_find_boot_by_id(j, b->id) == 1);
sd_journal_close(j);
}
for (int i = - (int) n_boots + 1; i <= (int) n_boots; i++) {
sd_id128_t id;
assert_ret(sd_journal_open_directory(&j, t, 0));
assert_se(journal_find_boot_by_offset(j, i, &id) == 1);
if (i <= 0)
assert_se(sd_id128_equal(id, boots[n_boots + i - 1].id));
else
assert_se(sd_id128_equal(id, boots[i - 1].id));
sd_journal_close(j);
}
log_info("Done...");
if (arg_keep)
log_info("Not removing %s", t);
else {
journal_directory_vacuum(".", 3000000, 0, 0, NULL, true);
assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
}
puts("------------------------------------------------------------");
}
TEST(boot_id) {
test_boot_id_one(setup_sequential, 3);
test_boot_id_one(setup_unreferenced_data, 3);
}
static void test_sequence_numbers_one(void) {
_cleanup_(mmap_cache_unrefp) MMapCache *m = NULL;
char t[] = "/var/tmp/journal-seq-XXXXXX";
JournalFile *one, *two;
uint64_t seqnum = 0;
sd_id128_t seqnum_id;
m = mmap_cache_new();
assert_se(m != NULL);
mkdtemp_chdir_chattr(t);
assert_se(journal_file_open(-1, "one.journal", O_RDWR|O_CREAT, JOURNAL_COMPRESS, 0644,
UINT64_MAX, NULL, m, NULL, &one) == 0);
append_number(one, 1, NULL, &seqnum);
printf("seqnum=%"PRIu64"\n", seqnum);
assert_se(seqnum == 1);
append_number(one, 2, NULL, &seqnum);
printf("seqnum=%"PRIu64"\n", seqnum);
assert_se(seqnum == 2);
assert_se(one->header->state == STATE_ONLINE);
assert_se(!sd_id128_equal(one->header->file_id, one->header->machine_id));
assert_se(!sd_id128_equal(one->header->file_id, one->header->tail_entry_boot_id));
assert_se(sd_id128_equal(one->header->file_id, one->header->seqnum_id));
memcpy(&seqnum_id, &one->header->seqnum_id, sizeof(sd_id128_t));
assert_se(journal_file_open(-1, "two.journal", O_RDWR|O_CREAT, JOURNAL_COMPRESS, 0644,
UINT64_MAX, NULL, m, one, &two) == 0);
assert_se(two->header->state == STATE_ONLINE);
assert_se(!sd_id128_equal(two->header->file_id, one->header->file_id));
assert_se(sd_id128_equal(two->header->machine_id, one->header->machine_id));
assert_se(sd_id128_is_null(two->header->tail_entry_boot_id)); /* Not written yet. */
assert_se(sd_id128_equal(two->header->seqnum_id, one->header->seqnum_id));
append_number(two, 3, NULL, &seqnum);
printf("seqnum=%"PRIu64"\n", seqnum);
assert_se(seqnum == 3);
append_number(two, 4, NULL, &seqnum);
printf("seqnum=%"PRIu64"\n", seqnum);
assert_se(seqnum == 4);
/* Verify tail_entry_boot_id. */
assert_se(sd_id128_equal(two->header->tail_entry_boot_id, one->header->tail_entry_boot_id));
test_close(two);
append_number(one, 5, NULL, &seqnum);
printf("seqnum=%"PRIu64"\n", seqnum);
assert_se(seqnum == 5);
append_number(one, 6, NULL, &seqnum);
printf("seqnum=%"PRIu64"\n", seqnum);
assert_se(seqnum == 6);
test_close(one);
/* If the machine-id is not initialized, the header file verification
* (which happens when re-opening a journal file) will fail. */
if (sd_id128_get_machine(NULL) >= 0) {
/* restart server */
seqnum = 0;
assert_se(journal_file_open(-1, "two.journal", O_RDWR, JOURNAL_COMPRESS, 0,
UINT64_MAX, NULL, m, NULL, &two) == 0);
assert_se(sd_id128_equal(two->header->seqnum_id, seqnum_id));
append_number(two, 7, NULL, &seqnum);
printf("seqnum=%"PRIu64"\n", seqnum);
assert_se(seqnum == 5);
/* So..., here we have the same seqnum in two files with the
* same seqnum_id. */
test_close(two);
}
log_info("Done...");
if (arg_keep)
log_info("Not removing %s", t);
else {
journal_directory_vacuum(".", 3000000, 0, 0, NULL, true);
assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
}
}
TEST(sequence_numbers) {
assert_se(setenv("SYSTEMD_JOURNAL_COMPACT", "0", 1) >= 0);
test_sequence_numbers_one();
assert_se(setenv("SYSTEMD_JOURNAL_COMPACT", "1", 1) >= 0);
test_sequence_numbers_one();
}
static int intro(void) {
/* journal_file_open() requires a valid machine id */
if (access("/etc/machine-id", F_OK) != 0)
return log_tests_skipped("/etc/machine-id not found");
arg_keep = saved_argc > 1;
return EXIT_SUCCESS;
}
DEFINE_TEST_MAIN_WITH_INTRO(LOG_DEBUG, intro);
-201
View File
@@ -1,201 +0,0 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <fcntl.h>
#include <unistd.h>
#include "sd-journal.h"
#include "alloc-util.h"
#include "chattr-util.h"
#include "io-util.h"
#include "journal-file-util.h"
#include "journal-internal.h"
#include "log.h"
#include "macro.h"
#include "parse-util.h"
#include "rm-rf.h"
#include "tests.h"
#define N_ENTRIES 200
static void verify_contents(sd_journal *j, unsigned skip) {
unsigned i;
assert_se(j);
i = 0;
SD_JOURNAL_FOREACH(j) {
const void *d;
char *k, *c;
size_t l;
unsigned u = 0;
assert_se(sd_journal_get_cursor(j, &k) >= 0);
printf("cursor: %s\n", k);
free(k);
assert_se(sd_journal_get_data(j, "MAGIC", &d, &l) >= 0);
printf("\t%.*s\n", (int) l, (const char*) d);
assert_se(sd_journal_get_data(j, "NUMBER", &d, &l) >= 0);
assert_se(k = strndup(d, l));
printf("\t%s\n", k);
if (skip > 0) {
assert_se(safe_atou(k + 7, &u) >= 0);
assert_se(i == u);
i += skip;
}
free(k);
assert_se(sd_journal_get_cursor(j, &c) >= 0);
assert_se(sd_journal_test_cursor(j, c) > 0);
free(c);
}
if (skip > 0)
assert_se(i == N_ENTRIES);
}
static void run_test(void) {
_cleanup_(mmap_cache_unrefp) MMapCache *m = NULL;
JournalFile *one, *two, *three;
char t[] = "/var/tmp/journal-stream-XXXXXX";
unsigned i;
_cleanup_(sd_journal_closep) sd_journal *j = NULL;
char *z;
const void *data;
size_t l;
dual_timestamp previous_ts = DUAL_TIMESTAMP_NULL;
m = mmap_cache_new();
assert_se(m != NULL);
assert_se(mkdtemp(t));
assert_se(chdir(t) >= 0);
(void) chattr_path(t, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
assert_se(journal_file_open(-1, "one.journal", O_RDWR|O_CREAT, JOURNAL_COMPRESS, 0666, UINT64_MAX, NULL, m, NULL, &one) == 0);
assert_se(journal_file_open(-1, "two.journal", O_RDWR|O_CREAT, JOURNAL_COMPRESS, 0666, UINT64_MAX, NULL, m, NULL, &two) == 0);
assert_se(journal_file_open(-1, "three.journal", O_RDWR|O_CREAT, JOURNAL_COMPRESS, 0666, UINT64_MAX, NULL, m, NULL, &three) == 0);
for (i = 0; i < N_ENTRIES; i++) {
char *p, *q;
dual_timestamp ts;
struct iovec iovec[2];
dual_timestamp_get(&ts);
if (ts.monotonic <= previous_ts.monotonic)
ts.monotonic = previous_ts.monotonic + 1;
if (ts.realtime <= previous_ts.realtime)
ts.realtime = previous_ts.realtime + 1;
previous_ts = ts;
assert_se(asprintf(&p, "NUMBER=%u", i) >= 0);
iovec[0] = IOVEC_MAKE(p, strlen(p));
assert_se(asprintf(&q, "MAGIC=%s", i % 5 == 0 ? "quux" : "waldo") >= 0);
iovec[1] = IOVEC_MAKE(q, strlen(q));
if (i % 10 == 0)
assert_se(journal_file_append_entry(three, &ts, NULL, iovec, 2, NULL, NULL, NULL, NULL) == 0);
else {
if (i % 3 == 0)
assert_se(journal_file_append_entry(two, &ts, NULL, iovec, 2, NULL, NULL, NULL, NULL) == 0);
assert_se(journal_file_append_entry(one, &ts, NULL, iovec, 2, NULL, NULL, NULL, NULL) == 0);
}
free(p);
free(q);
}
(void) journal_file_offline_close(one);
(void) journal_file_offline_close(two);
(void) journal_file_offline_close(three);
assert_se(sd_journal_open_directory(&j, t, 0) >= 0);
assert_se(sd_journal_add_match(j, "MAGIC=quux", 0) >= 0);
SD_JOURNAL_FOREACH_BACKWARDS(j) {
_cleanup_free_ char *c;
assert_se(sd_journal_get_data(j, "NUMBER", &data, &l) >= 0);
printf("\t%.*s\n", (int) l, (const char*) data);
assert_se(sd_journal_get_cursor(j, &c) >= 0);
assert_se(sd_journal_test_cursor(j, c) > 0);
}
SD_JOURNAL_FOREACH(j) {
_cleanup_free_ char *c;
assert_se(sd_journal_get_data(j, "NUMBER", &data, &l) >= 0);
printf("\t%.*s\n", (int) l, (const char*) data);
assert_se(sd_journal_get_cursor(j, &c) >= 0);
assert_se(sd_journal_test_cursor(j, c) > 0);
}
sd_journal_flush_matches(j);
verify_contents(j, 1);
printf("NEXT TEST\n");
assert_se(sd_journal_add_match(j, "MAGIC=quux", 0) >= 0);
assert_se(z = journal_make_match_string(j));
printf("resulting match expression is: %s\n", z);
free(z);
verify_contents(j, 5);
printf("NEXT TEST\n");
sd_journal_flush_matches(j);
assert_se(sd_journal_add_match(j, "MAGIC=waldo", 0) >= 0);
assert_se(sd_journal_add_match(j, "NUMBER=10", 0) >= 0);
assert_se(sd_journal_add_match(j, "NUMBER=11", 0) >= 0);
assert_se(sd_journal_add_match(j, "NUMBER=12", 0) >= 0);
assert_se(z = journal_make_match_string(j));
printf("resulting match expression is: %s\n", z);
free(z);
verify_contents(j, 0);
assert_se(sd_journal_query_unique(j, "NUMBER") >= 0);
SD_JOURNAL_FOREACH_UNIQUE(j, data, l)
printf("%.*s\n", (int) l, (const char*) data);
assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
}
int main(int argc, char *argv[]) {
/* journal_file_open() requires a valid machine id */
if (access("/etc/machine-id", F_OK) != 0)
return log_tests_skipped("/etc/machine-id not found");
test_setup_logging(LOG_DEBUG);
/* Run this test multiple times with different configurations of features. */
assert_se(setenv("SYSTEMD_JOURNAL_KEYED_HASH", "0", 1) >= 0);
run_test();
assert_se(setenv("SYSTEMD_JOURNAL_KEYED_HASH", "1", 1) >= 0);
run_test();
assert_se(setenv("SYSTEMD_JOURNAL_COMPACT", "0", 1) >= 0);
run_test();
assert_se(setenv("SYSTEMD_JOURNAL_COMPACT", "1", 1) >= 0);
run_test();
return 0;
}
-210
View File
@@ -1,210 +0,0 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include "chattr-util.h"
#include "fd-util.h"
#include "io-util.h"
#include "journal-file-util.h"
#include "journal-verify.h"
#include "log.h"
#include "mmap-cache.h"
#include "rm-rf.h"
#include "strv.h"
#include "terminal-util.h"
#include "tests.h"
#define N_ENTRIES 6000
#define RANDOM_RANGE 77
static void bit_toggle(const char *fn, uint64_t p) {
uint8_t b;
ssize_t r;
int fd;
fd = open(fn, O_RDWR|O_CLOEXEC);
assert_se(fd >= 0);
r = pread(fd, &b, 1, p/8);
assert_se(r == 1);
b ^= 1 << (p % 8);
r = pwrite(fd, &b, 1, p/8);
assert_se(r == 1);
safe_close(fd);
}
static int raw_verify(const char *fn, const char *verification_key) {
_cleanup_(mmap_cache_unrefp) MMapCache *m = NULL;
JournalFile *f;
int r;
m = mmap_cache_new();
assert_se(m != NULL);
r = journal_file_open(
/* fd= */ -1,
fn,
O_RDONLY,
JOURNAL_COMPRESS|(verification_key ? JOURNAL_SEAL : 0),
0666,
/* compress_threshold_bytes= */ UINT64_MAX,
/* metrics= */ NULL,
m,
/* template= */ NULL,
&f);
if (r < 0)
return r;
r = journal_file_verify(f, verification_key, NULL, NULL, NULL, false);
(void) journal_file_close(f);
return r;
}
static int run_test(const char *verification_key, ssize_t max_iterations) {
_cleanup_(mmap_cache_unrefp) MMapCache *m = NULL;
char t[] = "/var/tmp/journal-XXXXXX";
struct stat st;
JournalFile *f;
JournalFile *df;
usec_t from = 0, to = 0, total = 0;
uint64_t start, end;
int r;
m = mmap_cache_new();
assert_se(m != NULL);
/* journal_file_open() requires a valid machine id */
if (sd_id128_get_machine(NULL) < 0)
return log_tests_skipped("No valid machine ID found");
test_setup_logging(LOG_DEBUG);
assert_se(mkdtemp(t));
assert_se(chdir(t) >= 0);
(void) chattr_path(t, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
log_info("Generating a test journal");
assert_se(journal_file_open(
/* fd= */ -1,
"test.journal",
O_RDWR|O_CREAT,
JOURNAL_COMPRESS|(verification_key ? JOURNAL_SEAL : 0),
0666,
/* compress_threshold_bytes= */ UINT64_MAX,
/* metrics= */ NULL,
m,
/* template= */ NULL,
&df) == 0);
for (size_t n = 0; n < N_ENTRIES; n++) {
_cleanup_free_ char *test = NULL;
struct iovec iovec;
struct dual_timestamp ts;
dual_timestamp_get(&ts);
assert_se(asprintf(&test, "RANDOM=%li", random() % RANDOM_RANGE));
iovec = IOVEC_MAKE_STRING(test);
assert_se(journal_file_append_entry(
df,
&ts,
/* boot_id= */ NULL,
&iovec,
/* n_iovec= */ 1,
/* seqnum= */ NULL,
/* seqnum_id= */ NULL,
/* ret_object= */ NULL,
/* ret_offset= */ NULL) == 0);
}
(void) journal_file_offline_close(df);
log_info("Verifying with key: %s", strna(verification_key));
assert_se(journal_file_open(
/* fd= */ -1,
"test.journal",
O_RDONLY,
JOURNAL_COMPRESS|(verification_key ? JOURNAL_SEAL : 0),
0666,
/* compress_threshold_bytes= */ UINT64_MAX,
/* metrics= */ NULL,
m,
/* template= */ NULL,
&f) == 0);
journal_file_print_header(f);
journal_file_dump(f);
assert_se(journal_file_verify(f, verification_key, &from, &to, &total, true) >= 0);
if (verification_key && JOURNAL_HEADER_SEALED(f->header))
log_info("=> Validated from %s to %s, %s missing",
FORMAT_TIMESTAMP(from),
FORMAT_TIMESTAMP(to),
FORMAT_TIMESPAN(total > to ? total - to : 0, 0));
(void) journal_file_close(f);
assert_se(stat("test.journal", &st) >= 0);
start = 38448 * 8 + 0;
end = max_iterations < 0 ? (uint64_t)st.st_size * 8 : start + max_iterations;
log_info("Toggling bits %"PRIu64 " to %"PRIu64, start, end);
for (uint64_t p = start; p < end; p++) {
bit_toggle("test.journal", p);
if (max_iterations < 0)
log_info("[ %"PRIu64"+%"PRIu64"]", p / 8, p % 8);
r = raw_verify("test.journal", verification_key);
/* Suppress the notice when running in the limited (CI) mode */
if (verification_key && max_iterations < 0 && r >= 0)
log_notice(ANSI_HIGHLIGHT_RED ">>>> %"PRIu64" (bit %"PRIu64") can be toggled without detection." ANSI_NORMAL, p / 8, p % 8);
bit_toggle("test.journal", p);
}
assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
return 0;
}
int main(int argc, char *argv[]) {
const char *verification_key = NULL;
int max_iterations = 512;
if (argc > 1) {
/* Don't limit the number of iterations when the verification key
* is provided on the command line, we want to do that only in CIs */
verification_key = argv[1];
max_iterations = -1;
}
assert_se(setenv("SYSTEMD_JOURNAL_COMPACT", "0", 1) >= 0);
run_test(verification_key, max_iterations);
assert_se(setenv("SYSTEMD_JOURNAL_COMPACT", "1", 1) >= 0);
run_test(verification_key, max_iterations);
#if HAVE_GCRYPT
/* If we're running without any arguments and we're compiled with gcrypt
* check the journal verification stuff with a valid key as well */
if (argc <= 1) {
verification_key = "c262bd-85187f-0b1b04-877cc5/1c7af8-35a4e900";
assert_se(setenv("SYSTEMD_JOURNAL_COMPACT", "0", 1) >= 0);
run_test(verification_key, max_iterations);
assert_se(setenv("SYSTEMD_JOURNAL_COMPACT", "1", 1) >= 0);
run_test(verification_key, max_iterations);
}
#endif
return 0;
}
-280
View File
@@ -1,280 +0,0 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <fcntl.h>
#include <unistd.h>
#include "chattr-util.h"
#include "io-util.h"
#include "journal-authenticate.h"
#include "journal-file-util.h"
#include "journal-vacuum.h"
#include "log.h"
#include "rm-rf.h"
#include "tests.h"
static bool arg_keep = false;
static void mkdtemp_chdir_chattr(char *path) {
assert_se(mkdtemp(path));
assert_se(chdir(path) >= 0);
/* Speed up things a bit on btrfs, ensuring that CoW is turned off for all files created in our
* directory during the test run */
(void) chattr_path(path, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
}
static void test_non_empty_one(void) {
_cleanup_(mmap_cache_unrefp) MMapCache *m = NULL;
dual_timestamp ts;
JournalFile *f;
struct iovec iovec;
static const char test[] = "TEST1=1", test2[] = "TEST2=2";
Object *o, *d;
uint64_t p;
sd_id128_t fake_boot_id;
char t[] = "/var/tmp/journal-XXXXXX";
m = mmap_cache_new();
assert_se(m != NULL);
mkdtemp_chdir_chattr(t);
assert_se(journal_file_open(-1, "test.journal", O_RDWR|O_CREAT, JOURNAL_COMPRESS|JOURNAL_SEAL, 0666, UINT64_MAX, NULL, m, NULL, &f) == 0);
assert_se(dual_timestamp_get(&ts));
assert_se(sd_id128_randomize(&fake_boot_id) == 0);
iovec = IOVEC_MAKE_STRING(test);
assert_se(journal_file_append_entry(f, &ts, NULL, &iovec, 1, NULL, NULL, NULL, NULL) == 0);
iovec = IOVEC_MAKE_STRING(test2);
assert_se(journal_file_append_entry(f, &ts, NULL, &iovec, 1, NULL, NULL, NULL, NULL) == 0);
iovec = IOVEC_MAKE_STRING(test);
assert_se(journal_file_append_entry(f, &ts, &fake_boot_id, &iovec, 1, NULL, NULL, NULL, NULL) == 0);
#if HAVE_GCRYPT
journal_file_append_tag(f);
#endif
journal_file_dump(f);
assert_se(journal_file_next_entry(f, 0, DIRECTION_DOWN, &o, &p) == 1);
assert_se(le64toh(o->entry.seqnum) == 1);
assert_se(journal_file_next_entry(f, p, DIRECTION_DOWN, &o, &p) == 1);
assert_se(le64toh(o->entry.seqnum) == 2);
assert_se(journal_file_next_entry(f, p, DIRECTION_DOWN, &o, &p) == 1);
assert_se(le64toh(o->entry.seqnum) == 3);
assert_se(sd_id128_equal(o->entry.boot_id, fake_boot_id));
assert_se(journal_file_next_entry(f, p, DIRECTION_DOWN, &o, &p) == 0);
assert_se(journal_file_next_entry(f, 0, DIRECTION_DOWN, &o, &p) == 1);
assert_se(le64toh(o->entry.seqnum) == 1);
assert_se(journal_file_find_data_object(f, test, strlen(test), &d, NULL) == 1);
assert_se(journal_file_move_to_entry_for_data(f, d, DIRECTION_DOWN, &o, NULL) == 1);
assert_se(le64toh(o->entry.seqnum) == 1);
assert_se(journal_file_move_to_entry_for_data(f, d, DIRECTION_UP, &o, NULL) == 1);
assert_se(le64toh(o->entry.seqnum) == 3);
assert_se(journal_file_find_data_object(f, test2, strlen(test2), &d, NULL) == 1);
assert_se(journal_file_move_to_entry_for_data(f, d, DIRECTION_UP, &o, NULL) == 1);
assert_se(le64toh(o->entry.seqnum) == 2);
assert_se(journal_file_move_to_entry_for_data(f, d, DIRECTION_DOWN, &o, NULL) == 1);
assert_se(le64toh(o->entry.seqnum) == 2);
assert_se(journal_file_find_data_object(f, "quux", 4, &d, NULL) == 0);
assert_se(journal_file_move_to_entry_by_seqnum(f, 1, DIRECTION_DOWN, &o, NULL) == 1);
assert_se(le64toh(o->entry.seqnum) == 1);
assert_se(journal_file_move_to_entry_by_seqnum(f, 3, DIRECTION_DOWN, &o, NULL) == 1);
assert_se(le64toh(o->entry.seqnum) == 3);
assert_se(journal_file_move_to_entry_by_seqnum(f, 2, DIRECTION_DOWN, &o, NULL) == 1);
assert_se(le64toh(o->entry.seqnum) == 2);
assert_se(journal_file_move_to_entry_by_seqnum(f, 10, DIRECTION_DOWN, &o, NULL) == 0);
journal_file_rotate(&f, m, JOURNAL_SEAL|JOURNAL_COMPRESS, UINT64_MAX, NULL);
journal_file_rotate(&f, m, JOURNAL_SEAL|JOURNAL_COMPRESS, UINT64_MAX, NULL);
(void) journal_file_offline_close(f);
log_info("Done...");
if (arg_keep)
log_info("Not removing %s", t);
else {
journal_directory_vacuum(".", 3000000, 0, 0, NULL, true);
assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
}
puts("------------------------------------------------------------");
}
TEST(non_empty) {
assert_se(setenv("SYSTEMD_JOURNAL_COMPACT", "0", 1) >= 0);
test_non_empty_one();
assert_se(setenv("SYSTEMD_JOURNAL_COMPACT", "1", 1) >= 0);
test_non_empty_one();
}
static void test_empty_one(void) {
_cleanup_(mmap_cache_unrefp) MMapCache *m = NULL;
JournalFile *f1, *f2, *f3, *f4;
char t[] = "/var/tmp/journal-XXXXXX";
m = mmap_cache_new();
assert_se(m != NULL);
mkdtemp_chdir_chattr(t);
assert_se(journal_file_open(-1, "test.journal", O_RDWR|O_CREAT, 0, 0666, UINT64_MAX, NULL, m, NULL, &f1) == 0);
assert_se(journal_file_open(-1, "test-compress.journal", O_RDWR|O_CREAT, JOURNAL_COMPRESS, 0666, UINT64_MAX, NULL, m, NULL, &f2) == 0);
assert_se(journal_file_open(-1, "test-seal.journal", O_RDWR|O_CREAT, JOURNAL_SEAL, 0666, UINT64_MAX, NULL, m, NULL, &f3) == 0);
assert_se(journal_file_open(-1, "test-seal-compress.journal", O_RDWR|O_CREAT, JOURNAL_COMPRESS|JOURNAL_SEAL, 0666, UINT64_MAX, NULL, m, NULL, &f4) == 0);
journal_file_print_header(f1);
puts("");
journal_file_print_header(f2);
puts("");
journal_file_print_header(f3);
puts("");
journal_file_print_header(f4);
puts("");
log_info("Done...");
if (arg_keep)
log_info("Not removing %s", t);
else {
journal_directory_vacuum(".", 3000000, 0, 0, NULL, true);
assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
}
(void) journal_file_offline_close(f1);
(void) journal_file_offline_close(f2);
(void) journal_file_offline_close(f3);
(void) journal_file_offline_close(f4);
}
TEST(empty) {
assert_se(setenv("SYSTEMD_JOURNAL_COMPACT", "0", 1) >= 0);
test_empty_one();
assert_se(setenv("SYSTEMD_JOURNAL_COMPACT", "1", 1) >= 0);
test_empty_one();
}
#if HAVE_COMPRESSION
static bool check_compressed(uint64_t compress_threshold, uint64_t data_size) {
_cleanup_(mmap_cache_unrefp) MMapCache *m = NULL;
dual_timestamp ts;
JournalFile *f;
struct iovec iovec;
Object *o;
uint64_t p;
char t[] = "/var/tmp/journal-XXXXXX";
char data[2048] = "FIELD=";
bool is_compressed;
int r;
assert_se(data_size <= sizeof(data));
m = mmap_cache_new();
assert_se(m != NULL);
mkdtemp_chdir_chattr(t);
assert_se(journal_file_open(-1, "test.journal", O_RDWR|O_CREAT, JOURNAL_COMPRESS|JOURNAL_SEAL, 0666, compress_threshold, NULL, m, NULL, &f) == 0);
dual_timestamp_get(&ts);
iovec = IOVEC_MAKE(data, data_size);
assert_se(journal_file_append_entry(f, &ts, NULL, &iovec, 1, NULL, NULL, NULL, NULL) == 0);
#if HAVE_GCRYPT
journal_file_append_tag(f);
#endif
journal_file_dump(f);
/* We have to partially reimplement some of the dump logic, because the normal next_entry does the
* decompression for us. */
p = le64toh(f->header->header_size);
for (;;) {
r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &o);
assert_se(r == 0);
if (o->object.type == OBJECT_DATA)
break;
assert_se(p < le64toh(f->header->tail_object_offset));
p = p + ALIGN64(le64toh(o->object.size));
}
is_compressed = COMPRESSION_FROM_OBJECT(o) != COMPRESSION_NONE;
(void) journal_file_offline_close(f);
log_info("Done...");
if (arg_keep)
log_info("Not removing %s", t);
else {
journal_directory_vacuum(".", 3000000, 0, 0, NULL, true);
assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
}
puts("------------------------------------------------------------");
return is_compressed;
}
static void test_min_compress_size_one(void) {
/* Note that XZ will actually fail to compress anything under 80 bytes, so you have to choose the limits
* carefully */
/* DEFAULT_MIN_COMPRESS_SIZE is 512 */
assert_se(!check_compressed(UINT64_MAX, 255));
assert_se(check_compressed(UINT64_MAX, 513));
/* compress everything */
assert_se(check_compressed(0, 96));
assert_se(check_compressed(8, 96));
/* Ensure we don't try to compress less than 8 bytes */
assert_se(!check_compressed(0, 7));
/* check boundary conditions */
assert_se(check_compressed(256, 256));
assert_se(!check_compressed(256, 255));
}
TEST(min_compress_size) {
assert_se(setenv("SYSTEMD_JOURNAL_COMPACT", "0", 1) >= 0);
test_min_compress_size_one();
assert_se(setenv("SYSTEMD_JOURNAL_COMPACT", "1", 1) >= 0);
test_min_compress_size_one();
}
#endif
static int intro(void) {
arg_keep = saved_argc > 1;
/* journal_file_open() requires a valid machine id */
if (access("/etc/machine-id", F_OK) != 0)
return log_tests_skipped("/etc/machine-id not found");
return EXIT_SUCCESS;
}
DEFINE_TEST_MAIN_WITH_INTRO(LOG_DEBUG, intro);