chiark / gitweb /
journal: replace contexts hashmap with a plain array
[elogind.git] / src / journal / journal-file.c
index f2f1f35fc3b64f4c9d7a6afb379654740ff4ecde..7cdaf2920b8c13d94892323bc3c98394facc7fc3 100644 (file)
 #include <sys/statvfs.h>
 #include <fcntl.h>
 #include <stddef.h>
-
-#ifdef HAVE_XATTR
-#include <attr/xattr.h>
-#endif
+#include <sys/xattr.h>
 
 #include "journal-def.h"
 #include "journal-file.h"
@@ -139,9 +136,9 @@ void journal_file_close(JournalFile *f) {
         if (f->mmap)
                 mmap_cache_unref(f->mmap);
 
-        hashmap_free_free(f->chain_cache);
+        ordered_hashmap_free_free(f->chain_cache);
 
-#ifdef HAVE_XZ
+#if defined(HAVE_XZ) || defined(HAVE_LZ4)
         free(f->compress_buffer);
 #endif
 
@@ -161,21 +158,21 @@ void journal_file_close(JournalFile *f) {
 }
 
 static int journal_file_init_header(JournalFile *f, JournalFile *template) {
-        Header h;
+        Header h = {};
         ssize_t k;
         int r;
 
         assert(f);
 
-        zero(h);
         memcpy(h.signature, HEADER_SIGNATURE, 8);
         h.header_size = htole64(ALIGN64(sizeof(h)));
 
-        h.incompatible_flags =
-                htole32(f->compress ? HEADER_INCOMPATIBLE_COMPRESSED : 0);
+        h.incompatible_flags |= htole32(
+                f->compress_xz * HEADER_INCOMPATIBLE_COMPRESSED_XZ |
+                f->compress_lz4 * HEADER_INCOMPATIBLE_COMPRESSED_LZ4);
 
-        h.compatible_flags =
-                htole32(f->seal ? HEADER_COMPATIBLE_SEALED : 0);
+        h.compatible_flags = htole32(
+                f->seal * HEADER_COMPATIBLE_SEALED);
 
         r = sd_id128_randomize(&h.file_id);
         if (r < 0)
@@ -225,6 +222,8 @@ static int journal_file_refresh_header(JournalFile *f) {
 }
 
 static int journal_file_verify_header(JournalFile *f) {
+        uint32_t flags;
+
         assert(f);
 
         if (memcmp(f->header->signature, HEADER_SIGNATURE, 8))
@@ -232,24 +231,30 @@ static int journal_file_verify_header(JournalFile *f) {
 
         /* In both read and write mode we refuse to open files with
          * incompatible flags we don't know */
-#ifdef HAVE_XZ
-        if ((le32toh(f->header->incompatible_flags) & ~HEADER_INCOMPATIBLE_COMPRESSED) != 0)
+        flags = le32toh(f->header->incompatible_flags);
+        if (flags & ~HEADER_INCOMPATIBLE_SUPPORTED) {
+                if (flags & ~HEADER_INCOMPATIBLE_ANY)
+                        log_debug("Journal file %s has unknown incompatible flags %"PRIx32,
+                                  f->path, flags & ~HEADER_INCOMPATIBLE_ANY);
+                flags = (flags & HEADER_INCOMPATIBLE_ANY) & ~HEADER_INCOMPATIBLE_SUPPORTED;
+                if (flags)
+                        log_debug("Journal file %s uses incompatible flags %"PRIx32
+                                  " disabled at compilation time.", f->path, flags);
                 return -EPROTONOSUPPORT;
-#else
-        if (f->header->incompatible_flags != 0)
-                return -EPROTONOSUPPORT;
-#endif
+        }
 
         /* When open for writing we refuse to open files with
          * compatible flags, too */
-        if (f->writable) {
-#ifdef HAVE_GCRYPT
-                if ((le32toh(f->header->compatible_flags) & ~HEADER_COMPATIBLE_SEALED) != 0)
-                        return -EPROTONOSUPPORT;
-#else
-                if (f->header->compatible_flags != 0)
-                        return -EPROTONOSUPPORT;
-#endif
+        flags = le32toh(f->header->compatible_flags);
+        if (f->writable && (flags & ~HEADER_COMPATIBLE_SUPPORTED)) {
+                if (flags & ~HEADER_COMPATIBLE_ANY)
+                        log_debug("Journal file %s has unknown compatible flags %"PRIx32,
+                                  f->path, flags & ~HEADER_COMPATIBLE_ANY);
+                flags = (flags & HEADER_COMPATIBLE_ANY) & ~HEADER_COMPATIBLE_SUPPORTED;
+                if (flags)
+                        log_debug("Journal file %s uses compatible flags %"PRIx32
+                                  " disabled at compilation time.", f->path, flags);
+                return -EPROTONOSUPPORT;
         }
 
         if (f->header->state >= _STATE_MAX)
@@ -274,12 +279,6 @@ static int journal_file_verify_header(JournalFile *f) {
             !VALID64(le64toh(f->header->entry_array_offset)))
                 return -ENODATA;
 
-        if (le64toh(f->header->data_hash_table_offset) < le64toh(f->header->header_size) ||
-            le64toh(f->header->field_hash_table_offset) < le64toh(f->header->header_size) ||
-            le64toh(f->header->tail_object_offset) < le64toh(f->header->header_size) ||
-            le64toh(f->header->entry_array_offset) < le64toh(f->header->header_size))
-                return -ENODATA;
-
         if (f->writable) {
                 uint8_t state;
                 sd_id128_t machine_id;
@@ -305,7 +304,8 @@ static int journal_file_verify_header(JournalFile *f) {
                 }
         }
 
-        f->compress = JOURNAL_HEADER_COMPRESSED(f->header);
+        f->compress_xz = JOURNAL_HEADER_COMPRESSED_XZ(f->header);
+        f->compress_lz4 = JOURNAL_HEADER_COMPRESSED_LZ4(f->header);
 
         f->seal = JOURNAL_HEADER_SEALED(f->header);
 
@@ -374,7 +374,13 @@ static int journal_file_allocate(JournalFile *f, uint64_t offset, uint64_t size)
         return 0;
 }
 
-static int journal_file_move_to(JournalFile *f, int context, bool keep_always, uint64_t offset, uint64_t size, void **ret) {
+static unsigned type_to_context(ObjectType type) {
+        /* One context for each type, plus one catch-all for the rest */
+        assert_cc(_OBJECT_TYPE_MAX <= MMAP_CACHE_MAX_CONTEXTS);
+        return type > OBJECT_UNUSED && type < _OBJECT_TYPE_MAX ? type : 0;
+}
+
+static int journal_file_move_to(JournalFile *f, ObjectType type, bool keep_always, uint64_t offset, uint64_t size, void **ret) {
         assert(f);
         assert(ret);
 
@@ -391,7 +397,7 @@ static int journal_file_move_to(JournalFile *f, int context, bool keep_always, u
                         return -EADDRNOTAVAIL;
         }
 
-        return mmap_cache_get(f->mmap, f->fd, f->prot, context, keep_always, offset, size, &f->last_stat, ret);
+        return mmap_cache_get(f->mmap, f->fd, f->prot, type_to_context(type), keep_always, offset, size, &f->last_stat, ret);
 }
 
 static uint64_t minimum_header_size(Object *o) {
@@ -412,7 +418,7 @@ static uint64_t minimum_header_size(Object *o) {
         return table[o->object.type];
 }
 
-int journal_file_move_to_object(JournalFile *f, int type, uint64_t offset, Object **ret) {
+int journal_file_move_to_object(JournalFile *f, ObjectType type, uint64_t offset, Object **ret) {
         int r;
         void *t;
         Object *o;
@@ -425,8 +431,7 @@ int journal_file_move_to_object(JournalFile *f, int type, uint64_t offset, Objec
         if (!VALID64(offset))
                 return -EFAULT;
 
-
-        r = journal_file_move_to(f, type_to_context(type), false, offset, sizeof(ObjectHeader), &t);
+        r = journal_file_move_to(f, type, false, offset, sizeof(ObjectHeader), &t);
         if (r < 0)
                 return r;
 
@@ -442,11 +447,11 @@ int journal_file_move_to_object(JournalFile *f, int type, uint64_t offset, Objec
         if (s < minimum_header_size(o))
                 return -EBADMSG;
 
-        if (type > 0 && o->object.type != type)
+        if (type > OBJECT_UNUSED && o->object.type != type)
                 return -EBADMSG;
 
         if (s > sizeof(ObjectHeader)) {
-                r = journal_file_move_to(f, o->object.type, false, offset, s, &t);
+                r = journal_file_move_to(f, type, false, offset, s, &t);
                 if (r < 0)
                         return r;
 
@@ -483,14 +488,14 @@ static uint64_t journal_file_entry_seqnum(JournalFile *f, uint64_t *seqnum) {
         return r;
 }
 
-int journal_file_append_object(JournalFile *f, int type, uint64_t size, Object **ret, uint64_t *offset) {
+int journal_file_append_object(JournalFile *f, ObjectType type, uint64_t size, Object **ret, uint64_t *offset) {
         int r;
         uint64_t p;
         Object *tail, *o;
         void *t;
 
         assert(f);
-        assert(type > 0 && type < _OBJECT_TYPE_MAX);
+        assert(type > OBJECT_UNUSED && type < _OBJECT_TYPE_MAX);
         assert(size >= sizeof(ObjectHeader));
         assert(offset);
         assert(ret);
@@ -503,7 +508,7 @@ int journal_file_append_object(JournalFile *f, int type, uint64_t size, Object *
         if (p == 0)
                 p = le64toh(f->header->header_size);
         else {
-                r = journal_file_move_to_object(f, -1, p, &tail);
+                r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &tail);
                 if (r < 0)
                         return r;
 
@@ -812,9 +817,10 @@ int journal_file_find_data_object_with_hash(
                 if (le64toh(o->data.hash) != hash)
                         goto next;
 
-                if (o->object.flags & OBJECT_COMPRESSED) {
-#ifdef HAVE_XZ
-                        uint64_t l, rsize;
+                if (o->object.flags & OBJECT_COMPRESSION_MASK) {
+#if defined(HAVE_XZ) || defined(HAVE_LZ4)
+                        uint64_t l;
+                        size_t rsize;
 
                         l = le64toh(o->object.size);
                         if (l <= offsetof(Object, data.payload))
@@ -822,8 +828,10 @@ int journal_file_find_data_object_with_hash(
 
                         l -= offsetof(Object, data.payload);
 
-                        if (!uncompress_blob(o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize, 0))
-                                return -EBADMSG;
+                        r = decompress_blob(o->object.flags & OBJECT_COMPRESSION_MASK,
+                                            o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize, 0);
+                        if (r < 0)
+                                return r;
 
                         if (rsize == size &&
                             memcmp(f->compress_buffer, data, size) == 0) {
@@ -839,7 +847,6 @@ int journal_file_find_data_object_with_hash(
 #else
                         return -EPROTONOSUPPORT;
 #endif
-
                 } else if (le64toh(o->object.size) == osize &&
                            memcmp(o->data.payload, data, size) == 0) {
 
@@ -946,8 +953,7 @@ static int journal_file_append_data(
         uint64_t hash, p;
         uint64_t osize;
         Object *o;
-        int r;
-        bool compressed = false;
+        int r, compression = 0;
         const void *eq;
 
         assert(f);
@@ -976,23 +982,24 @@ static int journal_file_append_data(
 
         o->data.hash = htole64(hash);
 
-#ifdef HAVE_XZ
-        if (f->compress &&
+#if defined(HAVE_XZ) || defined(HAVE_LZ4)
+        if (f->compress_xz &&
             size >= COMPRESSION_SIZE_THRESHOLD) {
-                uint64_t rsize;
+                size_t rsize;
 
-                compressed = compress_blob(data, size, o->data.payload, &rsize);
+                compression = compress_blob(data, size, o->data.payload, &rsize);
 
-                if (compressed) {
+                if (compression) {
                         o->object.size = htole64(offsetof(Object, data.payload) + rsize);
-                        o->object.flags |= OBJECT_COMPRESSED;
+                        o->object.flags |= compression;
 
-                        log_debug("Compressed data object %"PRIu64" -> %"PRIu64, size, rsize);
+                        log_debug("Compressed data object %"PRIu64" -> %zu using %s",
+                                  size, rsize, object_compressed_to_string(compression));
                 }
         }
 #endif
 
-        if (!compressed && size > 0)
+        if (!compression && size > 0)
                 memcpy(o->data.payload, data, size);
 
         r = journal_file_link_data(f, o, p, hash);
@@ -1291,7 +1298,7 @@ void journal_file_post_change(JournalFile *f) {
         __sync_synchronize();
 
         if (ftruncate(f->fd, f->last_stat.st_size) < 0)
-                log_error("Failed to truncate file to its own size: %m");
+                log_error_errno(errno, "Failed to truncate file to its own size: %m");
 }
 
 static int entry_item_cmp(const void *_a, const void *_b) {
@@ -1365,7 +1372,7 @@ typedef struct ChainCacheItem {
 } ChainCacheItem;
 
 static void chain_cache_put(
-                Hashmap *h,
+                OrderedHashmap *h,
                 ChainCacheItem *ci,
                 uint64_t first,
                 uint64_t array,
@@ -1379,9 +1386,10 @@ static void chain_cache_put(
                 if (array == first)
                         return;
 
-                if (hashmap_size(h) >= CHAIN_CACHE_MAX)
-                        ci = hashmap_steal_first(h);
-                else {
+                if (ordered_hashmap_size(h) >= CHAIN_CACHE_MAX) {
+                        ci = ordered_hashmap_steal_first(h);
+                        assert(ci);
+                } else {
                         ci = new(ChainCacheItem, 1);
                         if (!ci)
                                 return;
@@ -1389,7 +1397,7 @@ static void chain_cache_put(
 
                 ci->first = first;
 
-                if (hashmap_put(h, &ci->first, ci) < 0) {
+                if (ordered_hashmap_put(h, &ci->first, ci) < 0) {
                         free(ci);
                         return;
                 }
@@ -1418,7 +1426,7 @@ static int generic_array_get(
         a = first;
 
         /* Try the chain cache first */
-        ci = hashmap_get(f->chain_cache, &first);
+        ci = ordered_hashmap_get(f->chain_cache, &first);
         if (ci && i > ci->total) {
                 a = ci->array;
                 i -= ci->total;
@@ -1521,7 +1529,7 @@ static int generic_array_bisect(
         /* Start with the first array in the chain */
         a = first;
 
-        ci = hashmap_get(f->chain_cache, &first);
+        ci = ordered_hashmap_get(f->chain_cache, &first);
         if (ci && n > ci->total) {
                 /* Ah, we have iterated this bisection array chain
                  * previously! Let's see if we can skip ahead in the
@@ -1655,7 +1663,7 @@ static int generic_array_bisect(
                         }
                 }
 
-                if (k > n) {
+                if (k >= n) {
                         if (direction == DIRECTION_UP) {
                                 i = n;
                                 subtract_one = true;
@@ -2287,7 +2295,7 @@ void journal_file_dump(JournalFile *f) {
 
         p = le64toh(f->header->header_size);
         while (p != 0) {
-                r = journal_file_move_to_object(f, -1, p, &o);
+                r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &o);
                 if (r < 0)
                         goto fail;
 
@@ -2335,8 +2343,9 @@ void journal_file_dump(JournalFile *f) {
                         break;
                 }
 
-                if (o->object.flags & OBJECT_COMPRESSED)
-                        printf("Flags: COMPRESSED\n");
+                if (o->object.flags & OBJECT_COMPRESSION_MASK)
+                        printf("Flags: %s\n",
+                               object_compressed_to_string(o->object.flags & OBJECT_COMPRESSION_MASK));
 
                 if (p == le64toh(f->header->tail_object_offset))
                         p = 0;
@@ -2373,7 +2382,7 @@ void journal_file_print_header(JournalFile *f) {
                "Sequential Number ID: %s\n"
                "State: %s\n"
                "Compatible Flags:%s%s\n"
-               "Incompatible Flags:%s%s\n"
+               "Incompatible Flags:%s%s%s\n"
                "Header size: %"PRIu64"\n"
                "Arena size: %"PRIu64"\n"
                "Data Hash Table Size: %"PRIu64"\n"
@@ -2395,9 +2404,10 @@ void journal_file_print_header(JournalFile *f) {
                f->header->state == STATE_ONLINE ? "ONLINE" :
                f->header->state == STATE_ARCHIVED ? "ARCHIVED" : "UNKNOWN",
                JOURNAL_HEADER_SEALED(f->header) ? " SEALED" : "",
-               (le32toh(f->header->compatible_flags) & ~HEADER_COMPATIBLE_SEALED) ? " ???" : "",
-               JOURNAL_HEADER_COMPRESSED(f->header) ? " COMPRESSED" : "",
-               (le32toh(f->header->incompatible_flags) & ~HEADER_INCOMPATIBLE_COMPRESSED) ? " ???" : "",
+               (le32toh(f->header->compatible_flags) & ~HEADER_COMPATIBLE_ANY) ? " ???" : "",
+               JOURNAL_HEADER_COMPRESSED_XZ(f->header) ? " COMPRESSED-XZ" : "",
+               JOURNAL_HEADER_COMPRESSED_LZ4(f->header) ? " COMPRESSED-LZ4" : "",
+               (le32toh(f->header->incompatible_flags) & ~HEADER_INCOMPATIBLE_ANY) ? " ???" : "",
                le64toh(f->header->header_size),
                le64toh(f->header->arena_size),
                le64toh(f->header->data_hash_table_size) / sizeof(HashItem),
@@ -2470,8 +2480,10 @@ int journal_file_open(
         f->flags = flags;
         f->prot = prot_from_flags(flags);
         f->writable = (flags & O_ACCMODE) != O_RDONLY;
-#ifdef HAVE_XZ
-        f->compress = compress;
+#if defined(HAVE_LZ4)
+        f->compress_lz4 = compress;
+#elif defined(HAVE_XZ)
+        f->compress_xz = compress;
 #endif
 #ifdef HAVE_GCRYPT
         f->seal = seal;
@@ -2493,7 +2505,7 @@ int journal_file_open(
                 goto fail;
         }
 
-        f->chain_cache = hashmap_new(uint64_hash_func, uint64_compare_func);
+        f->chain_cache = ordered_hashmap_new(&uint64_hash_ops);
         if (!f->chain_cache) {
                 r = -ENOMEM;
                 goto fail;
@@ -2511,7 +2523,6 @@ int journal_file_open(
         }
 
         if (f->last_stat.st_size == 0 && f->writable) {
-#ifdef HAVE_XATTR
                 uint64_t crtime;
 
                 /* Let's attach the creation time to the journal file,
@@ -2522,11 +2533,10 @@ int journal_file_open(
                  * currently no usable API to query this, hence let's
                  * emulate this via extended attributes. If extended
                  * attributes are not supported we'll just skip this,
-                 * and rely solely on mtime/atime/ctime of the file.*/
+                 * and rely solely on mtime/atime/ctime of the file. */
 
                 crtime = htole64((uint64_t) now(CLOCK_REALTIME));
                 fsetxattr(f->fd, "user.crtime_usec", &crtime, sizeof(crtime), XATTR_CREATE);
-#endif
 
 #ifdef HAVE_GCRYPT
                 /* Try to load the FSPRG state, and if we can't, then
@@ -2765,12 +2775,14 @@ int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint6
                 if ((uint64_t) t != l)
                         return -E2BIG;
 
-                if (o->object.flags & OBJECT_COMPRESSED) {
-#ifdef HAVE_XZ
-                        uint64_t rsize;
+                if (o->object.flags & OBJECT_COMPRESSION_MASK) {
+#if defined(HAVE_XZ) || defined(HAVE_LZ4)
+                        size_t rsize;
 
-                        if (!uncompress_blob(o->data.payload, l, &from->compress_buffer, &from->compress_buffer_size, &rsize, 0))
-                                return -EBADMSG;
+                        r = decompress_blob(o->object.flags & OBJECT_COMPRESSION_MASK,
+                                            o->data.payload, l, &from->compress_buffer, &from->compress_buffer_size, &rsize, 0);
+                        if (r < 0)
+                                return r;
 
                         data = from->compress_buffer;
                         l = rsize;