sd-journal: fix sd_journal_seek_monotonic_usec()

This fixes the following issues:

- We have a journal file, which contains entries of boot A and B. Let T
  be the timestamp of the _last_ entry of boot A.
  If sd_journal_seek_monotonic_usec() is called for boot A with a timestamp
  _after_ T, following sd_journal_next() will provide the _first_ entry of
  boot A, rather than the first entry of boot B.

- We have two journal files X and Y. The file X contains entries of boot A.
  Let T be the timestamp of the _last_ entry of boot A in file X. The file Y
  contains entries of boot A after timestamp T.
  If sd_journal_seek_monotonic_usec() is called for boot A with a
  timestamp _after_ T, following sd_journal_next() will provide the
  _first_ entry of boot A, whose timestamp is of course earlier than T.
This commit is contained in:
Yu Watanabe
2024-08-15 04:43:32 +09:00
parent a2e4b8d866
commit 9c04f67884
+84 -4
View File
@@ -766,6 +766,55 @@ static int next_for_match(
return 1;
}
static int move_by_boot_for_data(
sd_journal *j,
JournalFile *f,
direction_t direction,
sd_id128_t boot_id,
uint64_t data_offset,
Object **ret,
uint64_t *ret_offset) {
int r;
assert(j);
assert(f);
assert(IN_SET(direction, DIRECTION_DOWN, DIRECTION_UP));
for (;;) {
/* First, move to the last (or first when DIRECTION_UP) entry for the boot. */
uint64_t p = 0;
r = journal_file_move_to_entry_by_monotonic(f, boot_id,
direction == DIRECTION_DOWN ? USEC_INFINITY : 0,
direction == DIRECTION_DOWN ? DIRECTION_UP : DIRECTION_DOWN,
NULL, &p);
if (r <= 0)
return r;
/* Then, move to the first entry of the next boot (or the last entry of the previous boot with DIRECTION_UP). */
Object *entry;
r = journal_file_next_entry(f, p, direction, &entry, NULL);
if (r <= 0) /* r == 0 means that no next (or previous) boot found. That is, we are at HEAD or TAIL now. */
return r;
assert(entry->object.type == OBJECT_ENTRY);
boot_id = entry->entry.boot_id;
/* Note, this object cannot be reused, as journal_file_move_to_entry_by_monotonic() may invalidate the object. */
Object *data;
r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &data);
if (r < 0)
return r;
/* Then, move to the matching entry. */
r = journal_file_move_to_entry_by_monotonic_for_data(f, data, boot_id,
direction == DIRECTION_DOWN ? 0 : USEC_INFINITY, direction,
ret, ret_offset);
if (r != 0) /* Here r == 0 is OK, as that means the boot contains no entry matching with the data. */
return r;
}
}
static int find_location_for_match(
sd_journal *j,
Match *m,
@@ -793,8 +842,6 @@ static int find_location_for_match(
if (r <= 0)
return r;
/* FIXME: missing: find by monotonic */
if (j->current_location.type == LOCATION_HEAD)
return direction == DIRECTION_DOWN ? journal_file_move_to_entry_for_data(f, d, DIRECTION_DOWN, ret, offset) : 0;
if (j->current_location.type == LOCATION_TAIL)
@@ -810,10 +857,16 @@ static int find_location_for_match(
r = journal_file_move_to_object(f, OBJECT_DATA, dp, &d);
if (r < 0)
return r;
/* If not found, fall back to realtime if set, or go to the first entry of the next boot
* (or the last entry of the previous boot when DIRECTION_UP). */
}
if (j->current_location.realtime_set)
return journal_file_move_to_entry_by_realtime_for_data(f, d, j->current_location.realtime, direction, ret, offset);
if (j->current_location.monotonic_set)
return move_by_boot_for_data(j, f, direction, j->current_location.boot_id, dp, ret, offset);
return journal_file_move_to_entry_for_data(f, d, direction, ret, offset);
} else if (m->type == MATCH_OR_TERM) {
@@ -900,10 +953,28 @@ static int find_location_with_matches(
r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset);
if (r != 0)
return r;
/* If not found, fall back to realtime if set, or go to the first entry of the next boot
* (or the last entry of the previous boot when DIRECTION_UP). */
}
if (j->current_location.realtime_set)
return journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, ret, offset);
if (j->current_location.monotonic_set) {
uint64_t p = 0;
/* If not found in the above, first move to the last (or first when DIRECTION_UP) entry for the boot. */
r = journal_file_move_to_entry_by_monotonic(f, j->current_location.boot_id,
direction == DIRECTION_DOWN ? USEC_INFINITY : 0,
direction == DIRECTION_DOWN ? DIRECTION_UP : DIRECTION_DOWN,
NULL, &p);
if (r <= 0)
return r;
/* Then, move to the next or previous boot. */
return journal_file_next_entry(f, p, direction, ret, offset);
}
return journal_file_next_entry(f, 0, direction, ret, offset);
} else
return find_location_for_match(j, j->level0, f, direction, ret, offset);
@@ -967,9 +1038,18 @@ static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direc
journal_file_save_location(f, c, cp);
}
} else {
f->last_direction = direction;
r = find_location_with_matches(j, f, direction, &c, &cp);
/* LOCATION_SEEK specified to j->current_location.type here means that this is called first
* after sd_journal_seek_monotonic_usec() or friends was called. In that case, this file may
* not contain any matching entries with the user-specified location, but another file may
* contain them. If so, the second call of this function will use the seqnum, and we may find
* an entry in _this_ file with the seqnum. To prevent the second call of this function exits
* earlier by the first 'if' block of this function, do not save the direction if the current
* location is LOCATION_SEEK. */
if (r > 0 || j->current_location.type != LOCATION_SEEK)
f->last_direction = direction;
else
assert(f->last_direction == _DIRECTION_INVALID);
if (r <= 0)
return r;