Blame SOURCES/0394-journalctl-don-t-trust-the-per-field-entry-tables-wh.patch

17b0f1
From cc5710c3ad0ff51fa84b736d66d5f70aa0ade2b3 Mon Sep 17 00:00:00 2001
17b0f1
From: Lennart Poettering <lennart@poettering.net>
17b0f1
Date: Mon, 25 Apr 2016 18:08:42 +0200
17b0f1
Subject: [PATCH] journalctl: don't trust the per-field entry tables when
17b0f1
 looking for boot IDs
17b0f1
17b0f1
When appending to a journal file, journald will:
17b0f1
17b0f1
a) first, append the actual entry to the end of the journal file
17b0f1
b) second, add an offset reference to it to the global entry array stored at
17b0f1
   the beginning of the file
17b0f1
c) third, add offset references to it to the per-field entry array stored at
17b0f1
   various places of the file
17b0f1
17b0f1
The global entry array, maintained by b) is used when iterating through the
17b0f1
journal without matches applied.
17b0f1
17b0f1
The per-field entry array maintained by c) is used when iterating through the
17b0f1
journal with a match for that specific field applied.
17b0f1
17b0f1
In the wild, there are journal files where a) and b) were completed, but c)
17b0f1
was not before the files were abandoned. This means, that in some cases log
17b0f1
entries are at the end of these files that appear in the global entry array,
17b0f1
but not in the per-field entry array of the _BOOT_ID= field. Now, the
17b0f1
"journalctl --list-boots" command alternatingly uses the global entry array
17b0f1
and the per-field entry array of the _BOOT_ID= field. It seeks to the last
17b0f1
entry of a specific _BOOT_ID=field by having the right match installed, and
17b0f1
then jumps to the next following entry with no match installed anymore, under
17b0f1
the assumption this would bring it to the next boot ID. However, if the
17b0f1
per-field entry wasn't written fully, it might actually turn out that the
17b0f1
global entry array might know one more entry with the same _BOOT_ID, thus
17b0f1
resulting in a indefinite loop around the same _BOOT_ID.
17b0f1
17b0f1
This patch fixes that, by updating the boot search logic to always continue
17b0f1
reading entries until the boot ID actually changed from the previous. Thus, the
17b0f1
per-field entry array is used as quick jump index (i.e. as an optimization),
17b0f1
but not trusted otherwise.  Only the global entry array is trusted.
17b0f1
17b0f1
This replaces PR #1904, which is actually very similar to this one. However,
17b0f1
this one actually reads the boot ID directly from the entry header, and doesn't
17b0f1
try to read it at all until the read pointer is actually really located on the
17b0f1
first item to read.
17b0f1
17b0f1
Fixes: #617
17b0f1
17b0f1
Replaces: #1904
17b0f1
17b0f1
Cherry-picked from: dc00966228ff90c554fd034e588ea55eb605ec52
17b0f1
Related: #1318994
17b0f1
---
17b0f1
 src/journal/journalctl.c | 71 ++++++++++++++++++++++++----------------
17b0f1
 1 file changed, 42 insertions(+), 29 deletions(-)
17b0f1
17b0f1
diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c
17b0f1
index 5864ff50a6..723854a2e9 100644
17b0f1
--- a/src/journal/journalctl.c
17b0f1
+++ b/src/journal/journalctl.c
17b0f1
@@ -941,18 +941,18 @@ static void boot_id_free_all(BootId *l) {
17b0f1
         }
17b0f1
 }
17b0f1
 
17b0f1
-static int discover_next_boot(
17b0f1
-                sd_journal *j,
17b0f1
-                BootId **boot,
17b0f1
+static int discover_next_boot(sd_journal *j,
17b0f1
+                sd_id128_t previous_boot_id,
17b0f1
                 bool advance_older,
17b0f1
-                bool read_realtime) {
17b0f1
+                BootId **ret) {
17b0f1
 
17b0f1
-        int r;
17b0f1
-        char match[9+32+1] = "_BOOT_ID=";
17b0f1
         _cleanup_free_ BootId *next_boot = NULL;
17b0f1
+        char match[9+32+1] = "_BOOT_ID=";
17b0f1
+        sd_id128_t boot_id;
17b0f1
+        int r;
17b0f1
 
17b0f1
         assert(j);
17b0f1
-        assert(boot);
17b0f1
+        assert(ret);
17b0f1
 
17b0f1
         /* We expect the journal to be on the last position of a boot
17b0f1
          * (in relation to the direction we are going), so that the next
17b0f1
@@ -965,29 +965,40 @@ static int discover_next_boot(
17b0f1
          * we can actually advance to a *different* boot. */
17b0f1
         sd_journal_flush_matches(j);
17b0f1
 
17b0f1
-        if (advance_older)
17b0f1
-                r = sd_journal_previous(j);
17b0f1
-        else
17b0f1
-                r = sd_journal_next(j);
17b0f1
-        if (r < 0)
17b0f1
-                return r;
17b0f1
-        else if (r == 0)
17b0f1
-                return 0; /* End of journal, yay. */
17b0f1
+        do {
17b0f1
+                if (advance_older)
17b0f1
+                        r = sd_journal_previous(j);
17b0f1
+                else
17b0f1
+                        r = sd_journal_next(j);
17b0f1
+                if (r < 0)
17b0f1
+                        return r;
17b0f1
+                else if (r == 0)
17b0f1
+                        return 0; /* End of journal, yay. */
17b0f1
+
17b0f1
+                r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
17b0f1
+                if (r < 0)
17b0f1
+                        return r;
17b0f1
+
17b0f1
+                /* We iterate through this in a loop, until the boot ID differs from the previous one. Note that
17b0f1
+                 * normally, this will only require a single iteration, as we seeked to the last entry of the previous
17b0f1
+                 * boot entry already. However, it might happen that the per-journal-field entry arrays are less
17b0f1
+                 * complete than the main entry array, and hence might reference an entry that's not actually the last
17b0f1
+                 * one of the boot ID as last one. Let's hence use the per-field array is initial seek position to
17b0f1
+                 * speed things up, but let's not trust that it is complete, and hence, manually advance as
17b0f1
+                 * necessary. */
17b0f1
+
17b0f1
+        } while (sd_id128_equal(boot_id, previous_boot_id));
17b0f1
 
17b0f1
         next_boot = new0(BootId, 1);
17b0f1
         if (!next_boot)
17b0f1
                 return log_oom();
17b0f1
 
17b0f1
-        r = sd_journal_get_monotonic_usec(j, NULL, &next_boot->id);
17b0f1
+        next_boot->id = boot_id;
17b0f1
+
17b0f1
+        r = sd_journal_get_realtime_usec(j, &next_boot->first);
17b0f1
         if (r < 0)
17b0f1
                 return r;
17b0f1
 
17b0f1
-        if (read_realtime) {
17b0f1
-                r = sd_journal_get_realtime_usec(j, &next_boot->first);
17b0f1
-                if (r < 0)
17b0f1
-                        return r;
17b0f1
-        }
17b0f1
-
17b0f1
         /* Now seek to the last occurrence of this boot ID. */
17b0f1
         sd_id128_to_string(next_boot->id, match + 9);
17b0f1
         r = sd_journal_add_match(j, match, sizeof(match) - 1);
17b0f1
@@ -1010,13 +1021,11 @@ static int discover_next_boot(
17b0f1
         else if (r == 0)
17b0f1
                 return -ENODATA; /* This shouldn't happen. We just came from this very boot ID. */
17b0f1
 
17b0f1
-        if (read_realtime) {
17b0f1
-                r = sd_journal_get_realtime_usec(j, &next_boot->last);
17b0f1
-                if (r < 0)
17b0f1
-                        return r;
17b0f1
-        }
17b0f1
+        r = sd_journal_get_realtime_usec(j, &next_boot->last);
17b0f1
+        if (r < 0)
17b0f1
+                return r;
17b0f1
 
17b0f1
-        *boot = next_boot;
17b0f1
+        *ret = next_boot;
17b0f1
         next_boot = NULL;
17b0f1
 
17b0f1
         return 0;
17b0f1
@@ -1032,6 +1041,7 @@ static int get_boots(
17b0f1
         int r, count = 0;
17b0f1
         BootId *head = NULL, *tail = NULL;
17b0f1
         const bool advance_older = query_ref_boot && ref_boot_offset <= 0;
17b0f1
+        sd_id128_t previous_boot_id;
17b0f1
 
17b0f1
         assert(j);
17b0f1
 
17b0f1
@@ -1085,10 +1095,11 @@ static int get_boots(
17b0f1
                 /* No sd_journal_next/previous here. */
17b0f1
         }
17b0f1
 
17b0f1
+        previous_boot_id = SD_ID128_NULL;
17b0f1
         for (;;) {
17b0f1
                 _cleanup_free_ BootId *current = NULL;
17b0f1
 
17b0f1
-                r = discover_next_boot(j, &current, advance_older, !query_ref_boot);
17b0f1
+                r = discover_next_boot(j, previous_boot_id, advance_older, ¤t;;
17b0f1
                 if (r < 0) {
17b0f1
                         boot_id_free_all(head);
17b0f1
                         return r;
17b0f1
@@ -1097,6 +1108,8 @@ static int get_boots(
17b0f1
                 if (!current)
17b0f1
                         break;
17b0f1
 
17b0f1
+                previous_boot_id = current->id;
17b0f1
+
17b0f1
                 if (query_ref_boot) {
17b0f1
                         if (!skip_once)
17b0f1
                                 ref_boot_offset += advance_older ? 1 : -1;