Blob Blame History Raw
From 0983c262c2badca9f9bdcaa8aecc4560dd3589e3 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Fri, 24 Jul 2015 01:55:45 +0200
Subject: [PATCH] journal: avoid mapping empty data and field hash tables

When a new journal file is created we write the header first, then sync
and only then create the data and field hash tables in them. That means
to other processes it might appear that the files have a valid header
but not data and field hash tables. Our reader code should be able to
deal with this.

With this change we'll not map the two hash tables right-away after
opening a file for reading anymore (because that will of course fail if
the objects are missing), but delay this until the first time we access
them. On top of that, when we want to look something up in the hash
tables and we notice they aren't initialized yet, we consider them
empty.

This improves handling of some journal files reported in #487.

(cherry picked from commit dade37d403f1b8c1d7bb2efbe2361f2a3e999613)
---
 src/journal/journal-file.c   | 37 ++++++++++++++++++++++++++-----------
 src/journal/journal-file.h   |  3 +++
 src/journal/journal-verify.c | 14 ++++++++++++++
 3 files changed, 43 insertions(+), 11 deletions(-)

diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c
index f500568fec..6ccbb35a70 100644
--- a/src/journal/journal-file.c
+++ b/src/journal/journal-file.c
@@ -656,13 +656,16 @@ static int journal_file_setup_field_hash_table(JournalFile *f) {
         return 0;
 }
 
-static int journal_file_map_data_hash_table(JournalFile *f) {
+int journal_file_map_data_hash_table(JournalFile *f) {
         uint64_t s, p;
         void *t;
         int r;
 
         assert(f);
 
+        if (f->data_hash_table)
+                return 0;
+
         p = le64toh(f->header->data_hash_table_offset);
         s = le64toh(f->header->data_hash_table_size);
 
@@ -678,13 +681,16 @@ static int journal_file_map_data_hash_table(JournalFile *f) {
         return 0;
 }
 
-static int journal_file_map_field_hash_table(JournalFile *f) {
+int journal_file_map_field_hash_table(JournalFile *f) {
         uint64_t s, p;
         void *t;
         int r;
 
         assert(f);
 
+        if (f->field_hash_table)
+                return 0;
+
         p = le64toh(f->header->field_hash_table_offset);
         s = le64toh(f->header->field_hash_table_size);
 
@@ -803,10 +809,18 @@ int journal_file_find_field_object_with_hash(
         assert(f);
         assert(field && size > 0);
 
+        /* If the field hash table is empty, we can't find anything */
+        if (le64toh(f->header->field_hash_table_size) <= 0)
+                return 0;
+
+        /* Map the field hash table, if it isn't mapped yet. */
+        r = journal_file_map_field_hash_table(f);
+        if (r < 0)
+                return r;
+
         osize = offsetof(Object, field.payload) + size;
 
         m = le64toh(f->header->field_hash_table_size) / sizeof(HashItem);
-
         if (m <= 0)
                 return -EBADMSG;
 
@@ -866,6 +880,15 @@ int journal_file_find_data_object_with_hash(
         assert(f);
         assert(data || size == 0);
 
+        /* If there's no data hash table, then there's no entry. */
+        if (le64toh(f->header->data_hash_table_size) <= 0)
+                return 0;
+
+        /* Map the data hash table, if it isn't mapped yet. */
+        r = journal_file_map_data_hash_table(f);
+        if (r < 0)
+                return r;
+
         osize = offsetof(Object, data.payload) + size;
 
         m = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
@@ -2705,14 +2728,6 @@ int journal_file_open(
 #endif
         }
 
-        r = journal_file_map_field_hash_table(f);
-        if (r < 0)
-                goto fail;
-
-        r = journal_file_map_data_hash_table(f);
-        if (r < 0)
-                goto fail;
-
         if (mmap_cache_got_sigbus(f->mmap, f->fd)) {
                 r = -EIO;
                 goto fail;
diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h
index 403c8f760c..e92b75eabe 100644
--- a/src/journal/journal-file.h
+++ b/src/journal/journal-file.h
@@ -229,3 +229,6 @@ int journal_file_get_cutoff_realtime_usec(JournalFile *f, usec_t *from, usec_t *
 int journal_file_get_cutoff_monotonic_usec(JournalFile *f, sd_id128_t boot, usec_t *from, usec_t *to);
 
 bool journal_file_rotate_suggested(JournalFile *f, usec_t max_file_usec);
+
+int journal_file_map_data_hash_table(JournalFile *f);
+int journal_file_map_field_hash_table(JournalFile *f);
diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c
index 983217c1bc..d2d5c400c1 100644
--- a/src/journal/journal-verify.c
+++ b/src/journal/journal-verify.c
@@ -590,6 +590,13 @@ static int verify_hash_table(
         assert(last_usec);
 
         n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
+        if (n <= 0)
+                return 0;
+
+        r = journal_file_map_data_hash_table(f);
+        if (r < 0)
+                return log_error_errno(r, "Failed to map data hash table: %m");
+
         for (i = 0; i < n; i++) {
                 uint64_t last = 0, p;
 
@@ -647,6 +654,13 @@ static int data_object_in_hash_table(JournalFile *f, uint64_t hash, uint64_t p)
         assert(f);
 
         n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
+        if (n <= 0)
+                return 0;
+
+        r = journal_file_map_data_hash_table(f);
+        if (r < 0)
+                return log_error_errno(r, "Failed to map data hash table: %m");
+
         h = hash % n;
 
         q = le64toh(f->data_hash_table[h].head_hash_offset);