From: Chris Down Date: Wed, 17 Jun 2026 13:12:28 +0000 (+0800) Subject: test-journal: Test bisecting reads when missing per-data entry array X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=104195939547df91e46da1ee34abda844452b5fb;p=thirdparty%2Fsystemd.git test-journal: Test bisecting reads when missing per-data entry array --- diff --git a/src/libsystemd/sd-journal/test-journal.c b/src/libsystemd/sd-journal/test-journal.c index 1e1ee37a568..95b1d0596cd 100644 --- a/src/libsystemd/sd-journal/test-journal.c +++ b/src/libsystemd/sd-journal/test-journal.c @@ -404,6 +404,92 @@ TEST(recover_truncated_linear) { test_recover_truncated_linear_one(/* zeroed_tail= */ true); } +static void test_recover_truncated_indexed_one(bool zeroed_tail) { + _cleanup_(mmap_cache_unrefp) MMapCache *m = NULL; + dual_timestamp ts; + JournalFile *f; + Object *o, *d; + EntryArrayCut cut; + uint64_t file_size; + static const char field[] = "FOO=bar"; + char t[] = "/var/tmp/journal-XXXXXX"; + + /* The same vague idea as above with the truncation, but this time it's the bisection case since the + * per-data entry array is missing. */ + + ASSERT_NOT_NULL(m = mmap_cache_new()); + mkdtemp_chdir_chattr(t); + + ASSERT_OK_ZERO(journal_file_open( + -EBADF, "test.journal", O_RDWR|O_CREAT, JOURNAL_COMPRESS, 0666, UINT64_MAX, + /* metrics= */ NULL, m, /* template= */ NULL, &f)); + dual_timestamp_now(&ts); + + for (unsigned i = 0; i < 200; i++) { + struct iovec iovec = IOVEC_MAKE_STRING(field); + ASSERT_OK_ZERO(journal_file_append_entry( + f, &ts, /* boot_id= */ NULL, &iovec, 1, + /* seqnum= */ NULL, /* seqnum_id= */ NULL, + /* ret_object= */ NULL, /* ret_offset= */ NULL)); + } + + ASSERT_EQ(journal_file_find_data_object(f, field, strlen(field), &d, /* ret_offset= */ NULL), 1); + cut = find_entry_array_cut(f, le64toh(d->data.entry_array_offset)); + file_size = (uint64_t) f->last_stat.st_size; + ASSERT_GT(file_size, cut.offset); + (void) journal_file_offline_close(f); + + /* Remove the final per-data array object. The data object's n_entries now overcounts the per-data + * arrays on disk */ + ASSERT_OK_ERRNO(truncate("test.journal", (int64_t) cut.offset)); + if (zeroed_tail) + ASSERT_OK_ERRNO(truncate("test.journal", (int64_t) file_size)); + + ASSERT_OK_ZERO(journal_file_open( + -EBADF, "test.journal", O_RDONLY, JOURNAL_COMPRESS, 0666, UINT64_MAX, + /* metrics= */ NULL, m, /* template= */ NULL, &f)); + ASSERT_EQ(journal_file_find_data_object(f, field, strlen(field), &d, /* ret_offset= */ NULL), 1); + + /* Seeking up for the largest possible seqnum walks into the missing tail array, and must fall back + * to the last entry that actually survived rather than failing the query. */ + ASSERT_EQ(journal_file_move_to_entry_by_seqnum_for_data( + f, d, UINT64_MAX, DIRECTION_UP, &o, /* ret_offset= */ NULL), 1); + ASSERT_EQ(le64toh(o->entry.seqnum), cut.last_surviving_seqnum); + ASSERT_LT(le64toh(o->entry.seqnum), UINT64_C(200)); + + /* Seeking down past everything that survived also walks into the missing array, and must report no + * match rather than propagating the read error to the caller. */ + ASSERT_OK_ZERO(journal_file_move_to_entry_by_seqnum_for_data( + f, d, UINT64_MAX, DIRECTION_DOWN, &o, /* ret_offset= */ NULL)); + + /* A seqnum that only existed in the lost region must degrade to no match, not a false hit (on an + * intact file this seqnum would have matched). */ + ASSERT_OK_ZERO(journal_file_move_to_entry_by_seqnum_for_data( + f, d, cut.last_surviving_seqnum + 1, DIRECTION_DOWN, &o, /* ret_offset= */ NULL)); + + /* The head of the chain is intact, so a downward seek from the start still finds the first entry. */ + ASSERT_EQ(journal_file_move_to_entry_by_seqnum_for_data( + f, d, 0, DIRECTION_DOWN, &o, /* ret_offset= */ NULL), 1); + ASSERT_EQ(le64toh(o->entry.seqnum), UINT64_C(1)); + + (void) journal_file_close(f); + + if (arg_keep) + log_info("Not removing %s", t); + else + ASSERT_OK(rm_rf(t, REMOVE_ROOT | REMOVE_PHYSICAL)); +} + +TEST(recover_truncated_indexed) { + ASSERT_OK_ERRNO(setenv("SYSTEMD_JOURNAL_COMPACT", "0", 1)); + test_recover_truncated_indexed_one(/* zeroed_tail= */ false); + test_recover_truncated_indexed_one(/* zeroed_tail= */ true); + + ASSERT_OK_ERRNO(setenv("SYSTEMD_JOURNAL_COMPACT", "1", 1)); + test_recover_truncated_indexed_one(/* zeroed_tail= */ false); + test_recover_truncated_indexed_one(/* zeroed_tail= */ true); +} + static int intro(void) { arg_keep = saved_argc > 1;