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 */
68 if (f->seal && f->writable)
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->fss_file, PAGE_ALIGN(f->fss_file_size));
101 else if (f->fsprg_state)
102 free(f->fsprg_state);
107 gcry_md_close(f->hmac);
113 static int journal_file_init_header(JournalFile *f, JournalFile *template) {
121 memcpy(h.signature, HEADER_SIGNATURE, 8);
122 h.header_size = htole64(ALIGN64(sizeof(h)));
124 h.incompatible_flags =
125 htole32(f->compress ? HEADER_INCOMPATIBLE_COMPRESSED : 0);
128 htole32(f->seal ? HEADER_COMPATIBLE_SEALED : 0);
130 r = sd_id128_randomize(&h.file_id);
135 h.seqnum_id = template->header->seqnum_id;
136 h.tail_entry_seqnum = template->header->tail_entry_seqnum;
138 h.seqnum_id = h.file_id;
140 k = pwrite(f->fd, &h, sizeof(h), 0);
150 static int journal_file_refresh_header(JournalFile *f) {
156 r = sd_id128_get_machine(&f->header->machine_id);
160 r = sd_id128_get_boot(&boot_id);
164 if (sd_id128_equal(boot_id, f->header->boot_id))
165 f->tail_entry_monotonic_valid = true;
167 f->header->boot_id = boot_id;
169 f->header->state = STATE_ONLINE;
171 /* Sync the online state to disk */
172 msync(f->header, PAGE_ALIGN(sizeof(Header)), MS_SYNC);
178 static int journal_file_verify_header(JournalFile *f) {
181 if (memcmp(f->header->signature, HEADER_SIGNATURE, 8))
184 /* In both read and write mode we refuse to open files with
185 * incompatible flags we don't know */
187 if ((le32toh(f->header->incompatible_flags) & ~HEADER_INCOMPATIBLE_COMPRESSED) != 0)
188 return -EPROTONOSUPPORT;
190 if (f->header->incompatible_flags != 0)
191 return -EPROTONOSUPPORT;
194 /* When open for writing we refuse to open files with
195 * compatible flags, too */
198 if ((le32toh(f->header->compatible_flags) & ~HEADER_COMPATIBLE_SEALED) != 0)
199 return -EPROTONOSUPPORT;
201 if (f->header->compatible_flags != 0)
202 return -EPROTONOSUPPORT;
206 if (f->header->state >= _STATE_MAX)
209 /* The first addition was n_data, so check that we are at least this large */
210 if (le64toh(f->header->header_size) < HEADER_SIZE_MIN)
213 if ((le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_SEALED) &&
214 !JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays))
217 if ((le64toh(f->header->header_size) + le64toh(f->header->arena_size)) > (uint64_t) f->last_stat.st_size)
220 if (le64toh(f->header->tail_object_offset) > (le64toh(f->header->header_size) + le64toh(f->header->arena_size)))
223 if (!VALID64(f->header->data_hash_table_offset) ||
224 !VALID64(f->header->field_hash_table_offset) ||
225 !VALID64(f->header->tail_object_offset) ||
226 !VALID64(f->header->entry_array_offset))
231 sd_id128_t machine_id;
234 r = sd_id128_get_machine(&machine_id);
238 if (!sd_id128_equal(machine_id, f->header->machine_id))
241 state = f->header->state;
243 if (state == STATE_ONLINE) {
244 log_debug("Journal file %s is already online. Assuming unclean closing.", f->path);
246 } else if (state == STATE_ARCHIVED)
248 else if (state != STATE_OFFLINE) {
249 log_debug("Journal file %s has unknown state %u.", f->path, state);
254 f->compress = !!(le32toh(f->header->incompatible_flags) & HEADER_INCOMPATIBLE_COMPRESSED);
257 f->seal = !!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_SEALED);
262 static int journal_file_allocate(JournalFile *f, uint64_t offset, uint64_t size) {
263 uint64_t old_size, new_size;
268 /* We assume that this file is not sparse, and we know that
269 * for sure, since we always call posix_fallocate()
273 le64toh(f->header->header_size) +
274 le64toh(f->header->arena_size);
276 new_size = PAGE_ALIGN(offset + size);
277 if (new_size < le64toh(f->header->header_size))
278 new_size = le64toh(f->header->header_size);
280 if (new_size <= old_size)
283 if (f->metrics.max_size > 0 &&
284 new_size > f->metrics.max_size)
287 if (new_size > f->metrics.min_size &&
288 f->metrics.keep_free > 0) {
291 if (fstatvfs(f->fd, &svfs) >= 0) {
294 available = svfs.f_bfree * svfs.f_bsize;
296 if (available >= f->metrics.keep_free)
297 available -= f->metrics.keep_free;
301 if (new_size - old_size > available)
306 /* Note that the glibc fallocate() fallback is very
307 inefficient, hence we try to minimize the allocation area
309 r = posix_fallocate(f->fd, old_size, new_size - old_size);
313 mmap_cache_close_fd_range(f->mmap, f->fd, old_size);
315 if (fstat(f->fd, &f->last_stat) < 0)
318 f->header->arena_size = htole64(new_size - le64toh(f->header->header_size));
323 static int journal_file_move_to(JournalFile *f, int context, uint64_t offset, uint64_t size, void **ret) {
327 /* Avoid SIGBUS on invalid accesses */
328 if (offset + size > (uint64_t) f->last_stat.st_size) {
329 /* Hmm, out of range? Let's refresh the fstat() data
330 * first, before we trust that check. */
332 if (fstat(f->fd, &f->last_stat) < 0 ||
333 offset + size > (uint64_t) f->last_stat.st_size)
334 return -EADDRNOTAVAIL;
337 return mmap_cache_get(f->mmap, f->fd, f->prot, context, offset, size, ret);
340 static uint64_t minimum_header_size(Object *o) {
342 static uint64_t table[] = {
343 [OBJECT_DATA] = sizeof(DataObject),
344 [OBJECT_FIELD] = sizeof(FieldObject),
345 [OBJECT_ENTRY] = sizeof(EntryObject),
346 [OBJECT_DATA_HASH_TABLE] = sizeof(HashTableObject),
347 [OBJECT_FIELD_HASH_TABLE] = sizeof(HashTableObject),
348 [OBJECT_ENTRY_ARRAY] = sizeof(EntryArrayObject),
349 [OBJECT_TAG] = sizeof(TagObject),
352 if (o->object.type >= ELEMENTSOF(table) || table[o->object.type] <= 0)
353 return sizeof(ObjectHeader);
355 return table[o->object.type];
358 int journal_file_move_to_object(JournalFile *f, int type, uint64_t offset, Object **ret) {
368 /* Objects may only be located at multiple of 64 bit */
369 if (!VALID64(offset))
372 /* One context for each type, plus one catch-all for the rest */
373 context = type > 0 && type < _OBJECT_TYPE_MAX ? type : 0;
375 r = journal_file_move_to(f, context, offset, sizeof(ObjectHeader), &t);
380 s = le64toh(o->object.size);
382 if (s < sizeof(ObjectHeader))
385 if (o->object.type <= OBJECT_UNUSED)
388 if (s < minimum_header_size(o))
391 if (type >= 0 && o->object.type != type)
394 if (s > sizeof(ObjectHeader)) {
395 r = journal_file_move_to(f, o->object.type, offset, s, &t);
406 static uint64_t journal_file_entry_seqnum(JournalFile *f, uint64_t *seqnum) {
411 r = le64toh(f->header->tail_entry_seqnum) + 1;
414 /* If an external seqnum counter was passed, we update
415 * both the local and the external one, and set it to
416 * the maximum of both */
424 f->header->tail_entry_seqnum = htole64(r);
426 if (f->header->head_entry_seqnum == 0)
427 f->header->head_entry_seqnum = htole64(r);
432 int journal_file_append_object(JournalFile *f, int type, uint64_t size, Object **ret, uint64_t *offset) {
439 assert(type > 0 && type < _OBJECT_TYPE_MAX);
440 assert(size >= sizeof(ObjectHeader));
444 p = le64toh(f->header->tail_object_offset);
446 p = le64toh(f->header->header_size);
448 r = journal_file_move_to_object(f, -1, p, &tail);
452 p += ALIGN64(le64toh(tail->object.size));
455 r = journal_file_allocate(f, p, size);
459 r = journal_file_move_to(f, type, p, size, &t);
466 o->object.type = type;
467 o->object.size = htole64(size);
469 f->header->tail_object_offset = htole64(p);
470 f->header->n_objects = htole64(le64toh(f->header->n_objects) + 1);
478 static int journal_file_setup_data_hash_table(JournalFile *f) {
485 /* We estimate that we need 1 hash table entry per 768 of
486 journal file and we want to make sure we never get beyond
487 75% fill level. Calculate the hash table size for the
488 maximum file size based on these metrics. */
490 s = (f->metrics.max_size * 4 / 768 / 3) * sizeof(HashItem);
491 if (s < DEFAULT_DATA_HASH_TABLE_SIZE)
492 s = DEFAULT_DATA_HASH_TABLE_SIZE;
494 log_info("Reserving %llu entries in hash table.", (unsigned long long) (s / sizeof(HashItem)));
496 r = journal_file_append_object(f,
497 OBJECT_DATA_HASH_TABLE,
498 offsetof(Object, hash_table.items) + s,
503 memset(o->hash_table.items, 0, s);
505 f->header->data_hash_table_offset = htole64(p + offsetof(Object, hash_table.items));
506 f->header->data_hash_table_size = htole64(s);
511 static int journal_file_setup_field_hash_table(JournalFile *f) {
518 s = DEFAULT_FIELD_HASH_TABLE_SIZE;
519 r = journal_file_append_object(f,
520 OBJECT_FIELD_HASH_TABLE,
521 offsetof(Object, hash_table.items) + s,
526 memset(o->hash_table.items, 0, s);
528 f->header->field_hash_table_offset = htole64(p + offsetof(Object, hash_table.items));
529 f->header->field_hash_table_size = htole64(s);
534 static int journal_file_map_data_hash_table(JournalFile *f) {
541 p = le64toh(f->header->data_hash_table_offset);
542 s = le64toh(f->header->data_hash_table_size);
544 r = journal_file_move_to(f,
545 OBJECT_DATA_HASH_TABLE,
551 f->data_hash_table = t;
555 static int journal_file_map_field_hash_table(JournalFile *f) {
562 p = le64toh(f->header->field_hash_table_offset);
563 s = le64toh(f->header->field_hash_table_size);
565 r = journal_file_move_to(f,
566 OBJECT_FIELD_HASH_TABLE,
572 f->field_hash_table = t;
576 static int journal_file_link_data(JournalFile *f, Object *o, uint64_t offset, uint64_t hash) {
583 assert(o->object.type == OBJECT_DATA);
585 /* This might alter the window we are looking at */
587 o->data.next_hash_offset = o->data.next_field_offset = 0;
588 o->data.entry_offset = o->data.entry_array_offset = 0;
589 o->data.n_entries = 0;
591 h = hash % (le64toh(f->header->data_hash_table_size) / sizeof(HashItem));
592 p = le64toh(f->data_hash_table[h].tail_hash_offset);
594 /* Only entry in the hash table is easy */
595 f->data_hash_table[h].head_hash_offset = htole64(offset);
597 /* Move back to the previous data object, to patch in
600 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
604 o->data.next_hash_offset = htole64(offset);
607 f->data_hash_table[h].tail_hash_offset = htole64(offset);
609 if (JOURNAL_HEADER_CONTAINS(f->header, n_data))
610 f->header->n_data = htole64(le64toh(f->header->n_data) + 1);
615 int journal_file_find_data_object_with_hash(
617 const void *data, uint64_t size, uint64_t hash,
618 Object **ret, uint64_t *offset) {
620 uint64_t p, osize, h;
624 assert(data || size == 0);
626 osize = offsetof(Object, data.payload) + size;
628 if (f->header->data_hash_table_size == 0)
631 h = hash % (le64toh(f->header->data_hash_table_size) / sizeof(HashItem));
632 p = le64toh(f->data_hash_table[h].head_hash_offset);
637 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
641 if (le64toh(o->data.hash) != hash)
644 if (o->object.flags & OBJECT_COMPRESSED) {
648 l = le64toh(o->object.size);
649 if (l <= offsetof(Object, data.payload))
652 l -= offsetof(Object, data.payload);
654 if (!uncompress_blob(o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize))
658 memcmp(f->compress_buffer, data, size) == 0) {
669 return -EPROTONOSUPPORT;
672 } else if (le64toh(o->object.size) == osize &&
673 memcmp(o->data.payload, data, size) == 0) {
685 p = le64toh(o->data.next_hash_offset);
691 int journal_file_find_data_object(
693 const void *data, uint64_t size,
694 Object **ret, uint64_t *offset) {
699 assert(data || size == 0);
701 hash = hash64(data, size);
703 return journal_file_find_data_object_with_hash(f,
708 static int journal_file_append_data(
710 const void *data, uint64_t size,
711 Object **ret, uint64_t *offset) {
717 bool compressed = false;
720 assert(data || size == 0);
722 hash = hash64(data, size);
724 r = journal_file_find_data_object_with_hash(f, data, size, hash, &o, &p);
738 osize = offsetof(Object, data.payload) + size;
739 r = journal_file_append_object(f, OBJECT_DATA, osize, &o, &p);
743 o->data.hash = htole64(hash);
747 size >= COMPRESSION_SIZE_THRESHOLD) {
750 compressed = compress_blob(data, size, o->data.payload, &rsize);
753 o->object.size = htole64(offsetof(Object, data.payload) + rsize);
754 o->object.flags |= OBJECT_COMPRESSED;
756 log_debug("Compressed data object %lu -> %lu", (unsigned long) size, (unsigned long) rsize);
761 if (!compressed && size > 0)
762 memcpy(o->data.payload, data, size);
764 r = journal_file_link_data(f, o, p, hash);
768 r = journal_file_hmac_put_object(f, OBJECT_DATA, p);
772 /* The linking might have altered the window, so let's
773 * refresh our pointer */
774 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
787 uint64_t journal_file_entry_n_items(Object *o) {
789 assert(o->object.type == OBJECT_ENTRY);
791 return (le64toh(o->object.size) - offsetof(Object, entry.items)) / sizeof(EntryItem);
794 uint64_t journal_file_entry_array_n_items(Object *o) {
796 assert(o->object.type == OBJECT_ENTRY_ARRAY);
798 return (le64toh(o->object.size) - offsetof(Object, entry_array.items)) / sizeof(uint64_t);
801 static int link_entry_into_array(JournalFile *f,
806 uint64_t n = 0, ap = 0, q, i, a, hidx;
815 i = hidx = le64toh(*idx);
818 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
822 n = journal_file_entry_array_n_items(o);
824 o->entry_array.items[i] = htole64(p);
825 *idx = htole64(hidx + 1);
831 a = le64toh(o->entry_array.next_entry_array_offset);
842 r = journal_file_append_object(f, OBJECT_ENTRY_ARRAY,
843 offsetof(Object, entry_array.items) + n * sizeof(uint64_t),
848 r = journal_file_hmac_put_object(f, OBJECT_ENTRY_ARRAY, q);
852 o->entry_array.items[i] = htole64(p);
857 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, ap, &o);
861 o->entry_array.next_entry_array_offset = htole64(q);
864 if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays))
865 f->header->n_entry_arrays = htole64(le64toh(f->header->n_entry_arrays) + 1);
867 *idx = htole64(hidx + 1);
872 static int link_entry_into_array_plus_one(JournalFile *f,
891 i = htole64(le64toh(*idx) - 1);
892 r = link_entry_into_array(f, first, &i, p);
897 *idx = htole64(le64toh(*idx) + 1);
901 static int journal_file_link_entry_item(JournalFile *f, Object *o, uint64_t offset, uint64_t i) {
908 p = le64toh(o->entry.items[i].object_offset);
912 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
916 return link_entry_into_array_plus_one(f,
917 &o->data.entry_offset,
918 &o->data.entry_array_offset,
923 static int journal_file_link_entry(JournalFile *f, Object *o, uint64_t offset) {
930 assert(o->object.type == OBJECT_ENTRY);
932 __sync_synchronize();
934 /* Link up the entry itself */
935 r = link_entry_into_array(f,
936 &f->header->entry_array_offset,
937 &f->header->n_entries,
942 /* log_debug("=> %s seqnr=%lu n_entries=%lu", f->path, (unsigned long) o->entry.seqnum, (unsigned long) f->header->n_entries); */
944 if (f->header->head_entry_realtime == 0)
945 f->header->head_entry_realtime = o->entry.realtime;
947 f->header->tail_entry_realtime = o->entry.realtime;
948 f->header->tail_entry_monotonic = o->entry.monotonic;
950 f->tail_entry_monotonic_valid = true;
952 /* Link up the items */
953 n = journal_file_entry_n_items(o);
954 for (i = 0; i < n; i++) {
955 r = journal_file_link_entry_item(f, o, offset, i);
963 static int journal_file_append_entry_internal(
965 const dual_timestamp *ts,
967 const EntryItem items[], unsigned n_items,
969 Object **ret, uint64_t *offset) {
976 assert(items || n_items == 0);
979 osize = offsetof(Object, entry.items) + (n_items * sizeof(EntryItem));
981 r = journal_file_append_object(f, OBJECT_ENTRY, osize, &o, &np);
985 o->entry.seqnum = htole64(journal_file_entry_seqnum(f, seqnum));
986 memcpy(o->entry.items, items, n_items * sizeof(EntryItem));
987 o->entry.realtime = htole64(ts->realtime);
988 o->entry.monotonic = htole64(ts->monotonic);
989 o->entry.xor_hash = htole64(xor_hash);
990 o->entry.boot_id = f->header->boot_id;
992 r = journal_file_hmac_put_object(f, OBJECT_ENTRY, np);
996 r = journal_file_link_entry(f, o, np);
1009 void journal_file_post_change(JournalFile *f) {
1012 /* inotify() does not receive IN_MODIFY events from file
1013 * accesses done via mmap(). After each access we hence
1014 * trigger IN_MODIFY by truncating the journal file to its
1015 * current size which triggers IN_MODIFY. */
1017 __sync_synchronize();
1019 if (ftruncate(f->fd, f->last_stat.st_size) < 0)
1020 log_error("Failed to to truncate file to its own size: %m");
1023 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) {
1027 uint64_t xor_hash = 0;
1028 struct dual_timestamp _ts;
1031 assert(iovec || n_iovec == 0);
1037 dual_timestamp_get(&_ts);
1041 if (f->tail_entry_monotonic_valid &&
1042 ts->monotonic < le64toh(f->header->tail_entry_monotonic))
1045 r = journal_file_maybe_append_tag(f, ts->realtime);
1049 /* alloca() can't take 0, hence let's allocate at least one */
1050 items = alloca(sizeof(EntryItem) * MAX(1, n_iovec));
1052 for (i = 0; i < n_iovec; i++) {
1056 r = journal_file_append_data(f, iovec[i].iov_base, iovec[i].iov_len, &o, &p);
1060 xor_hash ^= le64toh(o->data.hash);
1061 items[i].object_offset = htole64(p);
1062 items[i].hash = o->data.hash;
1065 r = journal_file_append_entry_internal(f, ts, xor_hash, items, n_iovec, seqnum, ret, offset);
1067 journal_file_post_change(f);
1072 static int generic_array_get(JournalFile *f,
1075 Object **ret, uint64_t *offset) {
1087 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
1091 n = journal_file_entry_array_n_items(o);
1093 p = le64toh(o->entry_array.items[i]);
1098 a = le64toh(o->entry_array.next_entry_array_offset);
1101 if (a <= 0 || p <= 0)
1104 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
1117 static int generic_array_get_plus_one(JournalFile *f,
1121 Object **ret, uint64_t *offset) {
1130 r = journal_file_move_to_object(f, OBJECT_ENTRY, extra, &o);
1143 return generic_array_get(f, first, i-1, ret, offset);
1152 static int generic_array_bisect(JournalFile *f,
1156 int (*test_object)(JournalFile *f, uint64_t p, uint64_t needle),
1157 direction_t direction,
1162 uint64_t a, p, t = 0, i = 0, last_p = 0;
1163 bool subtract_one = false;
1164 Object *o, *array = NULL;
1168 assert(test_object);
1172 uint64_t left, right, k, lp;
1174 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &array);
1178 k = journal_file_entry_array_n_items(array);
1184 lp = p = le64toh(array->entry_array.items[i]);
1188 r = test_object(f, p, needle);
1192 if (r == TEST_FOUND)
1193 r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT;
1195 if (r == TEST_RIGHT) {
1199 if (left == right) {
1200 if (direction == DIRECTION_UP)
1201 subtract_one = true;
1207 assert(left < right);
1209 i = (left + right) / 2;
1210 p = le64toh(array->entry_array.items[i]);
1214 r = test_object(f, p, needle);
1218 if (r == TEST_FOUND)
1219 r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT;
1221 if (r == TEST_RIGHT)
1229 if (direction == DIRECTION_UP) {
1231 subtract_one = true;
1242 a = le64toh(array->entry_array.next_entry_array_offset);
1248 if (subtract_one && t == 0 && i == 0)
1251 if (subtract_one && i == 0)
1253 else if (subtract_one)
1254 p = le64toh(array->entry_array.items[i-1]);
1256 p = le64toh(array->entry_array.items[i]);
1258 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
1269 *idx = t + i + (subtract_one ? -1 : 0);
1274 static int generic_array_bisect_plus_one(JournalFile *f,
1279 int (*test_object)(JournalFile *f, uint64_t p, uint64_t needle),
1280 direction_t direction,
1286 bool step_back = false;
1290 assert(test_object);
1295 /* This bisects the array in object 'first', but first checks
1297 r = test_object(f, extra, needle);
1301 if (r == TEST_FOUND)
1302 r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT;
1304 /* if we are looking with DIRECTION_UP then we need to first
1305 see if in the actual array there is a matching entry, and
1306 return the last one of that. But if there isn't any we need
1307 to return this one. Hence remember this, and return it
1310 step_back = direction == DIRECTION_UP;
1312 if (r == TEST_RIGHT) {
1313 if (direction == DIRECTION_DOWN)
1319 r = generic_array_bisect(f, first, n-1, needle, test_object, direction, ret, offset, idx);
1321 if (r == 0 && step_back)
1330 r = journal_file_move_to_object(f, OBJECT_ENTRY, extra, &o);
1346 static int test_object_offset(JournalFile *f, uint64_t p, uint64_t needle) {
1352 else if (p < needle)
1358 int journal_file_move_to_entry_by_offset(
1361 direction_t direction,
1365 return generic_array_bisect(f,
1366 le64toh(f->header->entry_array_offset),
1367 le64toh(f->header->n_entries),
1375 static int test_object_seqnum(JournalFile *f, uint64_t p, uint64_t needle) {
1382 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
1386 if (le64toh(o->entry.seqnum) == needle)
1388 else if (le64toh(o->entry.seqnum) < needle)
1394 int journal_file_move_to_entry_by_seqnum(
1397 direction_t direction,
1401 return generic_array_bisect(f,
1402 le64toh(f->header->entry_array_offset),
1403 le64toh(f->header->n_entries),
1410 static int test_object_realtime(JournalFile *f, uint64_t p, uint64_t needle) {
1417 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
1421 if (le64toh(o->entry.realtime) == needle)
1423 else if (le64toh(o->entry.realtime) < needle)
1429 int journal_file_move_to_entry_by_realtime(
1432 direction_t direction,
1436 return generic_array_bisect(f,
1437 le64toh(f->header->entry_array_offset),
1438 le64toh(f->header->n_entries),
1440 test_object_realtime,
1445 static int test_object_monotonic(JournalFile *f, uint64_t p, uint64_t needle) {
1452 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
1456 if (le64toh(o->entry.monotonic) == needle)
1458 else if (le64toh(o->entry.monotonic) < needle)
1464 int journal_file_move_to_entry_by_monotonic(
1468 direction_t direction,
1472 char t[9+32+1] = "_BOOT_ID=";
1478 sd_id128_to_string(boot_id, t + 9);
1479 r = journal_file_find_data_object(f, t, strlen(t), &o, NULL);
1485 return generic_array_bisect_plus_one(f,
1486 le64toh(o->data.entry_offset),
1487 le64toh(o->data.entry_array_offset),
1488 le64toh(o->data.n_entries),
1490 test_object_monotonic,
1495 int journal_file_next_entry(
1497 Object *o, uint64_t p,
1498 direction_t direction,
1499 Object **ret, uint64_t *offset) {
1505 assert(p > 0 || !o);
1507 n = le64toh(f->header->n_entries);
1512 i = direction == DIRECTION_DOWN ? 0 : n - 1;
1514 if (o->object.type != OBJECT_ENTRY)
1517 r = generic_array_bisect(f,
1518 le64toh(f->header->entry_array_offset),
1519 le64toh(f->header->n_entries),
1528 if (direction == DIRECTION_DOWN) {
1541 /* And jump to it */
1542 return generic_array_get(f,
1543 le64toh(f->header->entry_array_offset),
1548 int journal_file_skip_entry(
1550 Object *o, uint64_t p,
1552 Object **ret, uint64_t *offset) {
1561 if (o->object.type != OBJECT_ENTRY)
1564 r = generic_array_bisect(f,
1565 le64toh(f->header->entry_array_offset),
1566 le64toh(f->header->n_entries),
1575 /* Calculate new index */
1577 if ((uint64_t) -skip >= i)
1580 i = i - (uint64_t) -skip;
1582 i += (uint64_t) skip;
1584 n = le64toh(f->header->n_entries);
1591 return generic_array_get(f,
1592 le64toh(f->header->entry_array_offset),
1597 int journal_file_next_entry_for_data(
1599 Object *o, uint64_t p,
1600 uint64_t data_offset,
1601 direction_t direction,
1602 Object **ret, uint64_t *offset) {
1609 assert(p > 0 || !o);
1611 r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
1615 n = le64toh(d->data.n_entries);
1620 i = direction == DIRECTION_DOWN ? 0 : n - 1;
1622 if (o->object.type != OBJECT_ENTRY)
1625 r = generic_array_bisect_plus_one(f,
1626 le64toh(d->data.entry_offset),
1627 le64toh(d->data.entry_array_offset),
1628 le64toh(d->data.n_entries),
1638 if (direction == DIRECTION_DOWN) {
1652 return generic_array_get_plus_one(f,
1653 le64toh(d->data.entry_offset),
1654 le64toh(d->data.entry_array_offset),
1659 int journal_file_move_to_entry_by_offset_for_data(
1661 uint64_t data_offset,
1663 direction_t direction,
1664 Object **ret, uint64_t *offset) {
1671 r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
1675 return generic_array_bisect_plus_one(f,
1676 le64toh(d->data.entry_offset),
1677 le64toh(d->data.entry_array_offset),
1678 le64toh(d->data.n_entries),
1685 int journal_file_move_to_entry_by_monotonic_for_data(
1687 uint64_t data_offset,
1690 direction_t direction,
1691 Object **ret, uint64_t *offset) {
1693 char t[9+32+1] = "_BOOT_ID=";
1700 /* First, seek by time */
1701 sd_id128_to_string(boot_id, t + 9);
1702 r = journal_file_find_data_object(f, t, strlen(t), &o, &b);
1708 r = generic_array_bisect_plus_one(f,
1709 le64toh(o->data.entry_offset),
1710 le64toh(o->data.entry_array_offset),
1711 le64toh(o->data.n_entries),
1713 test_object_monotonic,
1719 /* And now, continue seeking until we find an entry that
1720 * exists in both bisection arrays */
1726 r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
1730 r = generic_array_bisect_plus_one(f,
1731 le64toh(d->data.entry_offset),
1732 le64toh(d->data.entry_array_offset),
1733 le64toh(d->data.n_entries),
1741 r = journal_file_move_to_object(f, OBJECT_DATA, b, &o);
1745 r = generic_array_bisect_plus_one(f,
1746 le64toh(o->data.entry_offset),
1747 le64toh(o->data.entry_array_offset),
1748 le64toh(o->data.n_entries),
1772 int journal_file_move_to_entry_by_seqnum_for_data(
1774 uint64_t data_offset,
1776 direction_t direction,
1777 Object **ret, uint64_t *offset) {
1784 r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
1788 return generic_array_bisect_plus_one(f,
1789 le64toh(d->data.entry_offset),
1790 le64toh(d->data.entry_array_offset),
1791 le64toh(d->data.n_entries),
1798 int journal_file_move_to_entry_by_realtime_for_data(
1800 uint64_t data_offset,
1802 direction_t direction,
1803 Object **ret, uint64_t *offset) {
1810 r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
1814 return generic_array_bisect_plus_one(f,
1815 le64toh(d->data.entry_offset),
1816 le64toh(d->data.entry_array_offset),
1817 le64toh(d->data.n_entries),
1819 test_object_realtime,
1824 void journal_file_dump(JournalFile *f) {
1831 journal_file_print_header(f);
1833 p = le64toh(f->header->header_size);
1835 r = journal_file_move_to_object(f, -1, p, &o);
1839 switch (o->object.type) {
1842 printf("Type: OBJECT_UNUSED\n");
1846 printf("Type: OBJECT_DATA\n");
1850 printf("Type: OBJECT_ENTRY %llu %llu %llu\n",
1851 (unsigned long long) le64toh(o->entry.seqnum),
1852 (unsigned long long) le64toh(o->entry.monotonic),
1853 (unsigned long long) le64toh(o->entry.realtime));
1856 case OBJECT_FIELD_HASH_TABLE:
1857 printf("Type: OBJECT_FIELD_HASH_TABLE\n");
1860 case OBJECT_DATA_HASH_TABLE:
1861 printf("Type: OBJECT_DATA_HASH_TABLE\n");
1864 case OBJECT_ENTRY_ARRAY:
1865 printf("Type: OBJECT_ENTRY_ARRAY\n");
1869 printf("Type: OBJECT_TAG %llu\n",
1870 (unsigned long long) le64toh(o->tag.seqnum));
1874 if (o->object.flags & OBJECT_COMPRESSED)
1875 printf("Flags: COMPRESSED\n");
1877 if (p == le64toh(f->header->tail_object_offset))
1880 p = p + ALIGN64(le64toh(o->object.size));
1885 log_error("File corrupt");
1888 void journal_file_print_header(JournalFile *f) {
1889 char a[33], b[33], c[33];
1890 char x[FORMAT_TIMESTAMP_MAX], y[FORMAT_TIMESTAMP_MAX];
1894 printf("File Path: %s\n"
1898 "Sequential Number ID: %s\n"
1900 "Compatible Flags:%s%s\n"
1901 "Incompatible Flags:%s%s\n"
1902 "Header size: %llu\n"
1903 "Arena size: %llu\n"
1904 "Data Hash Table Size: %llu\n"
1905 "Field Hash Table Size: %llu\n"
1906 "Rotate Suggested: %s\n"
1907 "Head Sequential Number: %llu\n"
1908 "Tail Sequential Number: %llu\n"
1909 "Head Realtime Timestamp: %s\n"
1910 "Tail Realtime Timestamp: %s\n"
1912 "Entry Objects: %llu\n",
1914 sd_id128_to_string(f->header->file_id, a),
1915 sd_id128_to_string(f->header->machine_id, b),
1916 sd_id128_to_string(f->header->boot_id, c),
1917 sd_id128_to_string(f->header->seqnum_id, c),
1918 f->header->state == STATE_OFFLINE ? "OFFLINE" :
1919 f->header->state == STATE_ONLINE ? "ONLINE" :
1920 f->header->state == STATE_ARCHIVED ? "ARCHIVED" : "UNKNOWN",
1921 (f->header->compatible_flags & HEADER_COMPATIBLE_SEALED) ? " SEALED" : "",
1922 (f->header->compatible_flags & ~HEADER_COMPATIBLE_SEALED) ? " ???" : "",
1923 (f->header->incompatible_flags & HEADER_INCOMPATIBLE_COMPRESSED) ? " COMPRESSED" : "",
1924 (f->header->incompatible_flags & ~HEADER_INCOMPATIBLE_COMPRESSED) ? " ???" : "",
1925 (unsigned long long) le64toh(f->header->header_size),
1926 (unsigned long long) le64toh(f->header->arena_size),
1927 (unsigned long long) le64toh(f->header->data_hash_table_size) / sizeof(HashItem),
1928 (unsigned long long) le64toh(f->header->field_hash_table_size) / sizeof(HashItem),
1929 yes_no(journal_file_rotate_suggested(f)),
1930 (unsigned long long) le64toh(f->header->head_entry_seqnum),
1931 (unsigned long long) le64toh(f->header->tail_entry_seqnum),
1932 format_timestamp(x, sizeof(x), le64toh(f->header->head_entry_realtime)),
1933 format_timestamp(y, sizeof(y), le64toh(f->header->tail_entry_realtime)),
1934 (unsigned long long) le64toh(f->header->n_objects),
1935 (unsigned long long) le64toh(f->header->n_entries));
1937 if (JOURNAL_HEADER_CONTAINS(f->header, n_data))
1938 printf("Data Objects: %llu\n"
1939 "Data Hash Table Fill: %.1f%%\n",
1940 (unsigned long long) le64toh(f->header->n_data),
1941 100.0 * (double) le64toh(f->header->n_data) / ((double) (le64toh(f->header->data_hash_table_size) / sizeof(HashItem))));
1943 if (JOURNAL_HEADER_CONTAINS(f->header, n_fields))
1944 printf("Field Objects: %llu\n"
1945 "Field Hash Table Fill: %.1f%%\n",
1946 (unsigned long long) le64toh(f->header->n_fields),
1947 100.0 * (double) le64toh(f->header->n_fields) / ((double) (le64toh(f->header->field_hash_table_size) / sizeof(HashItem))));
1949 if (JOURNAL_HEADER_CONTAINS(f->header, n_tags))
1950 printf("Tag Objects: %llu\n",
1951 (unsigned long long) le64toh(f->header->n_tags));
1952 if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays))
1953 printf("Entry Array Objects: %llu\n",
1954 (unsigned long long) le64toh(f->header->n_entry_arrays));
1957 int journal_file_open(
1963 JournalMetrics *metrics,
1964 MMapCache *mmap_cache,
1965 JournalFile *template,
1966 JournalFile **ret) {
1970 bool newly_created = false;
1974 if ((flags & O_ACCMODE) != O_RDONLY &&
1975 (flags & O_ACCMODE) != O_RDWR)
1978 if (!endswith(fname, ".journal") &&
1979 !endswith(fname, ".journal~"))
1982 f = new0(JournalFile, 1);
1990 f->prot = prot_from_flags(flags);
1991 f->writable = (flags & O_ACCMODE) != O_RDONLY;
1992 f->compress = compress;
1996 f->mmap = mmap_cache_ref(mmap_cache);
1998 /* One context for each type, plus the zeroth catchall
1999 * context. One fd for the file plus one for each type
2000 * (which we need during verification */
2001 f->mmap = mmap_cache_new(_OBJECT_TYPE_MAX, 1 + _OBJECT_TYPE_MAX);
2008 f->path = strdup(fname);
2014 f->fd = open(f->path, f->flags|O_CLOEXEC, f->mode);
2020 if (fstat(f->fd, &f->last_stat) < 0) {
2025 if (f->last_stat.st_size == 0 && f->writable) {
2026 newly_created = true;
2028 /* Try to load the FSPRG state, and if we can't, then
2029 * just don't do sealing */
2030 r = journal_file_fss_load(f);
2034 r = journal_file_init_header(f, template);
2038 if (fstat(f->fd, &f->last_stat) < 0) {
2044 if (f->last_stat.st_size < (off_t) HEADER_SIZE_MIN) {
2049 f->header = mmap(NULL, PAGE_ALIGN(sizeof(Header)), prot_from_flags(flags), MAP_SHARED, f->fd, 0);
2050 if (f->header == MAP_FAILED) {
2056 if (!newly_created) {
2057 r = journal_file_verify_header(f);
2062 if (!newly_created && f->writable) {
2063 r = journal_file_fss_load(f);
2070 journal_default_metrics(metrics, f->fd);
2071 f->metrics = *metrics;
2072 } else if (template)
2073 f->metrics = template->metrics;
2075 r = journal_file_refresh_header(f);
2080 r = journal_file_hmac_setup(f);
2084 if (newly_created) {
2085 r = journal_file_setup_field_hash_table(f);
2089 r = journal_file_setup_data_hash_table(f);
2093 r = journal_file_append_first_tag(f);
2098 r = journal_file_map_field_hash_table(f);
2102 r = journal_file_map_data_hash_table(f);
2112 journal_file_close(f);
2117 int journal_file_rotate(JournalFile **f, bool compress, bool seal) {
2120 JournalFile *old_file, *new_file = NULL;
2128 if (!old_file->writable)
2131 if (!endswith(old_file->path, ".journal"))
2134 l = strlen(old_file->path);
2136 p = new(char, l + 1 + 32 + 1 + 16 + 1 + 16 + 1);
2140 memcpy(p, old_file->path, l - 8);
2142 sd_id128_to_string(old_file->header->seqnum_id, p + l - 8 + 1);
2143 snprintf(p + l - 8 + 1 + 32, 1 + 16 + 1 + 16 + 8 + 1,
2144 "-%016llx-%016llx.journal",
2145 (unsigned long long) le64toh((*f)->header->tail_entry_seqnum),
2146 (unsigned long long) le64toh((*f)->header->tail_entry_realtime));
2148 r = rename(old_file->path, p);
2154 old_file->header->state = STATE_ARCHIVED;
2156 r = journal_file_open(old_file->path, old_file->flags, old_file->mode, compress, seal, NULL, old_file->mmap, old_file, &new_file);
2157 journal_file_close(old_file);
2163 int journal_file_open_reliably(
2169 JournalMetrics *metrics,
2170 MMapCache *mmap_cache,
2171 JournalFile *template,
2172 JournalFile **ret) {
2178 r = journal_file_open(fname, flags, mode, compress, seal,
2179 metrics, mmap_cache, template, ret);
2180 if (r != -EBADMSG && /* corrupted */
2181 r != -ENODATA && /* truncated */
2182 r != -EHOSTDOWN && /* other machine */
2183 r != -EPROTONOSUPPORT && /* incompatible feature */
2184 r != -EBUSY && /* unclean shutdown */
2185 r != -ESHUTDOWN /* already archived */)
2188 if ((flags & O_ACCMODE) == O_RDONLY)
2191 if (!(flags & O_CREAT))
2194 if (!endswith(fname, ".journal"))
2197 /* The file is corrupted. Rotate it away and try it again (but only once) */
2200 if (asprintf(&p, "%.*s@%016llx-%016llx.journal~",
2202 (unsigned long long) now(CLOCK_REALTIME),
2206 r = rename(fname, p);
2211 log_warning("File %s corrupted or uncleanly shut down, renaming and replacing.", fname);
2213 return journal_file_open(fname, flags, mode, compress, seal,
2214 metrics, mmap_cache, template, ret);
2218 int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint64_t p, uint64_t *seqnum, Object **ret, uint64_t *offset) {
2220 uint64_t q, xor_hash = 0;
2233 ts.monotonic = le64toh(o->entry.monotonic);
2234 ts.realtime = le64toh(o->entry.realtime);
2236 if (to->tail_entry_monotonic_valid &&
2237 ts.monotonic < le64toh(to->header->tail_entry_monotonic))
2240 n = journal_file_entry_n_items(o);
2241 items = alloca(sizeof(EntryItem) * n);
2243 for (i = 0; i < n; i++) {
2250 q = le64toh(o->entry.items[i].object_offset);
2251 le_hash = o->entry.items[i].hash;
2253 r = journal_file_move_to_object(from, OBJECT_DATA, q, &o);
2257 if (le_hash != o->data.hash)
2260 l = le64toh(o->object.size) - offsetof(Object, data.payload);
2263 /* We hit the limit on 32bit machines */
2264 if ((uint64_t) t != l)
2267 if (o->object.flags & OBJECT_COMPRESSED) {
2271 if (!uncompress_blob(o->data.payload, l, &from->compress_buffer, &from->compress_buffer_size, &rsize))
2274 data = from->compress_buffer;
2277 return -EPROTONOSUPPORT;
2280 data = o->data.payload;
2282 r = journal_file_append_data(to, data, l, &u, &h);
2286 xor_hash ^= le64toh(u->data.hash);
2287 items[i].object_offset = htole64(h);
2288 items[i].hash = u->data.hash;
2290 r = journal_file_move_to_object(from, OBJECT_ENTRY, p, &o);
2295 return journal_file_append_entry_internal(to, &ts, xor_hash, items, n, seqnum, ret, offset);
2298 void journal_default_metrics(JournalMetrics *m, int fd) {
2299 uint64_t fs_size = 0;
2301 char a[FORMAT_BYTES_MAX], b[FORMAT_BYTES_MAX], c[FORMAT_BYTES_MAX], d[FORMAT_BYTES_MAX];
2306 if (fstatvfs(fd, &ss) >= 0)
2307 fs_size = ss.f_frsize * ss.f_blocks;
2309 if (m->max_use == (uint64_t) -1) {
2312 m->max_use = PAGE_ALIGN(fs_size / 10); /* 10% of file system size */
2314 if (m->max_use > DEFAULT_MAX_USE_UPPER)
2315 m->max_use = DEFAULT_MAX_USE_UPPER;
2317 if (m->max_use < DEFAULT_MAX_USE_LOWER)
2318 m->max_use = DEFAULT_MAX_USE_LOWER;
2320 m->max_use = DEFAULT_MAX_USE_LOWER;
2322 m->max_use = PAGE_ALIGN(m->max_use);
2324 if (m->max_use < JOURNAL_FILE_SIZE_MIN*2)
2325 m->max_use = JOURNAL_FILE_SIZE_MIN*2;
2328 if (m->max_size == (uint64_t) -1) {
2329 m->max_size = PAGE_ALIGN(m->max_use / 8); /* 8 chunks */
2331 if (m->max_size > DEFAULT_MAX_SIZE_UPPER)
2332 m->max_size = DEFAULT_MAX_SIZE_UPPER;
2334 m->max_size = PAGE_ALIGN(m->max_size);
2336 if (m->max_size < JOURNAL_FILE_SIZE_MIN)
2337 m->max_size = JOURNAL_FILE_SIZE_MIN;
2339 if (m->max_size*2 > m->max_use)
2340 m->max_use = m->max_size*2;
2342 if (m->min_size == (uint64_t) -1)
2343 m->min_size = JOURNAL_FILE_SIZE_MIN;
2345 m->min_size = PAGE_ALIGN(m->min_size);
2347 if (m->min_size < JOURNAL_FILE_SIZE_MIN)
2348 m->min_size = JOURNAL_FILE_SIZE_MIN;
2350 if (m->min_size > m->max_size)
2351 m->max_size = m->min_size;
2354 if (m->keep_free == (uint64_t) -1) {
2357 m->keep_free = PAGE_ALIGN(fs_size / 20); /* 5% of file system size */
2359 if (m->keep_free > DEFAULT_KEEP_FREE_UPPER)
2360 m->keep_free = DEFAULT_KEEP_FREE_UPPER;
2363 m->keep_free = DEFAULT_KEEP_FREE;
2366 log_info("Fixed max_use=%s max_size=%s min_size=%s keep_free=%s",
2367 format_bytes(a, sizeof(a), m->max_use),
2368 format_bytes(b, sizeof(b), m->max_size),
2369 format_bytes(c, sizeof(c), m->min_size),
2370 format_bytes(d, sizeof(d), m->keep_free));
2373 int journal_file_get_cutoff_realtime_usec(JournalFile *f, usec_t *from, usec_t *to) {
2378 if (f->header->head_entry_realtime == 0)
2381 *from = le64toh(f->header->head_entry_realtime);
2385 if (f->header->tail_entry_realtime == 0)
2388 *to = le64toh(f->header->tail_entry_realtime);
2394 int journal_file_get_cutoff_monotonic_usec(JournalFile *f, sd_id128_t boot_id, usec_t *from, usec_t *to) {
2395 char t[9+32+1] = "_BOOT_ID=";
2403 sd_id128_to_string(boot_id, t + 9);
2405 r = journal_file_find_data_object(f, t, strlen(t), &o, &p);
2409 if (le64toh(o->data.n_entries) <= 0)
2413 r = journal_file_move_to_object(f, OBJECT_ENTRY, le64toh(o->data.entry_offset), &o);
2417 *from = le64toh(o->entry.monotonic);
2421 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
2425 r = generic_array_get_plus_one(f,
2426 le64toh(o->data.entry_offset),
2427 le64toh(o->data.entry_array_offset),
2428 le64toh(o->data.n_entries)-1,
2433 *to = le64toh(o->entry.monotonic);
2439 bool journal_file_rotate_suggested(JournalFile *f) {
2442 /* If we gained new header fields we gained new features,
2443 * hence suggest a rotation */
2444 if (le64toh(f->header->header_size) < sizeof(Header)) {
2445 log_debug("%s uses an outdated header, suggesting rotation.", f->path);
2449 /* Let's check if the hash tables grew over a certain fill
2450 * level (75%, borrowing this value from Java's hash table
2451 * implementation), and if so suggest a rotation. To calculate
2452 * the fill level we need the n_data field, which only exists
2453 * in newer versions. */
2455 if (JOURNAL_HEADER_CONTAINS(f->header, n_data))
2456 if (le64toh(f->header->n_data) * 4ULL > (le64toh(f->header->data_hash_table_size) / sizeof(HashItem)) * 3ULL) {
2457 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.",
2459 100.0 * (double) le64toh(f->header->n_data) / ((double) (le64toh(f->header->data_hash_table_size) / sizeof(HashItem))),
2460 (unsigned long long) le64toh(f->header->n_data),
2461 (unsigned long long) (le64toh(f->header->data_hash_table_size) / sizeof(HashItem)),
2462 (unsigned long long) (f->last_stat.st_size),
2463 (unsigned long long) (f->last_stat.st_size / le64toh(f->header->n_data)));
2467 if (JOURNAL_HEADER_CONTAINS(f->header, n_fields))
2468 if (le64toh(f->header->n_fields) * 4ULL > (le64toh(f->header->field_hash_table_size) / sizeof(HashItem)) * 3ULL) {
2469 log_debug("Field hash table of %s has a fill level at %.1f (%llu of %llu items), suggesting rotation.",
2471 100.0 * (double) le64toh(f->header->n_fields) / ((double) (le64toh(f->header->field_hash_table_size) / sizeof(HashItem))),
2472 (unsigned long long) le64toh(f->header->n_fields),
2473 (unsigned long long) (le64toh(f->header->field_hash_table_size) / sizeof(HashItem)));