sd-journal: prefer exact cursor match after seek (#43297)
Fixes #31516.
This commit is contained in:
@@ -1127,7 +1127,7 @@ static int compare_locations(sd_journal *j, JournalFile *af, JournalFile *bf) {
|
||||
}
|
||||
|
||||
static int real_journal_next(sd_journal *j, direction_t direction) {
|
||||
JournalFile *new_file = NULL;
|
||||
JournalFile *new_file = NULL, *exact_match = NULL;
|
||||
unsigned n_files;
|
||||
const void **files;
|
||||
Object *o;
|
||||
@@ -1163,8 +1163,23 @@ static int real_journal_next(sd_journal *j, direction_t direction) {
|
||||
|
||||
if (found)
|
||||
new_file = f;
|
||||
|
||||
/* Track the file that holds the cursor's exact entry (matching seqnum_id and seqnum). On
|
||||
* systems without a reliable (or missing) RTC, compare_boot_ids() can produce incorrect
|
||||
* cross-boot ordering causing compare_locations() above to prefer a wrong file. We detect
|
||||
* this after the loop and override the choice if needed.
|
||||
*
|
||||
* See https://github.com/systemd/systemd/issues/31516 */
|
||||
if (j->current_location.type == LOCATION_SEEK &&
|
||||
j->current_location.seqnum_set &&
|
||||
sd_id128_equal(f->header->seqnum_id, j->current_location.seqnum_id) &&
|
||||
f->current_seqnum == j->current_location.seqnum)
|
||||
exact_match = f;
|
||||
}
|
||||
|
||||
if (exact_match)
|
||||
new_file = exact_match;
|
||||
|
||||
if (!new_file)
|
||||
return 0;
|
||||
|
||||
|
||||
@@ -262,6 +262,18 @@ static void test_cursor(sd_journal *j) {
|
||||
ASSERT_OK(sd_journal_next(j));
|
||||
ASSERT_OK_POSITIVE(sd_journal_test_cursor(j, *c));
|
||||
}
|
||||
|
||||
STRV_FOREACH(c, cursors) {
|
||||
ASSERT_OK(sd_journal_seek_cursor(j, *c));
|
||||
ASSERT_OK(sd_journal_previous(j));
|
||||
ASSERT_OK_POSITIVE(sd_journal_test_cursor(j, *c));
|
||||
}
|
||||
|
||||
ASSERT_OK(sd_journal_seek_tail(j));
|
||||
STRV_FOREACH_BACKWARDS(c, cursors) {
|
||||
ASSERT_OK(sd_journal_previous(j));
|
||||
ASSERT_OK_POSITIVE(sd_journal_test_cursor(j, *c));
|
||||
}
|
||||
}
|
||||
|
||||
static void test_skip_one(void (*setup)(void)) {
|
||||
@@ -1328,6 +1340,84 @@ TEST(seek_time) {
|
||||
}
|
||||
}
|
||||
|
||||
static void append_number_at(
|
||||
JournalFile *f,
|
||||
unsigned n,
|
||||
const sd_id128_t *boot_id,
|
||||
uint64_t realtime,
|
||||
uint64_t monotonic) {
|
||||
|
||||
_cleanup_free_ char *p = NULL, *q = NULL;
|
||||
dual_timestamp ts = { .realtime = realtime, .monotonic = monotonic };
|
||||
struct iovec iovec[2];
|
||||
size_t n_iov = 0;
|
||||
|
||||
ASSERT_OK(asprintf(&p, "NUMBER=%u", n));
|
||||
iovec[n_iov++] = IOVEC_MAKE_STRING(p);
|
||||
|
||||
if (boot_id) {
|
||||
ASSERT_NOT_NULL((q = strjoin("_BOOT_ID=", SD_ID128_TO_STRING(*boot_id))));
|
||||
iovec[n_iov++] = IOVEC_MAKE_STRING(q);
|
||||
}
|
||||
|
||||
ASSERT_OK(journal_file_append_entry(f, &ts, boot_id, iovec, n_iov, NULL, NULL, NULL, NULL));
|
||||
}
|
||||
|
||||
TEST(cursor_broken_rtc) {
|
||||
/* Reproducer for https://github.com/systemd/systemd/issues/31516.
|
||||
*
|
||||
* On systems without a reliable (or missing) RTC, the realtime clock can go backwards across
|
||||
* reboots. This creates journal files where a later boot's first entries have lower realtime
|
||||
* timestamps than the previous boot's last entries.
|
||||
*
|
||||
* compare_boot_ids() orders boots by their newest realtime timestamp, so it considers boot 1
|
||||
* "earlier" than boot 2 (since boot 2 eventually catches up). But when seeking to a cursor pointing
|
||||
* at boot 2's first entry (which has low realtime), the realtime-based positioning in boot 1's file
|
||||
* finds a nearby entry, and compare_locations() (via compare_boot_ids()) incorrectly picks that
|
||||
* entry because boot 1 is globally "earlier".
|
||||
*
|
||||
* A minimal setup for such scenario - 2 files with 3 entries:
|
||||
*
|
||||
* one.journal (boot 1): entry at realtime=2s
|
||||
* two.journal (boot 2): entry at realtime=1s (earlier than boot 1), then entry at realtime=3s */
|
||||
_cleanup_(test_donep) char *t = NULL;
|
||||
_cleanup_(sd_journal_closep) sd_journal *j = NULL;
|
||||
|
||||
mkdtemp_chdir_chattr("/var/tmp/journal-cursor-rtc-XXXXXX", &t);
|
||||
|
||||
{
|
||||
_cleanup_(journal_file_offline_closep) JournalFile *f1 = NULL, *f2 = NULL;
|
||||
sd_id128_t boot1, boot2;
|
||||
|
||||
f1 = test_open("one.journal");
|
||||
f2 = test_open("two.journal");
|
||||
|
||||
ASSERT_OK(sd_id128_randomize(&boot1));
|
||||
ASSERT_OK(sd_id128_randomize(&boot2));
|
||||
|
||||
log_info("boot1: %s, boot2: %s",
|
||||
SD_ID128_TO_STRING(boot1), SD_ID128_TO_STRING(boot2));
|
||||
|
||||
append_number_at(f1, 1, &boot1, 2 * USEC_PER_SEC, 100 * USEC_PER_MSEC);
|
||||
append_number_at(f2, 2, &boot2, 1 * USEC_PER_SEC, 100 * USEC_PER_MSEC);
|
||||
append_number_at(f2, 3, &boot2, 3 * USEC_PER_SEC, 200 * USEC_PER_MSEC);
|
||||
}
|
||||
|
||||
ASSERT_OK(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
|
||||
|
||||
/* Test cursor seeking */
|
||||
test_cursor(j);
|
||||
|
||||
/* Also verify forward and backward iterations */
|
||||
ASSERT_OK(sd_journal_seek_head(j));
|
||||
ASSERT_OK_POSITIVE(sd_journal_next(j));
|
||||
test_check_numbers_down(j, 3);
|
||||
|
||||
ASSERT_OK(sd_journal_seek_tail(j));
|
||||
ASSERT_OK_POSITIVE(sd_journal_previous(j));
|
||||
test_check_numbers_up(j, 3);
|
||||
}
|
||||
|
||||
static int intro(void) {
|
||||
/* journal_file_open() requires a valid machine id */
|
||||
if (access("/etc/machine-id", F_OK) != 0)
|
||||
|
||||
Reference in New Issue
Block a user