1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2011 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
26 #include <sys/statvfs.h>
30 #include "journal-def.h"
31 #include "journal-file.h"
32 #include "journal-authenticate.h"
37 #define DEFAULT_DATA_HASH_TABLE_SIZE (2047ULL*sizeof(HashItem))
38 #define DEFAULT_FIELD_HASH_TABLE_SIZE (333ULL*sizeof(HashItem))
40 #define COMPRESSION_SIZE_THRESHOLD (512ULL)
42 /* This is the minimum journal file size */
43 #define JOURNAL_FILE_SIZE_MIN (64ULL*1024ULL) /* 64 KiB */
45 /* These are the lower and upper bounds if we deduce the max_use value
46 * from the file system size */
47 #define DEFAULT_MAX_USE_LOWER (1ULL*1024ULL*1024ULL) /* 1 MiB */
48 #define DEFAULT_MAX_USE_UPPER (4ULL*1024ULL*1024ULL*1024ULL) /* 4 GiB */
50 /* This is the upper bound if we deduce max_size from max_use */
51 #define DEFAULT_MAX_SIZE_UPPER (128ULL*1024ULL*1024ULL) /* 128 MiB */
53 /* This is the upper bound if we deduce the keep_free value from the
55 #define DEFAULT_KEEP_FREE_UPPER (4ULL*1024ULL*1024ULL*1024ULL) /* 4 GiB */
57 /* This is the keep_free value when we can't determine the system
59 #define DEFAULT_KEEP_FREE (1024ULL*1024ULL) /* 1 MB */
61 /* n_data was the first entry we added after the initial file format design */
62 #define HEADER_SIZE_MIN ALIGN64(offsetof(Header, n_data))
64 void journal_file_close(JournalFile *f) {
67 /* Write the final tag */
69 journal_file_append_tag(f);
71 /* Sync everything to disk, before we mark the file offline */
72 if (f->mmap && f->fd >= 0)
73 mmap_cache_close_fd(f->mmap, f->fd);
75 if (f->writable && f->fd >= 0)
79 /* Mark the file offline. Don't override the archived state if it already is set */
80 if (f->writable && f->header->state == STATE_ONLINE)
81 f->header->state = STATE_OFFLINE;
83 munmap(f->header, PAGE_ALIGN(sizeof(Header)));
87 close_nointr_nofail(f->fd);
92 mmap_cache_unref(f->mmap);
95 free(f->compress_buffer);
100 munmap(f->fsprg_header, PAGE_ALIGN(f->fsprg_size));
103 gcry_md_close(f->hmac);
109 static int journal_file_init_header(JournalFile *f, JournalFile *template) {
117 memcpy(h.signature, HEADER_SIGNATURE, 8);
118 h.header_size = htole64(ALIGN64(sizeof(h)));
120 h.incompatible_flags =
121 htole32(f->compress ? HEADER_INCOMPATIBLE_COMPRESSED : 0);
124 htole32(f->authenticate ? HEADER_COMPATIBLE_AUTHENTICATED : 0);
126 r = sd_id128_randomize(&h.file_id);
131 h.seqnum_id = template->header->seqnum_id;
132 h.tail_entry_seqnum = template->header->tail_entry_seqnum;
134 h.seqnum_id = h.file_id;
136 k = pwrite(f->fd, &h, sizeof(h), 0);
146 static int journal_file_refresh_header(JournalFile *f) {
152 r = sd_id128_get_machine(&f->header->machine_id);
156 r = sd_id128_get_boot(&boot_id);
160 if (sd_id128_equal(boot_id, f->header->boot_id))
161 f->tail_entry_monotonic_valid = true;
163 f->header->boot_id = boot_id;
165 f->header->state = STATE_ONLINE;
167 /* Sync the online state to disk */
168 msync(f->header, PAGE_ALIGN(sizeof(Header)), MS_SYNC);
174 static int journal_file_verify_header(JournalFile *f) {
177 if (memcmp(f->header->signature, HEADER_SIGNATURE, 8))
180 /* In both read and write mode we refuse to open files with
181 * incompatible flags we don't know */
183 if ((le32toh(f->header->incompatible_flags) & ~HEADER_INCOMPATIBLE_COMPRESSED) != 0)
184 return -EPROTONOSUPPORT;
186 if (f->header->incompatible_flags != 0)
187 return -EPROTONOSUPPORT;
190 /* When open for writing we refuse to open files with
191 * compatible flags, too */
194 if ((le32toh(f->header->compatible_flags) & ~HEADER_COMPATIBLE_AUTHENTICATED) != 0)
195 return -EPROTONOSUPPORT;
197 if (f->header->compatible_flags != 0)
198 return -EPROTONOSUPPORT;
202 /* The first addition was n_data, so check that we are at least this large */
203 if (le64toh(f->header->header_size) < HEADER_SIZE_MIN)
206 if ((le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_AUTHENTICATED) &&
207 !JOURNAL_HEADER_CONTAINS(f->header, n_tags))
210 if ((uint64_t) f->last_stat.st_size < (le64toh(f->header->header_size) + le64toh(f->header->arena_size)))
215 sd_id128_t machine_id;
218 r = sd_id128_get_machine(&machine_id);
222 if (!sd_id128_equal(machine_id, f->header->machine_id))
225 state = f->header->state;
227 if (state == STATE_ONLINE) {
228 log_debug("Journal file %s is already online. Assuming unclean closing.", f->path);
230 } else if (state == STATE_ARCHIVED)
232 else if (state != STATE_OFFLINE) {
233 log_debug("Journal file %s has unknown state %u.", f->path, state);
238 f->compress = !!(le32toh(f->header->incompatible_flags) & HEADER_INCOMPATIBLE_COMPRESSED);
239 f->authenticate = !!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_AUTHENTICATED);
244 static int journal_file_allocate(JournalFile *f, uint64_t offset, uint64_t size) {
245 uint64_t old_size, new_size;
250 /* We assume that this file is not sparse, and we know that
251 * for sure, since we always call posix_fallocate()
255 le64toh(f->header->header_size) +
256 le64toh(f->header->arena_size);
258 new_size = PAGE_ALIGN(offset + size);
259 if (new_size < le64toh(f->header->header_size))
260 new_size = le64toh(f->header->header_size);
262 if (new_size <= old_size)
265 if (f->metrics.max_size > 0 &&
266 new_size > f->metrics.max_size)
269 if (new_size > f->metrics.min_size &&
270 f->metrics.keep_free > 0) {
273 if (fstatvfs(f->fd, &svfs) >= 0) {
276 available = svfs.f_bfree * svfs.f_bsize;
278 if (available >= f->metrics.keep_free)
279 available -= f->metrics.keep_free;
283 if (new_size - old_size > available)
288 /* Note that the glibc fallocate() fallback is very
289 inefficient, hence we try to minimize the allocation area
291 r = posix_fallocate(f->fd, old_size, new_size - old_size);
295 mmap_cache_close_fd_range(f->mmap, f->fd, old_size);
297 if (fstat(f->fd, &f->last_stat) < 0)
300 f->header->arena_size = htole64(new_size - le64toh(f->header->header_size));
305 static int journal_file_move_to(JournalFile *f, int context, uint64_t offset, uint64_t size, void **ret) {
309 /* Avoid SIGBUS on invalid accesses */
310 if (offset + size > (uint64_t) f->last_stat.st_size) {
311 /* Hmm, out of range? Let's refresh the fstat() data
312 * first, before we trust that check. */
314 if (fstat(f->fd, &f->last_stat) < 0 ||
315 offset + size > (uint64_t) f->last_stat.st_size)
316 return -EADDRNOTAVAIL;
319 return mmap_cache_get(f->mmap, f->fd, f->prot, context, offset, size, ret);
322 static bool verify_hash(Object *o) {
327 if (o->object.type == OBJECT_DATA && !(o->object.flags & OBJECT_COMPRESSED)) {
328 h1 = le64toh(o->data.hash);
329 h2 = hash64(o->data.payload, le64toh(o->object.size) - offsetof(Object, data.payload));
330 } else if (o->object.type == OBJECT_FIELD) {
331 h1 = le64toh(o->field.hash);
332 h2 = hash64(o->field.payload, le64toh(o->object.size) - offsetof(Object, field.payload));
339 static uint64_t minimum_header_size(Object *o) {
341 static uint64_t table[] = {
342 [OBJECT_DATA] = sizeof(DataObject),
343 [OBJECT_FIELD] = sizeof(FieldObject),
344 [OBJECT_ENTRY] = sizeof(EntryObject),
345 [OBJECT_DATA_HASH_TABLE] = sizeof(HashTableObject),
346 [OBJECT_FIELD_HASH_TABLE] = sizeof(HashTableObject),
347 [OBJECT_ENTRY_ARRAY] = sizeof(EntryArrayObject),
348 [OBJECT_TAG] = sizeof(TagObject),
351 if (o->object.type >= ELEMENTSOF(table) || table[o->object.type] <= 0)
352 return sizeof(ObjectHeader);
354 return table[o->object.type];
357 int journal_file_move_to_object(JournalFile *f, int type, uint64_t offset, Object **ret) {
367 /* One context for each type, plus one catch-all for the rest */
368 context = type > 0 && type < _OBJECT_TYPE_MAX ? type : 0;
370 r = journal_file_move_to(f, context, offset, sizeof(ObjectHeader), &t);
375 s = le64toh(o->object.size);
377 if (s < sizeof(ObjectHeader))
380 if (o->object.type <= OBJECT_UNUSED)
383 if (s < minimum_header_size(o))
386 if (type >= 0 && o->object.type != type)
389 if (s > sizeof(ObjectHeader)) {
390 r = journal_file_move_to(f, o->object.type, offset, s, &t);
404 static uint64_t journal_file_entry_seqnum(JournalFile *f, uint64_t *seqnum) {
409 r = le64toh(f->header->tail_entry_seqnum) + 1;
412 /* If an external seqnum counter was passed, we update
413 * both the local and the external one, and set it to
414 * the maximum of both */
422 f->header->tail_entry_seqnum = htole64(r);
424 if (f->header->head_entry_seqnum == 0)
425 f->header->head_entry_seqnum = htole64(r);
430 int journal_file_append_object(JournalFile *f, int type, uint64_t size, Object **ret, uint64_t *offset) {
437 assert(type > 0 && type < _OBJECT_TYPE_MAX);
438 assert(size >= sizeof(ObjectHeader));
442 p = le64toh(f->header->tail_object_offset);
444 p = le64toh(f->header->header_size);
446 r = journal_file_move_to_object(f, -1, p, &tail);
450 p += ALIGN64(le64toh(tail->object.size));
453 r = journal_file_allocate(f, p, size);
457 r = journal_file_move_to(f, type, p, size, &t);
464 o->object.type = type;
465 o->object.size = htole64(size);
467 f->header->tail_object_offset = htole64(p);
468 f->header->n_objects = htole64(le64toh(f->header->n_objects) + 1);
476 static int journal_file_setup_data_hash_table(JournalFile *f) {
483 /* We estimate that we need 1 hash table entry per 768 of
484 journal file and we want to make sure we never get beyond
485 75% fill level. Calculate the hash table size for the
486 maximum file size based on these metrics. */
488 s = (f->metrics.max_size * 4 / 768 / 3) * sizeof(HashItem);
489 if (s < DEFAULT_DATA_HASH_TABLE_SIZE)
490 s = DEFAULT_DATA_HASH_TABLE_SIZE;
492 log_info("Reserving %llu entries in hash table.", (unsigned long long) (s / sizeof(HashItem)));
494 r = journal_file_append_object(f,
495 OBJECT_DATA_HASH_TABLE,
496 offsetof(Object, hash_table.items) + s,
501 memset(o->hash_table.items, 0, s);
503 f->header->data_hash_table_offset = htole64(p + offsetof(Object, hash_table.items));
504 f->header->data_hash_table_size = htole64(s);
509 static int journal_file_setup_field_hash_table(JournalFile *f) {
516 s = DEFAULT_FIELD_HASH_TABLE_SIZE;
517 r = journal_file_append_object(f,
518 OBJECT_FIELD_HASH_TABLE,
519 offsetof(Object, hash_table.items) + s,
524 memset(o->hash_table.items, 0, s);
526 f->header->field_hash_table_offset = htole64(p + offsetof(Object, hash_table.items));
527 f->header->field_hash_table_size = htole64(s);
532 static int journal_file_map_data_hash_table(JournalFile *f) {
539 p = le64toh(f->header->data_hash_table_offset);
540 s = le64toh(f->header->data_hash_table_size);
542 r = journal_file_move_to(f,
543 OBJECT_DATA_HASH_TABLE,
549 f->data_hash_table = t;
553 static int journal_file_map_field_hash_table(JournalFile *f) {
560 p = le64toh(f->header->field_hash_table_offset);
561 s = le64toh(f->header->field_hash_table_size);
563 r = journal_file_move_to(f,
564 OBJECT_FIELD_HASH_TABLE,
570 f->field_hash_table = t;
574 static int journal_file_link_data(JournalFile *f, Object *o, uint64_t offset, uint64_t hash) {
581 assert(o->object.type == OBJECT_DATA);
583 /* This might alter the window we are looking at */
585 o->data.next_hash_offset = o->data.next_field_offset = 0;
586 o->data.entry_offset = o->data.entry_array_offset = 0;
587 o->data.n_entries = 0;
589 h = hash % (le64toh(f->header->data_hash_table_size) / sizeof(HashItem));
590 p = le64toh(f->data_hash_table[h].tail_hash_offset);
592 /* Only entry in the hash table is easy */
593 f->data_hash_table[h].head_hash_offset = htole64(offset);
595 /* Move back to the previous data object, to patch in
598 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
602 o->data.next_hash_offset = htole64(offset);
605 f->data_hash_table[h].tail_hash_offset = htole64(offset);
607 if (JOURNAL_HEADER_CONTAINS(f->header, n_data))
608 f->header->n_data = htole64(le64toh(f->header->n_data) + 1);
613 int journal_file_find_data_object_with_hash(
615 const void *data, uint64_t size, uint64_t hash,
616 Object **ret, uint64_t *offset) {
618 uint64_t p, osize, h;
622 assert(data || size == 0);
624 osize = offsetof(Object, data.payload) + size;
626 if (f->header->data_hash_table_size == 0)
629 h = hash % (le64toh(f->header->data_hash_table_size) / sizeof(HashItem));
630 p = le64toh(f->data_hash_table[h].head_hash_offset);
635 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
639 if (le64toh(o->data.hash) != hash)
642 if (o->object.flags & OBJECT_COMPRESSED) {
646 l = le64toh(o->object.size);
647 if (l <= offsetof(Object, data.payload))
650 l -= offsetof(Object, data.payload);
652 if (!uncompress_blob(o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize))
656 memcmp(f->compress_buffer, data, size) == 0) {
667 return -EPROTONOSUPPORT;
670 } else if (le64toh(o->object.size) == osize &&
671 memcmp(o->data.payload, data, size) == 0) {
683 p = le64toh(o->data.next_hash_offset);
689 int journal_file_find_data_object(
691 const void *data, uint64_t size,
692 Object **ret, uint64_t *offset) {
697 assert(data || size == 0);
699 hash = hash64(data, size);
701 return journal_file_find_data_object_with_hash(f,
706 static int journal_file_append_data(
708 const void *data, uint64_t size,
709 Object **ret, uint64_t *offset) {
715 bool compressed = false;
718 assert(data || size == 0);
720 hash = hash64(data, size);
722 r = journal_file_find_data_object_with_hash(f, data, size, hash, &o, &p);
736 osize = offsetof(Object, data.payload) + size;
737 r = journal_file_append_object(f, OBJECT_DATA, osize, &o, &p);
741 o->data.hash = htole64(hash);
745 size >= COMPRESSION_SIZE_THRESHOLD) {
748 compressed = compress_blob(data, size, o->data.payload, &rsize);
751 o->object.size = htole64(offsetof(Object, data.payload) + rsize);
752 o->object.flags |= OBJECT_COMPRESSED;
754 log_debug("Compressed data object %lu -> %lu", (unsigned long) size, (unsigned long) rsize);
759 if (!compressed && size > 0)
760 memcpy(o->data.payload, data, size);
762 r = journal_file_link_data(f, o, p, hash);
766 r = journal_file_hmac_put_object(f, OBJECT_DATA, p);
770 /* The linking might have altered the window, so let's
771 * refresh our pointer */
772 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
785 uint64_t journal_file_entry_n_items(Object *o) {
787 assert(o->object.type == OBJECT_ENTRY);
789 return (le64toh(o->object.size) - offsetof(Object, entry.items)) / sizeof(EntryItem);
792 uint64_t journal_file_entry_array_n_items(Object *o) {
794 assert(o->object.type == OBJECT_ENTRY_ARRAY);
796 return (le64toh(o->object.size) - offsetof(Object, entry_array.items)) / sizeof(uint64_t);
799 static int link_entry_into_array(JournalFile *f,
804 uint64_t n = 0, ap = 0, q, i, a, hidx;
813 i = hidx = le64toh(*idx);
816 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
820 n = journal_file_entry_array_n_items(o);
822 o->entry_array.items[i] = htole64(p);
823 *idx = htole64(hidx + 1);
829 a = le64toh(o->entry_array.next_entry_array_offset);
840 r = journal_file_append_object(f, OBJECT_ENTRY_ARRAY,
841 offsetof(Object, entry_array.items) + n * sizeof(uint64_t),
846 r = journal_file_hmac_put_object(f, OBJECT_ENTRY_ARRAY, q);
850 o->entry_array.items[i] = htole64(p);
855 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, ap, &o);
859 o->entry_array.next_entry_array_offset = htole64(q);
862 *idx = htole64(hidx + 1);
867 static int link_entry_into_array_plus_one(JournalFile *f,
886 i = htole64(le64toh(*idx) - 1);
887 r = link_entry_into_array(f, first, &i, p);
892 *idx = htole64(le64toh(*idx) + 1);
896 static int journal_file_link_entry_item(JournalFile *f, Object *o, uint64_t offset, uint64_t i) {
903 p = le64toh(o->entry.items[i].object_offset);
907 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
911 return link_entry_into_array_plus_one(f,
912 &o->data.entry_offset,
913 &o->data.entry_array_offset,
918 static int journal_file_link_entry(JournalFile *f, Object *o, uint64_t offset) {
925 assert(o->object.type == OBJECT_ENTRY);
927 __sync_synchronize();
929 /* Link up the entry itself */
930 r = link_entry_into_array(f,
931 &f->header->entry_array_offset,
932 &f->header->n_entries,
937 /* log_debug("=> %s seqnr=%lu n_entries=%lu", f->path, (unsigned long) o->entry.seqnum, (unsigned long) f->header->n_entries); */
939 if (f->header->head_entry_realtime == 0)
940 f->header->head_entry_realtime = o->entry.realtime;
942 f->header->tail_entry_realtime = o->entry.realtime;
943 f->header->tail_entry_monotonic = o->entry.monotonic;
945 f->tail_entry_monotonic_valid = true;
947 /* Link up the items */
948 n = journal_file_entry_n_items(o);
949 for (i = 0; i < n; i++) {
950 r = journal_file_link_entry_item(f, o, offset, i);
958 static int journal_file_append_entry_internal(
960 const dual_timestamp *ts,
962 const EntryItem items[], unsigned n_items,
964 Object **ret, uint64_t *offset) {
971 assert(items || n_items == 0);
974 osize = offsetof(Object, entry.items) + (n_items * sizeof(EntryItem));
976 r = journal_file_append_object(f, OBJECT_ENTRY, osize, &o, &np);
980 o->entry.seqnum = htole64(journal_file_entry_seqnum(f, seqnum));
981 memcpy(o->entry.items, items, n_items * sizeof(EntryItem));
982 o->entry.realtime = htole64(ts->realtime);
983 o->entry.monotonic = htole64(ts->monotonic);
984 o->entry.xor_hash = htole64(xor_hash);
985 o->entry.boot_id = f->header->boot_id;
987 r = journal_file_hmac_put_object(f, OBJECT_ENTRY, np);
991 r = journal_file_link_entry(f, o, np);
1004 void journal_file_post_change(JournalFile *f) {
1007 /* inotify() does not receive IN_MODIFY events from file
1008 * accesses done via mmap(). After each access we hence
1009 * trigger IN_MODIFY by truncating the journal file to its
1010 * current size which triggers IN_MODIFY. */
1012 __sync_synchronize();
1014 if (ftruncate(f->fd, f->last_stat.st_size) < 0)
1015 log_error("Failed to to truncate file to its own size: %m");
1018 int journal_file_append_entry(JournalFile *f, const dual_timestamp *ts, const struct iovec iovec[], unsigned n_iovec, uint64_t *seqnum, Object **ret, uint64_t *offset) {
1022 uint64_t xor_hash = 0;
1023 struct dual_timestamp _ts;
1026 assert(iovec || n_iovec == 0);
1032 dual_timestamp_get(&_ts);
1036 if (f->tail_entry_monotonic_valid &&
1037 ts->monotonic < le64toh(f->header->tail_entry_monotonic))
1040 r = journal_file_maybe_append_tag(f, ts->realtime);
1044 /* alloca() can't take 0, hence let's allocate at least one */
1045 items = alloca(sizeof(EntryItem) * MAX(1, n_iovec));
1047 for (i = 0; i < n_iovec; i++) {
1051 r = journal_file_append_data(f, iovec[i].iov_base, iovec[i].iov_len, &o, &p);
1055 xor_hash ^= le64toh(o->data.hash);
1056 items[i].object_offset = htole64(p);
1057 items[i].hash = o->data.hash;
1060 r = journal_file_append_entry_internal(f, ts, xor_hash, items, n_iovec, seqnum, ret, offset);
1062 journal_file_post_change(f);
1067 static int generic_array_get(JournalFile *f,
1070 Object **ret, uint64_t *offset) {
1082 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
1086 n = journal_file_entry_array_n_items(o);
1088 p = le64toh(o->entry_array.items[i]);
1093 a = le64toh(o->entry_array.next_entry_array_offset);
1096 if (a <= 0 || p <= 0)
1099 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
1112 static int generic_array_get_plus_one(JournalFile *f,
1116 Object **ret, uint64_t *offset) {
1125 r = journal_file_move_to_object(f, OBJECT_ENTRY, extra, &o);
1138 return generic_array_get(f, first, i-1, ret, offset);
1147 static int generic_array_bisect(JournalFile *f,
1151 int (*test_object)(JournalFile *f, uint64_t p, uint64_t needle),
1152 direction_t direction,
1157 uint64_t a, p, t = 0, i = 0, last_p = 0;
1158 bool subtract_one = false;
1159 Object *o, *array = NULL;
1163 assert(test_object);
1167 uint64_t left, right, k, lp;
1169 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &array);
1173 k = journal_file_entry_array_n_items(array);
1179 lp = p = le64toh(array->entry_array.items[i]);
1183 r = test_object(f, p, needle);
1187 if (r == TEST_FOUND)
1188 r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT;
1190 if (r == TEST_RIGHT) {
1194 if (left == right) {
1195 if (direction == DIRECTION_UP)
1196 subtract_one = true;
1202 assert(left < right);
1204 i = (left + right) / 2;
1205 p = le64toh(array->entry_array.items[i]);
1209 r = test_object(f, p, needle);
1213 if (r == TEST_FOUND)
1214 r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT;
1216 if (r == TEST_RIGHT)
1224 if (direction == DIRECTION_UP) {
1226 subtract_one = true;
1237 a = le64toh(array->entry_array.next_entry_array_offset);
1243 if (subtract_one && t == 0 && i == 0)
1246 if (subtract_one && i == 0)
1248 else if (subtract_one)
1249 p = le64toh(array->entry_array.items[i-1]);
1251 p = le64toh(array->entry_array.items[i]);
1253 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
1264 *idx = t + i + (subtract_one ? -1 : 0);
1269 static int generic_array_bisect_plus_one(JournalFile *f,
1274 int (*test_object)(JournalFile *f, uint64_t p, uint64_t needle),
1275 direction_t direction,
1281 bool step_back = false;
1285 assert(test_object);
1290 /* This bisects the array in object 'first', but first checks
1292 r = test_object(f, extra, needle);
1296 if (r == TEST_FOUND)
1297 r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT;
1299 /* if we are looking with DIRECTION_UP then we need to first
1300 see if in the actual array there is a matching entry, and
1301 return the last one of that. But if there isn't any we need
1302 to return this one. Hence remember this, and return it
1305 step_back = direction == DIRECTION_UP;
1307 if (r == TEST_RIGHT) {
1308 if (direction == DIRECTION_DOWN)
1314 r = generic_array_bisect(f, first, n-1, needle, test_object, direction, ret, offset, idx);
1316 if (r == 0 && step_back)
1325 r = journal_file_move_to_object(f, OBJECT_ENTRY, extra, &o);
1341 static int test_object_offset(JournalFile *f, uint64_t p, uint64_t needle) {
1347 else if (p < needle)
1353 int journal_file_move_to_entry_by_offset(
1356 direction_t direction,
1360 return generic_array_bisect(f,
1361 le64toh(f->header->entry_array_offset),
1362 le64toh(f->header->n_entries),
1370 static int test_object_seqnum(JournalFile *f, uint64_t p, uint64_t needle) {
1377 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
1381 if (le64toh(o->entry.seqnum) == needle)
1383 else if (le64toh(o->entry.seqnum) < needle)
1389 int journal_file_move_to_entry_by_seqnum(
1392 direction_t direction,
1396 return generic_array_bisect(f,
1397 le64toh(f->header->entry_array_offset),
1398 le64toh(f->header->n_entries),
1405 static int test_object_realtime(JournalFile *f, uint64_t p, uint64_t needle) {
1412 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
1416 if (le64toh(o->entry.realtime) == needle)
1418 else if (le64toh(o->entry.realtime) < needle)
1424 int journal_file_move_to_entry_by_realtime(
1427 direction_t direction,
1431 return generic_array_bisect(f,
1432 le64toh(f->header->entry_array_offset),
1433 le64toh(f->header->n_entries),
1435 test_object_realtime,
1440 static int test_object_monotonic(JournalFile *f, uint64_t p, uint64_t needle) {
1447 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
1451 if (le64toh(o->entry.monotonic) == needle)
1453 else if (le64toh(o->entry.monotonic) < needle)
1459 int journal_file_move_to_entry_by_monotonic(
1463 direction_t direction,
1467 char t[9+32+1] = "_BOOT_ID=";
1473 sd_id128_to_string(boot_id, t + 9);
1474 r = journal_file_find_data_object(f, t, strlen(t), &o, NULL);
1480 return generic_array_bisect_plus_one(f,
1481 le64toh(o->data.entry_offset),
1482 le64toh(o->data.entry_array_offset),
1483 le64toh(o->data.n_entries),
1485 test_object_monotonic,
1490 int journal_file_next_entry(
1492 Object *o, uint64_t p,
1493 direction_t direction,
1494 Object **ret, uint64_t *offset) {
1500 assert(p > 0 || !o);
1502 n = le64toh(f->header->n_entries);
1507 i = direction == DIRECTION_DOWN ? 0 : n - 1;
1509 if (o->object.type != OBJECT_ENTRY)
1512 r = generic_array_bisect(f,
1513 le64toh(f->header->entry_array_offset),
1514 le64toh(f->header->n_entries),
1523 if (direction == DIRECTION_DOWN) {
1536 /* And jump to it */
1537 return generic_array_get(f,
1538 le64toh(f->header->entry_array_offset),
1543 int journal_file_skip_entry(
1545 Object *o, uint64_t p,
1547 Object **ret, uint64_t *offset) {
1556 if (o->object.type != OBJECT_ENTRY)
1559 r = generic_array_bisect(f,
1560 le64toh(f->header->entry_array_offset),
1561 le64toh(f->header->n_entries),
1570 /* Calculate new index */
1572 if ((uint64_t) -skip >= i)
1575 i = i - (uint64_t) -skip;
1577 i += (uint64_t) skip;
1579 n = le64toh(f->header->n_entries);
1586 return generic_array_get(f,
1587 le64toh(f->header->entry_array_offset),
1592 int journal_file_next_entry_for_data(
1594 Object *o, uint64_t p,
1595 uint64_t data_offset,
1596 direction_t direction,
1597 Object **ret, uint64_t *offset) {
1604 assert(p > 0 || !o);
1606 r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
1610 n = le64toh(d->data.n_entries);
1615 i = direction == DIRECTION_DOWN ? 0 : n - 1;
1617 if (o->object.type != OBJECT_ENTRY)
1620 r = generic_array_bisect_plus_one(f,
1621 le64toh(d->data.entry_offset),
1622 le64toh(d->data.entry_array_offset),
1623 le64toh(d->data.n_entries),
1633 if (direction == DIRECTION_DOWN) {
1647 return generic_array_get_plus_one(f,
1648 le64toh(d->data.entry_offset),
1649 le64toh(d->data.entry_array_offset),
1654 int journal_file_move_to_entry_by_offset_for_data(
1656 uint64_t data_offset,
1658 direction_t direction,
1659 Object **ret, uint64_t *offset) {
1666 r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
1670 return generic_array_bisect_plus_one(f,
1671 le64toh(d->data.entry_offset),
1672 le64toh(d->data.entry_array_offset),
1673 le64toh(d->data.n_entries),
1680 int journal_file_move_to_entry_by_monotonic_for_data(
1682 uint64_t data_offset,
1685 direction_t direction,
1686 Object **ret, uint64_t *offset) {
1688 char t[9+32+1] = "_BOOT_ID=";
1695 /* First, seek by time */
1696 sd_id128_to_string(boot_id, t + 9);
1697 r = journal_file_find_data_object(f, t, strlen(t), &o, &b);
1703 r = generic_array_bisect_plus_one(f,
1704 le64toh(o->data.entry_offset),
1705 le64toh(o->data.entry_array_offset),
1706 le64toh(o->data.n_entries),
1708 test_object_monotonic,
1714 /* And now, continue seeking until we find an entry that
1715 * exists in both bisection arrays */
1721 r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
1725 r = generic_array_bisect_plus_one(f,
1726 le64toh(d->data.entry_offset),
1727 le64toh(d->data.entry_array_offset),
1728 le64toh(d->data.n_entries),
1736 r = journal_file_move_to_object(f, OBJECT_DATA, b, &o);
1740 r = generic_array_bisect_plus_one(f,
1741 le64toh(o->data.entry_offset),
1742 le64toh(o->data.entry_array_offset),
1743 le64toh(o->data.n_entries),
1767 int journal_file_move_to_entry_by_seqnum_for_data(
1769 uint64_t data_offset,
1771 direction_t direction,
1772 Object **ret, uint64_t *offset) {
1779 r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
1783 return generic_array_bisect_plus_one(f,
1784 le64toh(d->data.entry_offset),
1785 le64toh(d->data.entry_array_offset),
1786 le64toh(d->data.n_entries),
1793 int journal_file_move_to_entry_by_realtime_for_data(
1795 uint64_t data_offset,
1797 direction_t direction,
1798 Object **ret, uint64_t *offset) {
1805 r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
1809 return generic_array_bisect_plus_one(f,
1810 le64toh(d->data.entry_offset),
1811 le64toh(d->data.entry_array_offset),
1812 le64toh(d->data.n_entries),
1814 test_object_realtime,
1819 void journal_file_dump(JournalFile *f) {
1826 journal_file_print_header(f);
1828 p = le64toh(f->header->header_size);
1830 r = journal_file_move_to_object(f, -1, p, &o);
1834 switch (o->object.type) {
1837 printf("Type: OBJECT_UNUSED\n");
1841 printf("Type: OBJECT_DATA\n");
1845 printf("Type: OBJECT_ENTRY %llu %llu %llu\n",
1846 (unsigned long long) le64toh(o->entry.seqnum),
1847 (unsigned long long) le64toh(o->entry.monotonic),
1848 (unsigned long long) le64toh(o->entry.realtime));
1851 case OBJECT_FIELD_HASH_TABLE:
1852 printf("Type: OBJECT_FIELD_HASH_TABLE\n");
1855 case OBJECT_DATA_HASH_TABLE:
1856 printf("Type: OBJECT_DATA_HASH_TABLE\n");
1859 case OBJECT_ENTRY_ARRAY:
1860 printf("Type: OBJECT_ENTRY_ARRAY\n");
1864 printf("Type: OBJECT_TAG %llu\n",
1865 (unsigned long long) le64toh(o->tag.seqnum));
1869 if (o->object.flags & OBJECT_COMPRESSED)
1870 printf("Flags: COMPRESSED\n");
1872 if (p == le64toh(f->header->tail_object_offset))
1875 p = p + ALIGN64(le64toh(o->object.size));
1880 log_error("File corrupt");
1883 void journal_file_print_header(JournalFile *f) {
1884 char a[33], b[33], c[33];
1885 char x[FORMAT_TIMESTAMP_MAX], y[FORMAT_TIMESTAMP_MAX];
1889 printf("File Path: %s\n"
1893 "Sequential Number ID: %s\n"
1895 "Compatible Flags:%s%s\n"
1896 "Incompatible Flags:%s%s\n"
1897 "Header size: %llu\n"
1898 "Arena size: %llu\n"
1899 "Data Hash Table Size: %llu\n"
1900 "Field Hash Table Size: %llu\n"
1902 "Entry Objects: %llu\n"
1903 "Rotate Suggested: %s\n"
1904 "Head Sequential Number: %llu\n"
1905 "Tail Sequential Number: %llu\n"
1906 "Head Realtime Timestamp: %s\n"
1907 "Tail Realtime Timestamp: %s\n",
1909 sd_id128_to_string(f->header->file_id, a),
1910 sd_id128_to_string(f->header->machine_id, b),
1911 sd_id128_to_string(f->header->boot_id, c),
1912 sd_id128_to_string(f->header->seqnum_id, c),
1913 f->header->state == STATE_OFFLINE ? "offline" :
1914 f->header->state == STATE_ONLINE ? "online" :
1915 f->header->state == STATE_ARCHIVED ? "archived" : "unknown",
1916 (f->header->compatible_flags & HEADER_COMPATIBLE_AUTHENTICATED) ? " AUTHENTICATED" : "",
1917 (f->header->compatible_flags & ~HEADER_COMPATIBLE_AUTHENTICATED) ? " ???" : "",
1918 (f->header->incompatible_flags & HEADER_INCOMPATIBLE_COMPRESSED) ? " COMPRESSED" : "",
1919 (f->header->incompatible_flags & ~HEADER_INCOMPATIBLE_COMPRESSED) ? " ???" : "",
1920 (unsigned long long) le64toh(f->header->header_size),
1921 (unsigned long long) le64toh(f->header->arena_size),
1922 (unsigned long long) le64toh(f->header->data_hash_table_size) / sizeof(HashItem),
1923 (unsigned long long) le64toh(f->header->field_hash_table_size) / sizeof(HashItem),
1924 (unsigned long long) le64toh(f->header->n_objects),
1925 (unsigned long long) le64toh(f->header->n_entries),
1926 yes_no(journal_file_rotate_suggested(f)),
1927 (unsigned long long) le64toh(f->header->head_entry_seqnum),
1928 (unsigned long long) le64toh(f->header->tail_entry_seqnum),
1929 format_timestamp(x, sizeof(x), le64toh(f->header->head_entry_realtime)),
1930 format_timestamp(y, sizeof(y), le64toh(f->header->tail_entry_realtime)));
1932 if (JOURNAL_HEADER_CONTAINS(f->header, n_data))
1933 printf("Data Objects: %llu\n"
1934 "Data Hash Table Fill: %.1f%%\n",
1935 (unsigned long long) le64toh(f->header->n_data),
1936 100.0 * (double) le64toh(f->header->n_data) / ((double) (le64toh(f->header->data_hash_table_size) / sizeof(HashItem))));
1938 if (JOURNAL_HEADER_CONTAINS(f->header, n_fields))
1939 printf("Field Objects: %llu\n"
1940 "Field Hash Table Fill: %.1f%%\n",
1941 (unsigned long long) le64toh(f->header->n_fields),
1942 100.0 * (double) le64toh(f->header->n_fields) / ((double) (le64toh(f->header->field_hash_table_size) / sizeof(HashItem))));
1945 int journal_file_open(
1951 JournalMetrics *metrics,
1952 MMapCache *mmap_cache,
1953 JournalFile *template,
1954 JournalFile **ret) {
1958 bool newly_created = false;
1962 if ((flags & O_ACCMODE) != O_RDONLY &&
1963 (flags & O_ACCMODE) != O_RDWR)
1966 if (!endswith(fname, ".journal"))
1969 f = new0(JournalFile, 1);
1977 f->prot = prot_from_flags(flags);
1978 f->writable = (flags & O_ACCMODE) != O_RDONLY;
1979 f->compress = compress;
1980 f->authenticate = authenticate;
1983 f->mmap = mmap_cache_ref(mmap_cache);
1985 /* One context for each type, plus the zeroth catchall
1986 * context. One fd for the file plus one for each type
1987 * (which we need during verification */
1988 f->mmap = mmap_cache_new(_OBJECT_TYPE_MAX, 1 + _OBJECT_TYPE_MAX);
1995 f->path = strdup(fname);
2001 f->fd = open(f->path, f->flags|O_CLOEXEC, f->mode);
2007 if (fstat(f->fd, &f->last_stat) < 0) {
2012 if (f->last_stat.st_size == 0 && f->writable) {
2013 newly_created = true;
2015 /* Try to load the FSPRG state, and if we can't, then
2016 * just don't do authentication */
2017 r = journal_file_load_fsprg(f);
2019 f->authenticate = false;
2021 r = journal_file_init_header(f, template);
2025 if (fstat(f->fd, &f->last_stat) < 0) {
2031 if (f->last_stat.st_size < (off_t) HEADER_SIZE_MIN) {
2036 f->header = mmap(NULL, PAGE_ALIGN(sizeof(Header)), prot_from_flags(flags), MAP_SHARED, f->fd, 0);
2037 if (f->header == MAP_FAILED) {
2043 if (!newly_created) {
2044 r = journal_file_verify_header(f);
2049 if (!newly_created && f->writable) {
2050 r = journal_file_load_fsprg(f);
2057 journal_default_metrics(metrics, f->fd);
2058 f->metrics = *metrics;
2059 } else if (template)
2060 f->metrics = template->metrics;
2062 r = journal_file_refresh_header(f);
2066 r = journal_file_setup_hmac(f);
2071 if (newly_created) {
2072 r = journal_file_setup_field_hash_table(f);
2076 r = journal_file_setup_data_hash_table(f);
2080 r = journal_file_append_first_tag(f);
2085 r = journal_file_map_field_hash_table(f);
2089 r = journal_file_map_data_hash_table(f);
2099 journal_file_close(f);
2104 int journal_file_rotate(JournalFile **f, bool compress, bool authenticate) {
2107 JournalFile *old_file, *new_file = NULL;
2115 if (!old_file->writable)
2118 if (!endswith(old_file->path, ".journal"))
2121 l = strlen(old_file->path);
2123 p = new(char, l + 1 + 32 + 1 + 16 + 1 + 16 + 1);
2127 memcpy(p, old_file->path, l - 8);
2129 sd_id128_to_string(old_file->header->seqnum_id, p + l - 8 + 1);
2130 snprintf(p + l - 8 + 1 + 32, 1 + 16 + 1 + 16 + 8 + 1,
2131 "-%016llx-%016llx.journal",
2132 (unsigned long long) le64toh((*f)->header->tail_entry_seqnum),
2133 (unsigned long long) le64toh((*f)->header->tail_entry_realtime));
2135 r = rename(old_file->path, p);
2141 old_file->header->state = STATE_ARCHIVED;
2143 r = journal_file_open(old_file->path, old_file->flags, old_file->mode, compress, authenticate, NULL, old_file->mmap, old_file, &new_file);
2144 journal_file_close(old_file);
2150 int journal_file_open_reliably(
2156 JournalMetrics *metrics,
2158 JournalFile *template,
2159 JournalFile **ret) {
2165 r = journal_file_open(fname, flags, mode, compress, authenticate, metrics, mmap, template, ret);
2166 if (r != -EBADMSG && /* corrupted */
2167 r != -ENODATA && /* truncated */
2168 r != -EHOSTDOWN && /* other machine */
2169 r != -EPROTONOSUPPORT && /* incompatible feature */
2170 r != -EBUSY && /* unclean shutdown */
2171 r != -ESHUTDOWN /* already archived */)
2174 if ((flags & O_ACCMODE) == O_RDONLY)
2177 if (!(flags & O_CREAT))
2180 if (!endswith(fname, ".journal"))
2183 /* The file is corrupted. Rotate it away and try it again (but only once) */
2186 if (asprintf(&p, "%.*s@%016llx-%016llx.journal~",
2188 (unsigned long long) now(CLOCK_REALTIME),
2192 r = rename(fname, p);
2197 log_warning("File %s corrupted or uncleanly shut down, renaming and replacing.", fname);
2199 return journal_file_open(fname, flags, mode, compress, authenticate, metrics, mmap, template, ret);
2203 int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint64_t p, uint64_t *seqnum, Object **ret, uint64_t *offset) {
2205 uint64_t q, xor_hash = 0;
2218 ts.monotonic = le64toh(o->entry.monotonic);
2219 ts.realtime = le64toh(o->entry.realtime);
2221 if (to->tail_entry_monotonic_valid &&
2222 ts.monotonic < le64toh(to->header->tail_entry_monotonic))
2225 n = journal_file_entry_n_items(o);
2226 items = alloca(sizeof(EntryItem) * n);
2228 for (i = 0; i < n; i++) {
2235 q = le64toh(o->entry.items[i].object_offset);
2236 le_hash = o->entry.items[i].hash;
2238 r = journal_file_move_to_object(from, OBJECT_DATA, q, &o);
2242 if (le_hash != o->data.hash)
2245 l = le64toh(o->object.size) - offsetof(Object, data.payload);
2248 /* We hit the limit on 32bit machines */
2249 if ((uint64_t) t != l)
2252 if (o->object.flags & OBJECT_COMPRESSED) {
2256 if (!uncompress_blob(o->data.payload, l, &from->compress_buffer, &from->compress_buffer_size, &rsize))
2259 data = from->compress_buffer;
2262 return -EPROTONOSUPPORT;
2265 data = o->data.payload;
2267 r = journal_file_append_data(to, data, l, &u, &h);
2271 xor_hash ^= le64toh(u->data.hash);
2272 items[i].object_offset = htole64(h);
2273 items[i].hash = u->data.hash;
2275 r = journal_file_move_to_object(from, OBJECT_ENTRY, p, &o);
2280 return journal_file_append_entry_internal(to, &ts, xor_hash, items, n, seqnum, ret, offset);
2283 void journal_default_metrics(JournalMetrics *m, int fd) {
2284 uint64_t fs_size = 0;
2286 char a[FORMAT_BYTES_MAX], b[FORMAT_BYTES_MAX], c[FORMAT_BYTES_MAX], d[FORMAT_BYTES_MAX];
2291 if (fstatvfs(fd, &ss) >= 0)
2292 fs_size = ss.f_frsize * ss.f_blocks;
2294 if (m->max_use == (uint64_t) -1) {
2297 m->max_use = PAGE_ALIGN(fs_size / 10); /* 10% of file system size */
2299 if (m->max_use > DEFAULT_MAX_USE_UPPER)
2300 m->max_use = DEFAULT_MAX_USE_UPPER;
2302 if (m->max_use < DEFAULT_MAX_USE_LOWER)
2303 m->max_use = DEFAULT_MAX_USE_LOWER;
2305 m->max_use = DEFAULT_MAX_USE_LOWER;
2307 m->max_use = PAGE_ALIGN(m->max_use);
2309 if (m->max_use < JOURNAL_FILE_SIZE_MIN*2)
2310 m->max_use = JOURNAL_FILE_SIZE_MIN*2;
2313 if (m->max_size == (uint64_t) -1) {
2314 m->max_size = PAGE_ALIGN(m->max_use / 8); /* 8 chunks */
2316 if (m->max_size > DEFAULT_MAX_SIZE_UPPER)
2317 m->max_size = DEFAULT_MAX_SIZE_UPPER;
2319 m->max_size = PAGE_ALIGN(m->max_size);
2321 if (m->max_size < JOURNAL_FILE_SIZE_MIN)
2322 m->max_size = JOURNAL_FILE_SIZE_MIN;
2324 if (m->max_size*2 > m->max_use)
2325 m->max_use = m->max_size*2;
2327 if (m->min_size == (uint64_t) -1)
2328 m->min_size = JOURNAL_FILE_SIZE_MIN;
2330 m->min_size = PAGE_ALIGN(m->min_size);
2332 if (m->min_size < JOURNAL_FILE_SIZE_MIN)
2333 m->min_size = JOURNAL_FILE_SIZE_MIN;
2335 if (m->min_size > m->max_size)
2336 m->max_size = m->min_size;
2339 if (m->keep_free == (uint64_t) -1) {
2342 m->keep_free = PAGE_ALIGN(fs_size / 20); /* 5% of file system size */
2344 if (m->keep_free > DEFAULT_KEEP_FREE_UPPER)
2345 m->keep_free = DEFAULT_KEEP_FREE_UPPER;
2348 m->keep_free = DEFAULT_KEEP_FREE;
2351 log_info("Fixed max_use=%s max_size=%s min_size=%s keep_free=%s",
2352 format_bytes(a, sizeof(a), m->max_use),
2353 format_bytes(b, sizeof(b), m->max_size),
2354 format_bytes(c, sizeof(c), m->min_size),
2355 format_bytes(d, sizeof(d), m->keep_free));
2358 int journal_file_get_cutoff_realtime_usec(JournalFile *f, usec_t *from, usec_t *to) {
2363 if (f->header->head_entry_realtime == 0)
2366 *from = le64toh(f->header->head_entry_realtime);
2370 if (f->header->tail_entry_realtime == 0)
2373 *to = le64toh(f->header->tail_entry_realtime);
2379 int journal_file_get_cutoff_monotonic_usec(JournalFile *f, sd_id128_t boot_id, usec_t *from, usec_t *to) {
2380 char t[9+32+1] = "_BOOT_ID=";
2388 sd_id128_to_string(boot_id, t + 9);
2390 r = journal_file_find_data_object(f, t, strlen(t), &o, &p);
2394 if (le64toh(o->data.n_entries) <= 0)
2398 r = journal_file_move_to_object(f, OBJECT_ENTRY, le64toh(o->data.entry_offset), &o);
2402 *from = le64toh(o->entry.monotonic);
2406 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
2410 r = generic_array_get_plus_one(f,
2411 le64toh(o->data.entry_offset),
2412 le64toh(o->data.entry_array_offset),
2413 le64toh(o->data.n_entries)-1,
2418 *to = le64toh(o->entry.monotonic);
2424 bool journal_file_rotate_suggested(JournalFile *f) {
2427 /* If we gained new header fields we gained new features,
2428 * hence suggest a rotation */
2429 if (le64toh(f->header->header_size) < sizeof(Header)) {
2430 log_debug("%s uses an outdated header, suggesting rotation.", f->path);
2434 /* Let's check if the hash tables grew over a certain fill
2435 * level (75%, borrowing this value from Java's hash table
2436 * implementation), and if so suggest a rotation. To calculate
2437 * the fill level we need the n_data field, which only exists
2438 * in newer versions. */
2440 if (JOURNAL_HEADER_CONTAINS(f->header, n_data))
2441 if (le64toh(f->header->n_data) * 4ULL > (le64toh(f->header->data_hash_table_size) / sizeof(HashItem)) * 3ULL) {
2442 log_debug("Data hash table of %s has a fill level at %.1f (%llu of %llu items, %llu file size, %llu bytes per hash table item), suggesting rotation.",
2444 100.0 * (double) le64toh(f->header->n_data) / ((double) (le64toh(f->header->data_hash_table_size) / sizeof(HashItem))),
2445 (unsigned long long) le64toh(f->header->n_data),
2446 (unsigned long long) (le64toh(f->header->data_hash_table_size) / sizeof(HashItem)),
2447 (unsigned long long) (f->last_stat.st_size),
2448 (unsigned long long) (f->last_stat.st_size / le64toh(f->header->n_data)));
2452 if (JOURNAL_HEADER_CONTAINS(f->header, n_fields))
2453 if (le64toh(f->header->n_fields) * 4ULL > (le64toh(f->header->field_hash_table_size) / sizeof(HashItem)) * 3ULL) {
2454 log_debug("Field hash table of %s has a fill level at %.1f (%llu of %llu items), suggesting rotation.",
2456 100.0 * (double) le64toh(f->header->n_fields) / ((double) (le64toh(f->header->field_hash_table_size) / sizeof(HashItem))),
2457 (unsigned long long) le64toh(f->header->n_fields),
2458 (unsigned long long) (le64toh(f->header->field_hash_table_size) / sizeof(HashItem)));