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) {
68 /* Write the final tag */
69 if (f->seal && f->writable)
70 journal_file_append_tag(f);
73 /* Sync everything to disk, before we mark the file offline */
74 if (f->mmap && f->fd >= 0)
75 mmap_cache_close_fd(f->mmap, f->fd);
77 if (f->writable && f->fd >= 0)
81 /* Mark the file offline. Don't override the archived state if it already is set */
82 if (f->writable && f->header->state == STATE_ONLINE)
83 f->header->state = STATE_OFFLINE;
85 munmap(f->header, PAGE_ALIGN(sizeof(Header)));
89 close_nointr_nofail(f->fd);
94 mmap_cache_unref(f->mmap);
97 free(f->compress_buffer);
102 munmap(f->fss_file, PAGE_ALIGN(f->fss_file_size));
103 else if (f->fsprg_state)
104 free(f->fsprg_state);
109 gcry_md_close(f->hmac);
115 static int journal_file_init_header(JournalFile *f, JournalFile *template) {
123 memcpy(h.signature, HEADER_SIGNATURE, 8);
124 h.header_size = htole64(ALIGN64(sizeof(h)));
126 h.incompatible_flags =
127 htole32(f->compress ? HEADER_INCOMPATIBLE_COMPRESSED : 0);
130 htole32(f->seal ? HEADER_COMPATIBLE_SEALED : 0);
132 r = sd_id128_randomize(&h.file_id);
137 h.seqnum_id = template->header->seqnum_id;
138 h.tail_entry_seqnum = template->header->tail_entry_seqnum;
140 h.seqnum_id = h.file_id;
142 k = pwrite(f->fd, &h, sizeof(h), 0);
152 static int journal_file_refresh_header(JournalFile *f) {
158 r = sd_id128_get_machine(&f->header->machine_id);
162 r = sd_id128_get_boot(&boot_id);
166 if (sd_id128_equal(boot_id, f->header->boot_id))
167 f->tail_entry_monotonic_valid = true;
169 f->header->boot_id = boot_id;
171 f->header->state = STATE_ONLINE;
173 /* Sync the online state to disk */
174 msync(f->header, PAGE_ALIGN(sizeof(Header)), MS_SYNC);
180 static int journal_file_verify_header(JournalFile *f) {
183 if (memcmp(f->header->signature, HEADER_SIGNATURE, 8))
186 /* In both read and write mode we refuse to open files with
187 * incompatible flags we don't know */
189 if ((le32toh(f->header->incompatible_flags) & ~HEADER_INCOMPATIBLE_COMPRESSED) != 0)
190 return -EPROTONOSUPPORT;
192 if (f->header->incompatible_flags != 0)
193 return -EPROTONOSUPPORT;
196 /* When open for writing we refuse to open files with
197 * compatible flags, too */
200 if ((le32toh(f->header->compatible_flags) & ~HEADER_COMPATIBLE_SEALED) != 0)
201 return -EPROTONOSUPPORT;
203 if (f->header->compatible_flags != 0)
204 return -EPROTONOSUPPORT;
208 if (f->header->state >= _STATE_MAX)
211 /* The first addition was n_data, so check that we are at least this large */
212 if (le64toh(f->header->header_size) < HEADER_SIZE_MIN)
215 if (JOURNAL_HEADER_SEALED(f->header) && !JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays))
218 if ((le64toh(f->header->header_size) + le64toh(f->header->arena_size)) > (uint64_t) f->last_stat.st_size)
221 if (le64toh(f->header->tail_object_offset) > (le64toh(f->header->header_size) + le64toh(f->header->arena_size)))
224 if (!VALID64(le64toh(f->header->data_hash_table_offset)) ||
225 !VALID64(le64toh(f->header->field_hash_table_offset)) ||
226 !VALID64(le64toh(f->header->tail_object_offset)) ||
227 !VALID64(le64toh(f->header->entry_array_offset)))
230 if (le64toh(f->header->data_hash_table_offset) < le64toh(f->header->header_size) ||
231 le64toh(f->header->field_hash_table_offset) < le64toh(f->header->header_size) ||
232 le64toh(f->header->tail_object_offset) < le64toh(f->header->header_size) ||
233 le64toh(f->header->entry_array_offset) < le64toh(f->header->header_size))
238 sd_id128_t machine_id;
241 r = sd_id128_get_machine(&machine_id);
245 if (!sd_id128_equal(machine_id, f->header->machine_id))
248 state = f->header->state;
250 if (state == STATE_ONLINE) {
251 log_debug("Journal file %s is already online. Assuming unclean closing.", f->path);
253 } else if (state == STATE_ARCHIVED)
255 else if (state != STATE_OFFLINE) {
256 log_debug("Journal file %s has unknown state %u.", f->path, state);
261 f->compress = JOURNAL_HEADER_COMPRESSED(f->header);
263 f->seal = JOURNAL_HEADER_SEALED(f->header);
268 static int journal_file_allocate(JournalFile *f, uint64_t offset, uint64_t size) {
269 uint64_t old_size, new_size;
274 /* We assume that this file is not sparse, and we know that
275 * for sure, since we always call posix_fallocate()
279 le64toh(f->header->header_size) +
280 le64toh(f->header->arena_size);
282 new_size = PAGE_ALIGN(offset + size);
283 if (new_size < le64toh(f->header->header_size))
284 new_size = le64toh(f->header->header_size);
286 if (new_size <= old_size)
289 if (f->metrics.max_size > 0 &&
290 new_size > f->metrics.max_size)
293 if (new_size > f->metrics.min_size &&
294 f->metrics.keep_free > 0) {
297 if (fstatvfs(f->fd, &svfs) >= 0) {
300 available = svfs.f_bfree * svfs.f_bsize;
302 if (available >= f->metrics.keep_free)
303 available -= f->metrics.keep_free;
307 if (new_size - old_size > available)
312 /* Note that the glibc fallocate() fallback is very
313 inefficient, hence we try to minimize the allocation area
315 r = posix_fallocate(f->fd, old_size, new_size - old_size);
319 if (fstat(f->fd, &f->last_stat) < 0)
322 f->header->arena_size = htole64(new_size - le64toh(f->header->header_size));
327 static int journal_file_move_to(JournalFile *f, int context, bool keep_always, uint64_t offset, uint64_t size, void **ret) {
334 /* Avoid SIGBUS on invalid accesses */
335 if (offset + size > (uint64_t) f->last_stat.st_size) {
336 /* Hmm, out of range? Let's refresh the fstat() data
337 * first, before we trust that check. */
339 if (fstat(f->fd, &f->last_stat) < 0 ||
340 offset + size > (uint64_t) f->last_stat.st_size)
341 return -EADDRNOTAVAIL;
344 return mmap_cache_get(f->mmap, f->fd, f->prot, context, keep_always, offset, size, &f->last_stat, ret);
347 static uint64_t minimum_header_size(Object *o) {
349 static uint64_t table[] = {
350 [OBJECT_DATA] = sizeof(DataObject),
351 [OBJECT_FIELD] = sizeof(FieldObject),
352 [OBJECT_ENTRY] = sizeof(EntryObject),
353 [OBJECT_DATA_HASH_TABLE] = sizeof(HashTableObject),
354 [OBJECT_FIELD_HASH_TABLE] = sizeof(HashTableObject),
355 [OBJECT_ENTRY_ARRAY] = sizeof(EntryArrayObject),
356 [OBJECT_TAG] = sizeof(TagObject),
359 if (o->object.type >= ELEMENTSOF(table) || table[o->object.type] <= 0)
360 return sizeof(ObjectHeader);
362 return table[o->object.type];
365 int journal_file_move_to_object(JournalFile *f, int type, uint64_t offset, Object **ret) {
375 /* Objects may only be located at multiple of 64 bit */
376 if (!VALID64(offset))
379 /* One context for each type, plus one catch-all for the rest */
380 context = type > 0 && type < _OBJECT_TYPE_MAX ? type : 0;
382 r = journal_file_move_to(f, context, false, offset, sizeof(ObjectHeader), &t);
387 s = le64toh(o->object.size);
389 if (s < sizeof(ObjectHeader))
392 if (o->object.type <= OBJECT_UNUSED)
395 if (s < minimum_header_size(o))
398 if (type >= 0 && o->object.type != type)
401 if (s > sizeof(ObjectHeader)) {
402 r = journal_file_move_to(f, o->object.type, false, offset, s, &t);
413 static uint64_t journal_file_entry_seqnum(JournalFile *f, uint64_t *seqnum) {
418 r = le64toh(f->header->tail_entry_seqnum) + 1;
421 /* If an external seqnum counter was passed, we update
422 * both the local and the external one, and set it to
423 * the maximum of both */
431 f->header->tail_entry_seqnum = htole64(r);
433 if (f->header->head_entry_seqnum == 0)
434 f->header->head_entry_seqnum = htole64(r);
439 int journal_file_append_object(JournalFile *f, int type, uint64_t size, Object **ret, uint64_t *offset) {
446 assert(type > 0 && type < _OBJECT_TYPE_MAX);
447 assert(size >= sizeof(ObjectHeader));
451 p = le64toh(f->header->tail_object_offset);
453 p = le64toh(f->header->header_size);
455 r = journal_file_move_to_object(f, -1, p, &tail);
459 p += ALIGN64(le64toh(tail->object.size));
462 r = journal_file_allocate(f, p, size);
466 r = journal_file_move_to(f, type, false, p, size, &t);
473 o->object.type = type;
474 o->object.size = htole64(size);
476 f->header->tail_object_offset = htole64(p);
477 f->header->n_objects = htole64(le64toh(f->header->n_objects) + 1);
485 static int journal_file_setup_data_hash_table(JournalFile *f) {
492 /* We estimate that we need 1 hash table entry per 768 of
493 journal file and we want to make sure we never get beyond
494 75% fill level. Calculate the hash table size for the
495 maximum file size based on these metrics. */
497 s = (f->metrics.max_size * 4 / 768 / 3) * sizeof(HashItem);
498 if (s < DEFAULT_DATA_HASH_TABLE_SIZE)
499 s = DEFAULT_DATA_HASH_TABLE_SIZE;
501 log_debug("Reserving %llu entries in hash table.", (unsigned long long) (s / sizeof(HashItem)));
503 r = journal_file_append_object(f,
504 OBJECT_DATA_HASH_TABLE,
505 offsetof(Object, hash_table.items) + s,
510 memset(o->hash_table.items, 0, s);
512 f->header->data_hash_table_offset = htole64(p + offsetof(Object, hash_table.items));
513 f->header->data_hash_table_size = htole64(s);
518 static int journal_file_setup_field_hash_table(JournalFile *f) {
525 s = DEFAULT_FIELD_HASH_TABLE_SIZE;
526 r = journal_file_append_object(f,
527 OBJECT_FIELD_HASH_TABLE,
528 offsetof(Object, hash_table.items) + s,
533 memset(o->hash_table.items, 0, s);
535 f->header->field_hash_table_offset = htole64(p + offsetof(Object, hash_table.items));
536 f->header->field_hash_table_size = htole64(s);
541 static int journal_file_map_data_hash_table(JournalFile *f) {
548 p = le64toh(f->header->data_hash_table_offset);
549 s = le64toh(f->header->data_hash_table_size);
551 r = journal_file_move_to(f,
552 OBJECT_DATA_HASH_TABLE,
559 f->data_hash_table = t;
563 static int journal_file_map_field_hash_table(JournalFile *f) {
570 p = le64toh(f->header->field_hash_table_offset);
571 s = le64toh(f->header->field_hash_table_size);
573 r = journal_file_move_to(f,
574 OBJECT_FIELD_HASH_TABLE,
581 f->field_hash_table = t;
585 static int journal_file_link_data(JournalFile *f, Object *o, uint64_t offset, uint64_t hash) {
593 if (o->object.type != OBJECT_DATA)
596 /* This might alter the window we are looking at */
598 o->data.next_hash_offset = o->data.next_field_offset = 0;
599 o->data.entry_offset = o->data.entry_array_offset = 0;
600 o->data.n_entries = 0;
602 h = hash % (le64toh(f->header->data_hash_table_size) / sizeof(HashItem));
603 p = le64toh(f->data_hash_table[h].tail_hash_offset);
605 /* Only entry in the hash table is easy */
606 f->data_hash_table[h].head_hash_offset = htole64(offset);
608 /* Move back to the previous data object, to patch in
611 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
615 o->data.next_hash_offset = htole64(offset);
618 f->data_hash_table[h].tail_hash_offset = htole64(offset);
620 if (JOURNAL_HEADER_CONTAINS(f->header, n_data))
621 f->header->n_data = htole64(le64toh(f->header->n_data) + 1);
626 int journal_file_find_data_object_with_hash(
628 const void *data, uint64_t size, uint64_t hash,
629 Object **ret, uint64_t *offset) {
631 uint64_t p, osize, h;
635 assert(data || size == 0);
637 osize = offsetof(Object, data.payload) + size;
639 if (f->header->data_hash_table_size == 0)
642 h = hash % (le64toh(f->header->data_hash_table_size) / sizeof(HashItem));
643 p = le64toh(f->data_hash_table[h].head_hash_offset);
648 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
652 if (le64toh(o->data.hash) != hash)
655 if (o->object.flags & OBJECT_COMPRESSED) {
659 l = le64toh(o->object.size);
660 if (l <= offsetof(Object, data.payload))
663 l -= offsetof(Object, data.payload);
665 if (!uncompress_blob(o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize))
669 memcmp(f->compress_buffer, data, size) == 0) {
680 return -EPROTONOSUPPORT;
683 } else if (le64toh(o->object.size) == osize &&
684 memcmp(o->data.payload, data, size) == 0) {
696 p = le64toh(o->data.next_hash_offset);
702 int journal_file_find_data_object(
704 const void *data, uint64_t size,
705 Object **ret, uint64_t *offset) {
710 assert(data || size == 0);
712 hash = hash64(data, size);
714 return journal_file_find_data_object_with_hash(f,
719 static int journal_file_append_data(
721 const void *data, uint64_t size,
722 Object **ret, uint64_t *offset) {
728 bool compressed = false;
731 assert(data || size == 0);
733 hash = hash64(data, size);
735 r = journal_file_find_data_object_with_hash(f, data, size, hash, &o, &p);
749 osize = offsetof(Object, data.payload) + size;
750 r = journal_file_append_object(f, OBJECT_DATA, osize, &o, &p);
754 o->data.hash = htole64(hash);
758 size >= COMPRESSION_SIZE_THRESHOLD) {
761 compressed = compress_blob(data, size, o->data.payload, &rsize);
764 o->object.size = htole64(offsetof(Object, data.payload) + rsize);
765 o->object.flags |= OBJECT_COMPRESSED;
767 log_debug("Compressed data object %lu -> %lu", (unsigned long) size, (unsigned long) rsize);
772 if (!compressed && size > 0)
773 memcpy(o->data.payload, data, size);
775 r = journal_file_link_data(f, o, p, hash);
779 /* The linking might have altered the window, so let's
780 * refresh our pointer */
781 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
786 r = journal_file_hmac_put_object(f, OBJECT_DATA, o, p);
800 uint64_t journal_file_entry_n_items(Object *o) {
803 if (o->object.type != OBJECT_ENTRY)
806 return (le64toh(o->object.size) - offsetof(Object, entry.items)) / sizeof(EntryItem);
809 uint64_t journal_file_entry_array_n_items(Object *o) {
812 if (o->object.type != OBJECT_ENTRY_ARRAY)
815 return (le64toh(o->object.size) - offsetof(Object, entry_array.items)) / sizeof(uint64_t);
818 uint64_t journal_file_hash_table_n_items(Object *o) {
821 if (o->object.type != OBJECT_DATA_HASH_TABLE &&
822 o->object.type != OBJECT_FIELD_HASH_TABLE)
825 return (le64toh(o->object.size) - offsetof(Object, hash_table.items)) / sizeof(HashItem);
828 static int link_entry_into_array(JournalFile *f,
833 uint64_t n = 0, ap = 0, q, i, a, hidx;
842 i = hidx = le64toh(*idx);
845 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
849 n = journal_file_entry_array_n_items(o);
851 o->entry_array.items[i] = htole64(p);
852 *idx = htole64(hidx + 1);
858 a = le64toh(o->entry_array.next_entry_array_offset);
869 r = journal_file_append_object(f, OBJECT_ENTRY_ARRAY,
870 offsetof(Object, entry_array.items) + n * sizeof(uint64_t),
876 r = journal_file_hmac_put_object(f, OBJECT_ENTRY_ARRAY, o, q);
881 o->entry_array.items[i] = htole64(p);
886 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, ap, &o);
890 o->entry_array.next_entry_array_offset = htole64(q);
893 if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays))
894 f->header->n_entry_arrays = htole64(le64toh(f->header->n_entry_arrays) + 1);
896 *idx = htole64(hidx + 1);
901 static int link_entry_into_array_plus_one(JournalFile *f,
920 i = htole64(le64toh(*idx) - 1);
921 r = link_entry_into_array(f, first, &i, p);
926 *idx = htole64(le64toh(*idx) + 1);
930 static int journal_file_link_entry_item(JournalFile *f, Object *o, uint64_t offset, uint64_t i) {
937 p = le64toh(o->entry.items[i].object_offset);
941 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
945 return link_entry_into_array_plus_one(f,
946 &o->data.entry_offset,
947 &o->data.entry_array_offset,
952 static int journal_file_link_entry(JournalFile *f, Object *o, uint64_t offset) {
960 if (o->object.type != OBJECT_ENTRY)
963 __sync_synchronize();
965 /* Link up the entry itself */
966 r = link_entry_into_array(f,
967 &f->header->entry_array_offset,
968 &f->header->n_entries,
973 /* log_debug("=> %s seqnr=%lu n_entries=%lu", f->path, (unsigned long) o->entry.seqnum, (unsigned long) f->header->n_entries); */
975 if (f->header->head_entry_realtime == 0)
976 f->header->head_entry_realtime = o->entry.realtime;
978 f->header->tail_entry_realtime = o->entry.realtime;
979 f->header->tail_entry_monotonic = o->entry.monotonic;
981 f->tail_entry_monotonic_valid = true;
983 /* Link up the items */
984 n = journal_file_entry_n_items(o);
985 for (i = 0; i < n; i++) {
986 r = journal_file_link_entry_item(f, o, offset, i);
994 static int journal_file_append_entry_internal(
996 const dual_timestamp *ts,
998 const EntryItem items[], unsigned n_items,
1000 Object **ret, uint64_t *offset) {
1007 assert(items || n_items == 0);
1010 osize = offsetof(Object, entry.items) + (n_items * sizeof(EntryItem));
1012 r = journal_file_append_object(f, OBJECT_ENTRY, osize, &o, &np);
1016 o->entry.seqnum = htole64(journal_file_entry_seqnum(f, seqnum));
1017 memcpy(o->entry.items, items, n_items * sizeof(EntryItem));
1018 o->entry.realtime = htole64(ts->realtime);
1019 o->entry.monotonic = htole64(ts->monotonic);
1020 o->entry.xor_hash = htole64(xor_hash);
1021 o->entry.boot_id = f->header->boot_id;
1024 r = journal_file_hmac_put_object(f, OBJECT_ENTRY, o, np);
1029 r = journal_file_link_entry(f, o, np);
1042 void journal_file_post_change(JournalFile *f) {
1045 /* inotify() does not receive IN_MODIFY events from file
1046 * accesses done via mmap(). After each access we hence
1047 * trigger IN_MODIFY by truncating the journal file to its
1048 * current size which triggers IN_MODIFY. */
1050 __sync_synchronize();
1052 if (ftruncate(f->fd, f->last_stat.st_size) < 0)
1053 log_error("Failed to truncate file to its own size: %m");
1056 static int entry_item_cmp(const void *_a, const void *_b) {
1057 const EntryItem *a = _a, *b = _b;
1059 if (le64toh(a->object_offset) < le64toh(b->object_offset))
1061 if (le64toh(a->object_offset) > le64toh(b->object_offset))
1066 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) {
1070 uint64_t xor_hash = 0;
1071 struct dual_timestamp _ts;
1074 assert(iovec || n_iovec == 0);
1080 dual_timestamp_get(&_ts);
1084 if (f->tail_entry_monotonic_valid &&
1085 ts->monotonic < le64toh(f->header->tail_entry_monotonic))
1089 r = journal_file_maybe_append_tag(f, ts->realtime);
1094 /* alloca() can't take 0, hence let's allocate at least one */
1095 items = alloca(sizeof(EntryItem) * MAX(1, n_iovec));
1097 for (i = 0; i < n_iovec; i++) {
1101 r = journal_file_append_data(f, iovec[i].iov_base, iovec[i].iov_len, &o, &p);
1105 xor_hash ^= le64toh(o->data.hash);
1106 items[i].object_offset = htole64(p);
1107 items[i].hash = o->data.hash;
1110 /* Order by the position on disk, in order to improve seek
1111 * times for rotating media. */
1112 qsort(items, n_iovec, sizeof(EntryItem), entry_item_cmp);
1114 r = journal_file_append_entry_internal(f, ts, xor_hash, items, n_iovec, seqnum, ret, offset);
1116 journal_file_post_change(f);
1121 static int generic_array_get(JournalFile *f,
1124 Object **ret, uint64_t *offset) {
1136 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
1140 n = journal_file_entry_array_n_items(o);
1142 p = le64toh(o->entry_array.items[i]);
1147 a = le64toh(o->entry_array.next_entry_array_offset);
1150 if (a <= 0 || p <= 0)
1153 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
1166 static int generic_array_get_plus_one(JournalFile *f,
1170 Object **ret, uint64_t *offset) {
1179 r = journal_file_move_to_object(f, OBJECT_ENTRY, extra, &o);
1192 return generic_array_get(f, first, i-1, ret, offset);
1201 static int generic_array_bisect(JournalFile *f,
1205 int (*test_object)(JournalFile *f, uint64_t p, uint64_t needle),
1206 direction_t direction,
1211 uint64_t a, p, t = 0, i = 0, last_p = 0;
1212 bool subtract_one = false;
1213 Object *o, *array = NULL;
1217 assert(test_object);
1221 uint64_t left, right, k, lp;
1223 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &array);
1227 k = journal_file_entry_array_n_items(array);
1233 lp = p = le64toh(array->entry_array.items[i]);
1237 r = test_object(f, p, needle);
1241 if (r == TEST_FOUND)
1242 r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT;
1244 if (r == TEST_RIGHT) {
1248 if (left == right) {
1249 if (direction == DIRECTION_UP)
1250 subtract_one = true;
1256 assert(left < right);
1258 i = (left + right) / 2;
1259 p = le64toh(array->entry_array.items[i]);
1263 r = test_object(f, p, needle);
1267 if (r == TEST_FOUND)
1268 r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT;
1270 if (r == TEST_RIGHT)
1278 if (direction == DIRECTION_UP) {
1280 subtract_one = true;
1291 a = le64toh(array->entry_array.next_entry_array_offset);
1297 if (subtract_one && t == 0 && i == 0)
1300 if (subtract_one && i == 0)
1302 else if (subtract_one)
1303 p = le64toh(array->entry_array.items[i-1]);
1305 p = le64toh(array->entry_array.items[i]);
1307 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
1318 *idx = t + i + (subtract_one ? -1 : 0);
1323 static int generic_array_bisect_plus_one(JournalFile *f,
1328 int (*test_object)(JournalFile *f, uint64_t p, uint64_t needle),
1329 direction_t direction,
1335 bool step_back = false;
1339 assert(test_object);
1344 /* This bisects the array in object 'first', but first checks
1346 r = test_object(f, extra, needle);
1350 if (r == TEST_FOUND)
1351 r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT;
1353 /* if we are looking with DIRECTION_UP then we need to first
1354 see if in the actual array there is a matching entry, and
1355 return the last one of that. But if there isn't any we need
1356 to return this one. Hence remember this, and return it
1359 step_back = direction == DIRECTION_UP;
1361 if (r == TEST_RIGHT) {
1362 if (direction == DIRECTION_DOWN)
1368 r = generic_array_bisect(f, first, n-1, needle, test_object, direction, ret, offset, idx);
1370 if (r == 0 && step_back)
1379 r = journal_file_move_to_object(f, OBJECT_ENTRY, extra, &o);
1395 static int test_object_offset(JournalFile *f, uint64_t p, uint64_t needle) {
1401 else if (p < needle)
1407 int journal_file_move_to_entry_by_offset(
1410 direction_t direction,
1414 return generic_array_bisect(f,
1415 le64toh(f->header->entry_array_offset),
1416 le64toh(f->header->n_entries),
1424 static int test_object_seqnum(JournalFile *f, uint64_t p, uint64_t needle) {
1431 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
1435 if (le64toh(o->entry.seqnum) == needle)
1437 else if (le64toh(o->entry.seqnum) < needle)
1443 int journal_file_move_to_entry_by_seqnum(
1446 direction_t direction,
1450 return generic_array_bisect(f,
1451 le64toh(f->header->entry_array_offset),
1452 le64toh(f->header->n_entries),
1459 static int test_object_realtime(JournalFile *f, uint64_t p, uint64_t needle) {
1466 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
1470 if (le64toh(o->entry.realtime) == needle)
1472 else if (le64toh(o->entry.realtime) < needle)
1478 int journal_file_move_to_entry_by_realtime(
1481 direction_t direction,
1485 return generic_array_bisect(f,
1486 le64toh(f->header->entry_array_offset),
1487 le64toh(f->header->n_entries),
1489 test_object_realtime,
1494 static int test_object_monotonic(JournalFile *f, uint64_t p, uint64_t needle) {
1501 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
1505 if (le64toh(o->entry.monotonic) == needle)
1507 else if (le64toh(o->entry.monotonic) < needle)
1513 int journal_file_move_to_entry_by_monotonic(
1517 direction_t direction,
1521 char t[9+32+1] = "_BOOT_ID=";
1527 sd_id128_to_string(boot_id, t + 9);
1528 r = journal_file_find_data_object(f, t, strlen(t), &o, NULL);
1534 return generic_array_bisect_plus_one(f,
1535 le64toh(o->data.entry_offset),
1536 le64toh(o->data.entry_array_offset),
1537 le64toh(o->data.n_entries),
1539 test_object_monotonic,
1544 int journal_file_next_entry(
1546 Object *o, uint64_t p,
1547 direction_t direction,
1548 Object **ret, uint64_t *offset) {
1554 assert(p > 0 || !o);
1556 n = le64toh(f->header->n_entries);
1561 i = direction == DIRECTION_DOWN ? 0 : n - 1;
1563 if (o->object.type != OBJECT_ENTRY)
1566 r = generic_array_bisect(f,
1567 le64toh(f->header->entry_array_offset),
1568 le64toh(f->header->n_entries),
1577 if (direction == DIRECTION_DOWN) {
1590 /* And jump to it */
1591 return generic_array_get(f,
1592 le64toh(f->header->entry_array_offset),
1597 int journal_file_skip_entry(
1599 Object *o, uint64_t p,
1601 Object **ret, uint64_t *offset) {
1610 if (o->object.type != OBJECT_ENTRY)
1613 r = generic_array_bisect(f,
1614 le64toh(f->header->entry_array_offset),
1615 le64toh(f->header->n_entries),
1624 /* Calculate new index */
1626 if ((uint64_t) -skip >= i)
1629 i = i - (uint64_t) -skip;
1631 i += (uint64_t) skip;
1633 n = le64toh(f->header->n_entries);
1640 return generic_array_get(f,
1641 le64toh(f->header->entry_array_offset),
1646 int journal_file_next_entry_for_data(
1648 Object *o, uint64_t p,
1649 uint64_t data_offset,
1650 direction_t direction,
1651 Object **ret, uint64_t *offset) {
1658 assert(p > 0 || !o);
1660 r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
1664 n = le64toh(d->data.n_entries);
1669 i = direction == DIRECTION_DOWN ? 0 : n - 1;
1671 if (o->object.type != OBJECT_ENTRY)
1674 r = generic_array_bisect_plus_one(f,
1675 le64toh(d->data.entry_offset),
1676 le64toh(d->data.entry_array_offset),
1677 le64toh(d->data.n_entries),
1687 if (direction == DIRECTION_DOWN) {
1701 return generic_array_get_plus_one(f,
1702 le64toh(d->data.entry_offset),
1703 le64toh(d->data.entry_array_offset),
1708 int journal_file_move_to_entry_by_offset_for_data(
1710 uint64_t data_offset,
1712 direction_t direction,
1713 Object **ret, uint64_t *offset) {
1720 r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
1724 return generic_array_bisect_plus_one(f,
1725 le64toh(d->data.entry_offset),
1726 le64toh(d->data.entry_array_offset),
1727 le64toh(d->data.n_entries),
1734 int journal_file_move_to_entry_by_monotonic_for_data(
1736 uint64_t data_offset,
1739 direction_t direction,
1740 Object **ret, uint64_t *offset) {
1742 char t[9+32+1] = "_BOOT_ID=";
1749 /* First, seek by time */
1750 sd_id128_to_string(boot_id, t + 9);
1751 r = journal_file_find_data_object(f, t, strlen(t), &o, &b);
1757 r = generic_array_bisect_plus_one(f,
1758 le64toh(o->data.entry_offset),
1759 le64toh(o->data.entry_array_offset),
1760 le64toh(o->data.n_entries),
1762 test_object_monotonic,
1768 /* And now, continue seeking until we find an entry that
1769 * exists in both bisection arrays */
1775 r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
1779 r = generic_array_bisect_plus_one(f,
1780 le64toh(d->data.entry_offset),
1781 le64toh(d->data.entry_array_offset),
1782 le64toh(d->data.n_entries),
1790 r = journal_file_move_to_object(f, OBJECT_DATA, b, &o);
1794 r = generic_array_bisect_plus_one(f,
1795 le64toh(o->data.entry_offset),
1796 le64toh(o->data.entry_array_offset),
1797 le64toh(o->data.n_entries),
1821 int journal_file_move_to_entry_by_seqnum_for_data(
1823 uint64_t data_offset,
1825 direction_t direction,
1826 Object **ret, uint64_t *offset) {
1833 r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
1837 return generic_array_bisect_plus_one(f,
1838 le64toh(d->data.entry_offset),
1839 le64toh(d->data.entry_array_offset),
1840 le64toh(d->data.n_entries),
1847 int journal_file_move_to_entry_by_realtime_for_data(
1849 uint64_t data_offset,
1851 direction_t direction,
1852 Object **ret, uint64_t *offset) {
1859 r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
1863 return generic_array_bisect_plus_one(f,
1864 le64toh(d->data.entry_offset),
1865 le64toh(d->data.entry_array_offset),
1866 le64toh(d->data.n_entries),
1868 test_object_realtime,
1873 void journal_file_dump(JournalFile *f) {
1880 journal_file_print_header(f);
1882 p = le64toh(f->header->header_size);
1884 r = journal_file_move_to_object(f, -1, p, &o);
1888 switch (o->object.type) {
1891 printf("Type: OBJECT_UNUSED\n");
1895 printf("Type: OBJECT_DATA\n");
1899 printf("Type: OBJECT_ENTRY seqnum=%llu monotonic=%llu realtime=%llu\n",
1900 (unsigned long long) le64toh(o->entry.seqnum),
1901 (unsigned long long) le64toh(o->entry.monotonic),
1902 (unsigned long long) le64toh(o->entry.realtime));
1905 case OBJECT_FIELD_HASH_TABLE:
1906 printf("Type: OBJECT_FIELD_HASH_TABLE\n");
1909 case OBJECT_DATA_HASH_TABLE:
1910 printf("Type: OBJECT_DATA_HASH_TABLE\n");
1913 case OBJECT_ENTRY_ARRAY:
1914 printf("Type: OBJECT_ENTRY_ARRAY\n");
1918 printf("Type: OBJECT_TAG seqnum=%llu epoch=%llu\n",
1919 (unsigned long long) le64toh(o->tag.seqnum),
1920 (unsigned long long) le64toh(o->tag.epoch));
1924 if (o->object.flags & OBJECT_COMPRESSED)
1925 printf("Flags: COMPRESSED\n");
1927 if (p == le64toh(f->header->tail_object_offset))
1930 p = p + ALIGN64(le64toh(o->object.size));
1935 log_error("File corrupt");
1938 void journal_file_print_header(JournalFile *f) {
1939 char a[33], b[33], c[33];
1940 char x[FORMAT_TIMESTAMP_MAX], y[FORMAT_TIMESTAMP_MAX];
1942 char bytes[FORMAT_BYTES_MAX];
1946 printf("File Path: %s\n"
1950 "Sequential Number ID: %s\n"
1952 "Compatible Flags:%s%s\n"
1953 "Incompatible Flags:%s%s\n"
1954 "Header size: %llu\n"
1955 "Arena size: %llu\n"
1956 "Data Hash Table Size: %llu\n"
1957 "Field Hash Table Size: %llu\n"
1958 "Rotate Suggested: %s\n"
1959 "Head Sequential Number: %llu\n"
1960 "Tail Sequential Number: %llu\n"
1961 "Head Realtime Timestamp: %s\n"
1962 "Tail Realtime Timestamp: %s\n"
1964 "Entry Objects: %llu\n",
1966 sd_id128_to_string(f->header->file_id, a),
1967 sd_id128_to_string(f->header->machine_id, b),
1968 sd_id128_to_string(f->header->boot_id, c),
1969 sd_id128_to_string(f->header->seqnum_id, c),
1970 f->header->state == STATE_OFFLINE ? "OFFLINE" :
1971 f->header->state == STATE_ONLINE ? "ONLINE" :
1972 f->header->state == STATE_ARCHIVED ? "ARCHIVED" : "UNKNOWN",
1973 JOURNAL_HEADER_SEALED(f->header) ? " SEALED" : "",
1974 (le32toh(f->header->compatible_flags) & ~HEADER_COMPATIBLE_SEALED) ? " ???" : "",
1975 JOURNAL_HEADER_COMPRESSED(f->header) ? " COMPRESSED" : "",
1976 (le32toh(f->header->incompatible_flags) & ~HEADER_INCOMPATIBLE_COMPRESSED) ? " ???" : "",
1977 (unsigned long long) le64toh(f->header->header_size),
1978 (unsigned long long) le64toh(f->header->arena_size),
1979 (unsigned long long) le64toh(f->header->data_hash_table_size) / sizeof(HashItem),
1980 (unsigned long long) le64toh(f->header->field_hash_table_size) / sizeof(HashItem),
1981 yes_no(journal_file_rotate_suggested(f)),
1982 (unsigned long long) le64toh(f->header->head_entry_seqnum),
1983 (unsigned long long) le64toh(f->header->tail_entry_seqnum),
1984 format_timestamp(x, sizeof(x), le64toh(f->header->head_entry_realtime)),
1985 format_timestamp(y, sizeof(y), le64toh(f->header->tail_entry_realtime)),
1986 (unsigned long long) le64toh(f->header->n_objects),
1987 (unsigned long long) le64toh(f->header->n_entries));
1989 if (JOURNAL_HEADER_CONTAINS(f->header, n_data))
1990 printf("Data Objects: %llu\n"
1991 "Data Hash Table Fill: %.1f%%\n",
1992 (unsigned long long) le64toh(f->header->n_data),
1993 100.0 * (double) le64toh(f->header->n_data) / ((double) (le64toh(f->header->data_hash_table_size) / sizeof(HashItem))));
1995 if (JOURNAL_HEADER_CONTAINS(f->header, n_fields))
1996 printf("Field Objects: %llu\n"
1997 "Field Hash Table Fill: %.1f%%\n",
1998 (unsigned long long) le64toh(f->header->n_fields),
1999 100.0 * (double) le64toh(f->header->n_fields) / ((double) (le64toh(f->header->field_hash_table_size) / sizeof(HashItem))));
2001 if (JOURNAL_HEADER_CONTAINS(f->header, n_tags))
2002 printf("Tag Objects: %llu\n",
2003 (unsigned long long) le64toh(f->header->n_tags));
2004 if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays))
2005 printf("Entry Array Objects: %llu\n",
2006 (unsigned long long) le64toh(f->header->n_entry_arrays));
2008 if (fstat(f->fd, &st) >= 0)
2009 printf("Disk usage: %s\n", format_bytes(bytes, sizeof(bytes), (off_t) st.st_blocks * 512ULL));
2012 int journal_file_open(
2018 JournalMetrics *metrics,
2019 MMapCache *mmap_cache,
2020 JournalFile *template,
2021 JournalFile **ret) {
2025 bool newly_created = false;
2030 if ((flags & O_ACCMODE) != O_RDONLY &&
2031 (flags & O_ACCMODE) != O_RDWR)
2034 if (!endswith(fname, ".journal") &&
2035 !endswith(fname, ".journal~"))
2038 f = new0(JournalFile, 1);
2046 f->prot = prot_from_flags(flags);
2047 f->writable = (flags & O_ACCMODE) != O_RDONLY;
2049 f->compress = compress;
2056 f->mmap = mmap_cache_ref(mmap_cache);
2058 f->mmap = mmap_cache_new();
2065 f->path = strdup(fname);
2071 f->fd = open(f->path, f->flags|O_CLOEXEC, f->mode);
2077 if (fstat(f->fd, &f->last_stat) < 0) {
2082 if (f->last_stat.st_size == 0 && f->writable) {
2083 newly_created = true;
2086 /* Try to load the FSPRG state, and if we can't, then
2087 * just don't do sealing */
2089 r = journal_file_fss_load(f);
2095 r = journal_file_init_header(f, template);
2099 if (fstat(f->fd, &f->last_stat) < 0) {
2105 if (f->last_stat.st_size < (off_t) HEADER_SIZE_MIN) {
2110 f->header = mmap(NULL, PAGE_ALIGN(sizeof(Header)), prot_from_flags(flags), MAP_SHARED, f->fd, 0);
2111 if (f->header == MAP_FAILED) {
2117 if (!newly_created) {
2118 r = journal_file_verify_header(f);
2124 if (!newly_created && f->writable) {
2125 r = journal_file_fss_load(f);
2133 journal_default_metrics(metrics, f->fd);
2134 f->metrics = *metrics;
2135 } else if (template)
2136 f->metrics = template->metrics;
2138 r = journal_file_refresh_header(f);
2144 r = journal_file_hmac_setup(f);
2149 if (newly_created) {
2150 r = journal_file_setup_field_hash_table(f);
2154 r = journal_file_setup_data_hash_table(f);
2159 r = journal_file_append_first_tag(f);
2165 r = journal_file_map_field_hash_table(f);
2169 r = journal_file_map_data_hash_table(f);
2177 journal_file_close(f);
2182 int journal_file_rotate(JournalFile **f, bool compress, bool seal) {
2185 JournalFile *old_file, *new_file = NULL;
2193 if (!old_file->writable)
2196 if (!endswith(old_file->path, ".journal"))
2199 l = strlen(old_file->path);
2201 p = new(char, l + 1 + 32 + 1 + 16 + 1 + 16 + 1);
2205 memcpy(p, old_file->path, l - 8);
2207 sd_id128_to_string(old_file->header->seqnum_id, p + l - 8 + 1);
2208 snprintf(p + l - 8 + 1 + 32, 1 + 16 + 1 + 16 + 8 + 1,
2209 "-%016llx-%016llx.journal",
2210 (unsigned long long) le64toh((*f)->header->tail_entry_seqnum),
2211 (unsigned long long) le64toh((*f)->header->tail_entry_realtime));
2213 r = rename(old_file->path, p);
2219 old_file->header->state = STATE_ARCHIVED;
2221 r = journal_file_open(old_file->path, old_file->flags, old_file->mode, compress, seal, NULL, old_file->mmap, old_file, &new_file);
2222 journal_file_close(old_file);
2228 int journal_file_open_reliably(
2234 JournalMetrics *metrics,
2235 MMapCache *mmap_cache,
2236 JournalFile *template,
2237 JournalFile **ret) {
2243 r = journal_file_open(fname, flags, mode, compress, seal,
2244 metrics, mmap_cache, template, ret);
2245 if (r != -EBADMSG && /* corrupted */
2246 r != -ENODATA && /* truncated */
2247 r != -EHOSTDOWN && /* other machine */
2248 r != -EPROTONOSUPPORT && /* incompatible feature */
2249 r != -EBUSY && /* unclean shutdown */
2250 r != -ESHUTDOWN /* already archived */)
2253 if ((flags & O_ACCMODE) == O_RDONLY)
2256 if (!(flags & O_CREAT))
2259 if (!endswith(fname, ".journal"))
2262 /* The file is corrupted. Rotate it away and try it again (but only once) */
2265 if (asprintf(&p, "%.*s@%016llx-%016llx.journal~",
2267 (unsigned long long) now(CLOCK_REALTIME),
2271 r = rename(fname, p);
2276 log_warning("File %s corrupted or uncleanly shut down, renaming and replacing.", fname);
2278 return journal_file_open(fname, flags, mode, compress, seal,
2279 metrics, mmap_cache, template, ret);
2283 int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint64_t p, uint64_t *seqnum, Object **ret, uint64_t *offset) {
2285 uint64_t q, xor_hash = 0;
2298 ts.monotonic = le64toh(o->entry.monotonic);
2299 ts.realtime = le64toh(o->entry.realtime);
2301 if (to->tail_entry_monotonic_valid &&
2302 ts.monotonic < le64toh(to->header->tail_entry_monotonic))
2305 n = journal_file_entry_n_items(o);
2306 items = alloca(sizeof(EntryItem) * n);
2308 for (i = 0; i < n; i++) {
2315 q = le64toh(o->entry.items[i].object_offset);
2316 le_hash = o->entry.items[i].hash;
2318 r = journal_file_move_to_object(from, OBJECT_DATA, q, &o);
2322 if (le_hash != o->data.hash)
2325 l = le64toh(o->object.size) - offsetof(Object, data.payload);
2328 /* We hit the limit on 32bit machines */
2329 if ((uint64_t) t != l)
2332 if (o->object.flags & OBJECT_COMPRESSED) {
2336 if (!uncompress_blob(o->data.payload, l, &from->compress_buffer, &from->compress_buffer_size, &rsize))
2339 data = from->compress_buffer;
2342 return -EPROTONOSUPPORT;
2345 data = o->data.payload;
2347 r = journal_file_append_data(to, data, l, &u, &h);
2351 xor_hash ^= le64toh(u->data.hash);
2352 items[i].object_offset = htole64(h);
2353 items[i].hash = u->data.hash;
2355 r = journal_file_move_to_object(from, OBJECT_ENTRY, p, &o);
2360 return journal_file_append_entry_internal(to, &ts, xor_hash, items, n, seqnum, ret, offset);
2363 void journal_default_metrics(JournalMetrics *m, int fd) {
2364 uint64_t fs_size = 0;
2366 char a[FORMAT_BYTES_MAX], b[FORMAT_BYTES_MAX], c[FORMAT_BYTES_MAX], d[FORMAT_BYTES_MAX];
2371 if (fstatvfs(fd, &ss) >= 0)
2372 fs_size = ss.f_frsize * ss.f_blocks;
2374 if (m->max_use == (uint64_t) -1) {
2377 m->max_use = PAGE_ALIGN(fs_size / 10); /* 10% of file system size */
2379 if (m->max_use > DEFAULT_MAX_USE_UPPER)
2380 m->max_use = DEFAULT_MAX_USE_UPPER;
2382 if (m->max_use < DEFAULT_MAX_USE_LOWER)
2383 m->max_use = DEFAULT_MAX_USE_LOWER;
2385 m->max_use = DEFAULT_MAX_USE_LOWER;
2387 m->max_use = PAGE_ALIGN(m->max_use);
2389 if (m->max_use < JOURNAL_FILE_SIZE_MIN*2)
2390 m->max_use = JOURNAL_FILE_SIZE_MIN*2;
2393 if (m->max_size == (uint64_t) -1) {
2394 m->max_size = PAGE_ALIGN(m->max_use / 8); /* 8 chunks */
2396 if (m->max_size > DEFAULT_MAX_SIZE_UPPER)
2397 m->max_size = DEFAULT_MAX_SIZE_UPPER;
2399 m->max_size = PAGE_ALIGN(m->max_size);
2401 if (m->max_size < JOURNAL_FILE_SIZE_MIN)
2402 m->max_size = JOURNAL_FILE_SIZE_MIN;
2404 if (m->max_size*2 > m->max_use)
2405 m->max_use = m->max_size*2;
2407 if (m->min_size == (uint64_t) -1)
2408 m->min_size = JOURNAL_FILE_SIZE_MIN;
2410 m->min_size = PAGE_ALIGN(m->min_size);
2412 if (m->min_size < JOURNAL_FILE_SIZE_MIN)
2413 m->min_size = JOURNAL_FILE_SIZE_MIN;
2415 if (m->min_size > m->max_size)
2416 m->max_size = m->min_size;
2419 if (m->keep_free == (uint64_t) -1) {
2422 m->keep_free = PAGE_ALIGN(fs_size / 20); /* 5% of file system size */
2424 if (m->keep_free > DEFAULT_KEEP_FREE_UPPER)
2425 m->keep_free = DEFAULT_KEEP_FREE_UPPER;
2428 m->keep_free = DEFAULT_KEEP_FREE;
2431 log_debug("Fixed max_use=%s max_size=%s min_size=%s keep_free=%s",
2432 format_bytes(a, sizeof(a), m->max_use),
2433 format_bytes(b, sizeof(b), m->max_size),
2434 format_bytes(c, sizeof(c), m->min_size),
2435 format_bytes(d, sizeof(d), m->keep_free));
2438 int journal_file_get_cutoff_realtime_usec(JournalFile *f, usec_t *from, usec_t *to) {
2443 if (f->header->head_entry_realtime == 0)
2446 *from = le64toh(f->header->head_entry_realtime);
2450 if (f->header->tail_entry_realtime == 0)
2453 *to = le64toh(f->header->tail_entry_realtime);
2459 int journal_file_get_cutoff_monotonic_usec(JournalFile *f, sd_id128_t boot_id, usec_t *from, usec_t *to) {
2460 char t[9+32+1] = "_BOOT_ID=";
2468 sd_id128_to_string(boot_id, t + 9);
2470 r = journal_file_find_data_object(f, t, strlen(t), &o, &p);
2474 if (le64toh(o->data.n_entries) <= 0)
2478 r = journal_file_move_to_object(f, OBJECT_ENTRY, le64toh(o->data.entry_offset), &o);
2482 *from = le64toh(o->entry.monotonic);
2486 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
2490 r = generic_array_get_plus_one(f,
2491 le64toh(o->data.entry_offset),
2492 le64toh(o->data.entry_array_offset),
2493 le64toh(o->data.n_entries)-1,
2498 *to = le64toh(o->entry.monotonic);
2504 bool journal_file_rotate_suggested(JournalFile *f) {
2507 /* If we gained new header fields we gained new features,
2508 * hence suggest a rotation */
2509 if (le64toh(f->header->header_size) < sizeof(Header)) {
2510 log_debug("%s uses an outdated header, suggesting rotation.", f->path);
2514 /* Let's check if the hash tables grew over a certain fill
2515 * level (75%, borrowing this value from Java's hash table
2516 * implementation), and if so suggest a rotation. To calculate
2517 * the fill level we need the n_data field, which only exists
2518 * in newer versions. */
2520 if (JOURNAL_HEADER_CONTAINS(f->header, n_data))
2521 if (le64toh(f->header->n_data) * 4ULL > (le64toh(f->header->data_hash_table_size) / sizeof(HashItem)) * 3ULL) {
2522 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.",
2524 100.0 * (double) le64toh(f->header->n_data) / ((double) (le64toh(f->header->data_hash_table_size) / sizeof(HashItem))),
2525 (unsigned long long) le64toh(f->header->n_data),
2526 (unsigned long long) (le64toh(f->header->data_hash_table_size) / sizeof(HashItem)),
2527 (unsigned long long) (f->last_stat.st_size),
2528 (unsigned long long) (f->last_stat.st_size / le64toh(f->header->n_data)));
2532 if (JOURNAL_HEADER_CONTAINS(f->header, n_fields))
2533 if (le64toh(f->header->n_fields) * 4ULL > (le64toh(f->header->field_hash_table_size) / sizeof(HashItem)) * 3ULL) {
2534 log_debug("Field hash table of %s has a fill level at %.1f (%llu of %llu items), suggesting rotation.",
2536 100.0 * (double) le64toh(f->header->n_fields) / ((double) (le64toh(f->header->field_hash_table_size) / sizeof(HashItem))),
2537 (unsigned long long) le64toh(f->header->n_fields),
2538 (unsigned long long) (le64toh(f->header->field_hash_table_size) / sizeof(HashItem)));