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->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);
255 f->seal = !!(le32toh(f->header->compatible_flags) & HEADER_COMPATIBLE_SEALED);
260 static int journal_file_allocate(JournalFile *f, uint64_t offset, uint64_t size) {
261 uint64_t old_size, new_size;
266 /* We assume that this file is not sparse, and we know that
267 * for sure, since we always call posix_fallocate()
271 le64toh(f->header->header_size) +
272 le64toh(f->header->arena_size);
274 new_size = PAGE_ALIGN(offset + size);
275 if (new_size < le64toh(f->header->header_size))
276 new_size = le64toh(f->header->header_size);
278 if (new_size <= old_size)
281 if (f->metrics.max_size > 0 &&
282 new_size > f->metrics.max_size)
285 if (new_size > f->metrics.min_size &&
286 f->metrics.keep_free > 0) {
289 if (fstatvfs(f->fd, &svfs) >= 0) {
292 available = svfs.f_bfree * svfs.f_bsize;
294 if (available >= f->metrics.keep_free)
295 available -= f->metrics.keep_free;
299 if (new_size - old_size > available)
304 /* Note that the glibc fallocate() fallback is very
305 inefficient, hence we try to minimize the allocation area
307 r = posix_fallocate(f->fd, old_size, new_size - old_size);
311 mmap_cache_close_fd_range(f->mmap, f->fd, old_size);
313 if (fstat(f->fd, &f->last_stat) < 0)
316 f->header->arena_size = htole64(new_size - le64toh(f->header->header_size));
321 static int journal_file_move_to(JournalFile *f, int context, uint64_t offset, uint64_t size, void **ret) {
325 /* Avoid SIGBUS on invalid accesses */
326 if (offset + size > (uint64_t) f->last_stat.st_size) {
327 /* Hmm, out of range? Let's refresh the fstat() data
328 * first, before we trust that check. */
330 if (fstat(f->fd, &f->last_stat) < 0 ||
331 offset + size > (uint64_t) f->last_stat.st_size)
332 return -EADDRNOTAVAIL;
335 return mmap_cache_get(f->mmap, f->fd, f->prot, context, offset, size, ret);
338 static uint64_t minimum_header_size(Object *o) {
340 static uint64_t table[] = {
341 [OBJECT_DATA] = sizeof(DataObject),
342 [OBJECT_FIELD] = sizeof(FieldObject),
343 [OBJECT_ENTRY] = sizeof(EntryObject),
344 [OBJECT_DATA_HASH_TABLE] = sizeof(HashTableObject),
345 [OBJECT_FIELD_HASH_TABLE] = sizeof(HashTableObject),
346 [OBJECT_ENTRY_ARRAY] = sizeof(EntryArrayObject),
347 [OBJECT_TAG] = sizeof(TagObject),
350 if (o->object.type >= ELEMENTSOF(table) || table[o->object.type] <= 0)
351 return sizeof(ObjectHeader);
353 return table[o->object.type];
356 int journal_file_move_to_object(JournalFile *f, int type, uint64_t offset, Object **ret) {
366 /* Objects may only be located at multiple of 64 bit */
367 if (!VALID64(offset))
370 /* One context for each type, plus one catch-all for the rest */
371 context = type > 0 && type < _OBJECT_TYPE_MAX ? type : 0;
373 r = journal_file_move_to(f, context, offset, sizeof(ObjectHeader), &t);
378 s = le64toh(o->object.size);
380 if (s < sizeof(ObjectHeader))
383 if (o->object.type <= OBJECT_UNUSED)
386 if (s < minimum_header_size(o))
389 if (type >= 0 && o->object.type != type)
392 if (s > sizeof(ObjectHeader)) {
393 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 if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays))
863 f->header->n_entry_arrays = htole64(le64toh(f->header->n_entry_arrays) + 1);
865 *idx = htole64(hidx + 1);
870 static int link_entry_into_array_plus_one(JournalFile *f,
889 i = htole64(le64toh(*idx) - 1);
890 r = link_entry_into_array(f, first, &i, p);
895 *idx = htole64(le64toh(*idx) + 1);
899 static int journal_file_link_entry_item(JournalFile *f, Object *o, uint64_t offset, uint64_t i) {
906 p = le64toh(o->entry.items[i].object_offset);
910 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
914 return link_entry_into_array_plus_one(f,
915 &o->data.entry_offset,
916 &o->data.entry_array_offset,
921 static int journal_file_link_entry(JournalFile *f, Object *o, uint64_t offset) {
928 assert(o->object.type == OBJECT_ENTRY);
930 __sync_synchronize();
932 /* Link up the entry itself */
933 r = link_entry_into_array(f,
934 &f->header->entry_array_offset,
935 &f->header->n_entries,
940 /* log_debug("=> %s seqnr=%lu n_entries=%lu", f->path, (unsigned long) o->entry.seqnum, (unsigned long) f->header->n_entries); */
942 if (f->header->head_entry_realtime == 0)
943 f->header->head_entry_realtime = o->entry.realtime;
945 f->header->tail_entry_realtime = o->entry.realtime;
946 f->header->tail_entry_monotonic = o->entry.monotonic;
948 f->tail_entry_monotonic_valid = true;
950 /* Link up the items */
951 n = journal_file_entry_n_items(o);
952 for (i = 0; i < n; i++) {
953 r = journal_file_link_entry_item(f, o, offset, i);
961 static int journal_file_append_entry_internal(
963 const dual_timestamp *ts,
965 const EntryItem items[], unsigned n_items,
967 Object **ret, uint64_t *offset) {
974 assert(items || n_items == 0);
977 osize = offsetof(Object, entry.items) + (n_items * sizeof(EntryItem));
979 r = journal_file_append_object(f, OBJECT_ENTRY, osize, &o, &np);
983 o->entry.seqnum = htole64(journal_file_entry_seqnum(f, seqnum));
984 memcpy(o->entry.items, items, n_items * sizeof(EntryItem));
985 o->entry.realtime = htole64(ts->realtime);
986 o->entry.monotonic = htole64(ts->monotonic);
987 o->entry.xor_hash = htole64(xor_hash);
988 o->entry.boot_id = f->header->boot_id;
990 r = journal_file_hmac_put_object(f, OBJECT_ENTRY, np);
994 r = journal_file_link_entry(f, o, np);
1007 void journal_file_post_change(JournalFile *f) {
1010 /* inotify() does not receive IN_MODIFY events from file
1011 * accesses done via mmap(). After each access we hence
1012 * trigger IN_MODIFY by truncating the journal file to its
1013 * current size which triggers IN_MODIFY. */
1015 __sync_synchronize();
1017 if (ftruncate(f->fd, f->last_stat.st_size) < 0)
1018 log_error("Failed to to truncate file to its own size: %m");
1021 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) {
1025 uint64_t xor_hash = 0;
1026 struct dual_timestamp _ts;
1029 assert(iovec || n_iovec == 0);
1035 dual_timestamp_get(&_ts);
1039 if (f->tail_entry_monotonic_valid &&
1040 ts->monotonic < le64toh(f->header->tail_entry_monotonic))
1043 r = journal_file_maybe_append_tag(f, ts->realtime);
1047 /* alloca() can't take 0, hence let's allocate at least one */
1048 items = alloca(sizeof(EntryItem) * MAX(1, n_iovec));
1050 for (i = 0; i < n_iovec; i++) {
1054 r = journal_file_append_data(f, iovec[i].iov_base, iovec[i].iov_len, &o, &p);
1058 xor_hash ^= le64toh(o->data.hash);
1059 items[i].object_offset = htole64(p);
1060 items[i].hash = o->data.hash;
1063 r = journal_file_append_entry_internal(f, ts, xor_hash, items, n_iovec, seqnum, ret, offset);
1065 journal_file_post_change(f);
1070 static int generic_array_get(JournalFile *f,
1073 Object **ret, uint64_t *offset) {
1085 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
1089 n = journal_file_entry_array_n_items(o);
1091 p = le64toh(o->entry_array.items[i]);
1096 a = le64toh(o->entry_array.next_entry_array_offset);
1099 if (a <= 0 || p <= 0)
1102 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
1115 static int generic_array_get_plus_one(JournalFile *f,
1119 Object **ret, uint64_t *offset) {
1128 r = journal_file_move_to_object(f, OBJECT_ENTRY, extra, &o);
1141 return generic_array_get(f, first, i-1, ret, offset);
1150 static int generic_array_bisect(JournalFile *f,
1154 int (*test_object)(JournalFile *f, uint64_t p, uint64_t needle),
1155 direction_t direction,
1160 uint64_t a, p, t = 0, i = 0, last_p = 0;
1161 bool subtract_one = false;
1162 Object *o, *array = NULL;
1166 assert(test_object);
1170 uint64_t left, right, k, lp;
1172 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &array);
1176 k = journal_file_entry_array_n_items(array);
1182 lp = p = le64toh(array->entry_array.items[i]);
1186 r = test_object(f, p, needle);
1190 if (r == TEST_FOUND)
1191 r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT;
1193 if (r == TEST_RIGHT) {
1197 if (left == right) {
1198 if (direction == DIRECTION_UP)
1199 subtract_one = true;
1205 assert(left < right);
1207 i = (left + right) / 2;
1208 p = le64toh(array->entry_array.items[i]);
1212 r = test_object(f, p, needle);
1216 if (r == TEST_FOUND)
1217 r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT;
1219 if (r == TEST_RIGHT)
1227 if (direction == DIRECTION_UP) {
1229 subtract_one = true;
1240 a = le64toh(array->entry_array.next_entry_array_offset);
1246 if (subtract_one && t == 0 && i == 0)
1249 if (subtract_one && i == 0)
1251 else if (subtract_one)
1252 p = le64toh(array->entry_array.items[i-1]);
1254 p = le64toh(array->entry_array.items[i]);
1256 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
1267 *idx = t + i + (subtract_one ? -1 : 0);
1272 static int generic_array_bisect_plus_one(JournalFile *f,
1277 int (*test_object)(JournalFile *f, uint64_t p, uint64_t needle),
1278 direction_t direction,
1284 bool step_back = false;
1288 assert(test_object);
1293 /* This bisects the array in object 'first', but first checks
1295 r = test_object(f, extra, needle);
1299 if (r == TEST_FOUND)
1300 r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT;
1302 /* if we are looking with DIRECTION_UP then we need to first
1303 see if in the actual array there is a matching entry, and
1304 return the last one of that. But if there isn't any we need
1305 to return this one. Hence remember this, and return it
1308 step_back = direction == DIRECTION_UP;
1310 if (r == TEST_RIGHT) {
1311 if (direction == DIRECTION_DOWN)
1317 r = generic_array_bisect(f, first, n-1, needle, test_object, direction, ret, offset, idx);
1319 if (r == 0 && step_back)
1328 r = journal_file_move_to_object(f, OBJECT_ENTRY, extra, &o);
1344 static int test_object_offset(JournalFile *f, uint64_t p, uint64_t needle) {
1350 else if (p < needle)
1356 int journal_file_move_to_entry_by_offset(
1359 direction_t direction,
1363 return generic_array_bisect(f,
1364 le64toh(f->header->entry_array_offset),
1365 le64toh(f->header->n_entries),
1373 static int test_object_seqnum(JournalFile *f, uint64_t p, uint64_t needle) {
1380 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
1384 if (le64toh(o->entry.seqnum) == needle)
1386 else if (le64toh(o->entry.seqnum) < needle)
1392 int journal_file_move_to_entry_by_seqnum(
1395 direction_t direction,
1399 return generic_array_bisect(f,
1400 le64toh(f->header->entry_array_offset),
1401 le64toh(f->header->n_entries),
1408 static int test_object_realtime(JournalFile *f, uint64_t p, uint64_t needle) {
1415 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
1419 if (le64toh(o->entry.realtime) == needle)
1421 else if (le64toh(o->entry.realtime) < needle)
1427 int journal_file_move_to_entry_by_realtime(
1430 direction_t direction,
1434 return generic_array_bisect(f,
1435 le64toh(f->header->entry_array_offset),
1436 le64toh(f->header->n_entries),
1438 test_object_realtime,
1443 static int test_object_monotonic(JournalFile *f, uint64_t p, uint64_t needle) {
1450 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
1454 if (le64toh(o->entry.monotonic) == needle)
1456 else if (le64toh(o->entry.monotonic) < needle)
1462 int journal_file_move_to_entry_by_monotonic(
1466 direction_t direction,
1470 char t[9+32+1] = "_BOOT_ID=";
1476 sd_id128_to_string(boot_id, t + 9);
1477 r = journal_file_find_data_object(f, t, strlen(t), &o, NULL);
1483 return generic_array_bisect_plus_one(f,
1484 le64toh(o->data.entry_offset),
1485 le64toh(o->data.entry_array_offset),
1486 le64toh(o->data.n_entries),
1488 test_object_monotonic,
1493 int journal_file_next_entry(
1495 Object *o, uint64_t p,
1496 direction_t direction,
1497 Object **ret, uint64_t *offset) {
1503 assert(p > 0 || !o);
1505 n = le64toh(f->header->n_entries);
1510 i = direction == DIRECTION_DOWN ? 0 : n - 1;
1512 if (o->object.type != OBJECT_ENTRY)
1515 r = generic_array_bisect(f,
1516 le64toh(f->header->entry_array_offset),
1517 le64toh(f->header->n_entries),
1526 if (direction == DIRECTION_DOWN) {
1539 /* And jump to it */
1540 return generic_array_get(f,
1541 le64toh(f->header->entry_array_offset),
1546 int journal_file_skip_entry(
1548 Object *o, uint64_t p,
1550 Object **ret, uint64_t *offset) {
1559 if (o->object.type != OBJECT_ENTRY)
1562 r = generic_array_bisect(f,
1563 le64toh(f->header->entry_array_offset),
1564 le64toh(f->header->n_entries),
1573 /* Calculate new index */
1575 if ((uint64_t) -skip >= i)
1578 i = i - (uint64_t) -skip;
1580 i += (uint64_t) skip;
1582 n = le64toh(f->header->n_entries);
1589 return generic_array_get(f,
1590 le64toh(f->header->entry_array_offset),
1595 int journal_file_next_entry_for_data(
1597 Object *o, uint64_t p,
1598 uint64_t data_offset,
1599 direction_t direction,
1600 Object **ret, uint64_t *offset) {
1607 assert(p > 0 || !o);
1609 r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
1613 n = le64toh(d->data.n_entries);
1618 i = direction == DIRECTION_DOWN ? 0 : n - 1;
1620 if (o->object.type != OBJECT_ENTRY)
1623 r = generic_array_bisect_plus_one(f,
1624 le64toh(d->data.entry_offset),
1625 le64toh(d->data.entry_array_offset),
1626 le64toh(d->data.n_entries),
1636 if (direction == DIRECTION_DOWN) {
1650 return generic_array_get_plus_one(f,
1651 le64toh(d->data.entry_offset),
1652 le64toh(d->data.entry_array_offset),
1657 int journal_file_move_to_entry_by_offset_for_data(
1659 uint64_t data_offset,
1661 direction_t direction,
1662 Object **ret, uint64_t *offset) {
1669 r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
1673 return generic_array_bisect_plus_one(f,
1674 le64toh(d->data.entry_offset),
1675 le64toh(d->data.entry_array_offset),
1676 le64toh(d->data.n_entries),
1683 int journal_file_move_to_entry_by_monotonic_for_data(
1685 uint64_t data_offset,
1688 direction_t direction,
1689 Object **ret, uint64_t *offset) {
1691 char t[9+32+1] = "_BOOT_ID=";
1698 /* First, seek by time */
1699 sd_id128_to_string(boot_id, t + 9);
1700 r = journal_file_find_data_object(f, t, strlen(t), &o, &b);
1706 r = generic_array_bisect_plus_one(f,
1707 le64toh(o->data.entry_offset),
1708 le64toh(o->data.entry_array_offset),
1709 le64toh(o->data.n_entries),
1711 test_object_monotonic,
1717 /* And now, continue seeking until we find an entry that
1718 * exists in both bisection arrays */
1724 r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
1728 r = generic_array_bisect_plus_one(f,
1729 le64toh(d->data.entry_offset),
1730 le64toh(d->data.entry_array_offset),
1731 le64toh(d->data.n_entries),
1739 r = journal_file_move_to_object(f, OBJECT_DATA, b, &o);
1743 r = generic_array_bisect_plus_one(f,
1744 le64toh(o->data.entry_offset),
1745 le64toh(o->data.entry_array_offset),
1746 le64toh(o->data.n_entries),
1770 int journal_file_move_to_entry_by_seqnum_for_data(
1772 uint64_t data_offset,
1774 direction_t direction,
1775 Object **ret, uint64_t *offset) {
1782 r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
1786 return generic_array_bisect_plus_one(f,
1787 le64toh(d->data.entry_offset),
1788 le64toh(d->data.entry_array_offset),
1789 le64toh(d->data.n_entries),
1796 int journal_file_move_to_entry_by_realtime_for_data(
1798 uint64_t data_offset,
1800 direction_t direction,
1801 Object **ret, uint64_t *offset) {
1808 r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
1812 return generic_array_bisect_plus_one(f,
1813 le64toh(d->data.entry_offset),
1814 le64toh(d->data.entry_array_offset),
1815 le64toh(d->data.n_entries),
1817 test_object_realtime,
1822 void journal_file_dump(JournalFile *f) {
1829 journal_file_print_header(f);
1831 p = le64toh(f->header->header_size);
1833 r = journal_file_move_to_object(f, -1, p, &o);
1837 switch (o->object.type) {
1840 printf("Type: OBJECT_UNUSED\n");
1844 printf("Type: OBJECT_DATA\n");
1848 printf("Type: OBJECT_ENTRY %llu %llu %llu\n",
1849 (unsigned long long) le64toh(o->entry.seqnum),
1850 (unsigned long long) le64toh(o->entry.monotonic),
1851 (unsigned long long) le64toh(o->entry.realtime));
1854 case OBJECT_FIELD_HASH_TABLE:
1855 printf("Type: OBJECT_FIELD_HASH_TABLE\n");
1858 case OBJECT_DATA_HASH_TABLE:
1859 printf("Type: OBJECT_DATA_HASH_TABLE\n");
1862 case OBJECT_ENTRY_ARRAY:
1863 printf("Type: OBJECT_ENTRY_ARRAY\n");
1867 printf("Type: OBJECT_TAG %llu\n",
1868 (unsigned long long) le64toh(o->tag.seqnum));
1872 if (o->object.flags & OBJECT_COMPRESSED)
1873 printf("Flags: COMPRESSED\n");
1875 if (p == le64toh(f->header->tail_object_offset))
1878 p = p + ALIGN64(le64toh(o->object.size));
1883 log_error("File corrupt");
1886 void journal_file_print_header(JournalFile *f) {
1887 char a[33], b[33], c[33];
1888 char x[FORMAT_TIMESTAMP_MAX], y[FORMAT_TIMESTAMP_MAX];
1892 printf("File Path: %s\n"
1896 "Sequential Number ID: %s\n"
1898 "Compatible Flags:%s%s\n"
1899 "Incompatible Flags:%s%s\n"
1900 "Header size: %llu\n"
1901 "Arena size: %llu\n"
1902 "Data Hash Table Size: %llu\n"
1903 "Field Hash Table Size: %llu\n"
1904 "Rotate Suggested: %s\n"
1905 "Head Sequential Number: %llu\n"
1906 "Tail Sequential Number: %llu\n"
1907 "Head Realtime Timestamp: %s\n"
1908 "Tail Realtime Timestamp: %s\n"
1910 "Entry Objects: %llu\n",
1912 sd_id128_to_string(f->header->file_id, a),
1913 sd_id128_to_string(f->header->machine_id, b),
1914 sd_id128_to_string(f->header->boot_id, c),
1915 sd_id128_to_string(f->header->seqnum_id, c),
1916 f->header->state == STATE_OFFLINE ? "OFFLINE" :
1917 f->header->state == STATE_ONLINE ? "ONLINE" :
1918 f->header->state == STATE_ARCHIVED ? "ARCHIVED" : "UNKNOWN",
1919 (f->header->compatible_flags & HEADER_COMPATIBLE_SEALED) ? " SEALED" : "",
1920 (f->header->compatible_flags & ~HEADER_COMPATIBLE_SEALED) ? " ???" : "",
1921 (f->header->incompatible_flags & HEADER_INCOMPATIBLE_COMPRESSED) ? " COMPRESSED" : "",
1922 (f->header->incompatible_flags & ~HEADER_INCOMPATIBLE_COMPRESSED) ? " ???" : "",
1923 (unsigned long long) le64toh(f->header->header_size),
1924 (unsigned long long) le64toh(f->header->arena_size),
1925 (unsigned long long) le64toh(f->header->data_hash_table_size) / sizeof(HashItem),
1926 (unsigned long long) le64toh(f->header->field_hash_table_size) / sizeof(HashItem),
1927 yes_no(journal_file_rotate_suggested(f)),
1928 (unsigned long long) le64toh(f->header->head_entry_seqnum),
1929 (unsigned long long) le64toh(f->header->tail_entry_seqnum),
1930 format_timestamp(x, sizeof(x), le64toh(f->header->head_entry_realtime)),
1931 format_timestamp(y, sizeof(y), le64toh(f->header->tail_entry_realtime)),
1932 (unsigned long long) le64toh(f->header->n_objects),
1933 (unsigned long long) le64toh(f->header->n_entries));
1935 if (JOURNAL_HEADER_CONTAINS(f->header, n_data))
1936 printf("Data Objects: %llu\n"
1937 "Data Hash Table Fill: %.1f%%\n",
1938 (unsigned long long) le64toh(f->header->n_data),
1939 100.0 * (double) le64toh(f->header->n_data) / ((double) (le64toh(f->header->data_hash_table_size) / sizeof(HashItem))));
1941 if (JOURNAL_HEADER_CONTAINS(f->header, n_fields))
1942 printf("Field Objects: %llu\n"
1943 "Field Hash Table Fill: %.1f%%\n",
1944 (unsigned long long) le64toh(f->header->n_fields),
1945 100.0 * (double) le64toh(f->header->n_fields) / ((double) (le64toh(f->header->field_hash_table_size) / sizeof(HashItem))));
1947 if (JOURNAL_HEADER_CONTAINS(f->header, n_tags))
1948 printf("Tag Objects: %llu\n",
1949 (unsigned long long) le64toh(f->header->n_tags));
1950 if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays))
1951 printf("Entry Array Objects: %llu\n",
1952 (unsigned long long) le64toh(f->header->n_entry_arrays));
1955 int journal_file_open(
1961 JournalMetrics *metrics,
1962 MMapCache *mmap_cache,
1963 JournalFile *template,
1964 JournalFile **ret) {
1968 bool newly_created = false;
1972 if ((flags & O_ACCMODE) != O_RDONLY &&
1973 (flags & O_ACCMODE) != O_RDWR)
1976 if (!endswith(fname, ".journal") &&
1977 !endswith(fname, ".journal~"))
1980 f = new0(JournalFile, 1);
1988 f->prot = prot_from_flags(flags);
1989 f->writable = (flags & O_ACCMODE) != O_RDONLY;
1990 f->compress = compress;
1994 f->mmap = mmap_cache_ref(mmap_cache);
1996 /* One context for each type, plus the zeroth catchall
1997 * context. One fd for the file plus one for each type
1998 * (which we need during verification */
1999 f->mmap = mmap_cache_new(_OBJECT_TYPE_MAX, 1 + _OBJECT_TYPE_MAX);
2006 f->path = strdup(fname);
2012 f->fd = open(f->path, f->flags|O_CLOEXEC, f->mode);
2018 if (fstat(f->fd, &f->last_stat) < 0) {
2023 if (f->last_stat.st_size == 0 && f->writable) {
2024 newly_created = true;
2026 /* Try to load the FSPRG state, and if we can't, then
2027 * just don't do sealing */
2028 r = journal_file_fss_load(f);
2032 r = journal_file_init_header(f, template);
2036 if (fstat(f->fd, &f->last_stat) < 0) {
2042 if (f->last_stat.st_size < (off_t) HEADER_SIZE_MIN) {
2047 f->header = mmap(NULL, PAGE_ALIGN(sizeof(Header)), prot_from_flags(flags), MAP_SHARED, f->fd, 0);
2048 if (f->header == MAP_FAILED) {
2054 if (!newly_created) {
2055 r = journal_file_verify_header(f);
2060 if (!newly_created && f->writable) {
2061 r = journal_file_fss_load(f);
2068 journal_default_metrics(metrics, f->fd);
2069 f->metrics = *metrics;
2070 } else if (template)
2071 f->metrics = template->metrics;
2073 r = journal_file_refresh_header(f);
2078 r = journal_file_hmac_setup(f);
2082 if (newly_created) {
2083 r = journal_file_setup_field_hash_table(f);
2087 r = journal_file_setup_data_hash_table(f);
2091 r = journal_file_append_first_tag(f);
2096 r = journal_file_map_field_hash_table(f);
2100 r = journal_file_map_data_hash_table(f);
2110 journal_file_close(f);
2115 int journal_file_rotate(JournalFile **f, bool compress, bool seal) {
2118 JournalFile *old_file, *new_file = NULL;
2126 if (!old_file->writable)
2129 if (!endswith(old_file->path, ".journal"))
2132 l = strlen(old_file->path);
2134 p = new(char, l + 1 + 32 + 1 + 16 + 1 + 16 + 1);
2138 memcpy(p, old_file->path, l - 8);
2140 sd_id128_to_string(old_file->header->seqnum_id, p + l - 8 + 1);
2141 snprintf(p + l - 8 + 1 + 32, 1 + 16 + 1 + 16 + 8 + 1,
2142 "-%016llx-%016llx.journal",
2143 (unsigned long long) le64toh((*f)->header->tail_entry_seqnum),
2144 (unsigned long long) le64toh((*f)->header->tail_entry_realtime));
2146 r = rename(old_file->path, p);
2152 old_file->header->state = STATE_ARCHIVED;
2154 r = journal_file_open(old_file->path, old_file->flags, old_file->mode, compress, seal, NULL, old_file->mmap, old_file, &new_file);
2155 journal_file_close(old_file);
2161 int journal_file_open_reliably(
2167 JournalMetrics *metrics,
2168 MMapCache *mmap_cache,
2169 JournalFile *template,
2170 JournalFile **ret) {
2176 r = journal_file_open(fname, flags, mode, compress, seal,
2177 metrics, mmap_cache, template, ret);
2178 if (r != -EBADMSG && /* corrupted */
2179 r != -ENODATA && /* truncated */
2180 r != -EHOSTDOWN && /* other machine */
2181 r != -EPROTONOSUPPORT && /* incompatible feature */
2182 r != -EBUSY && /* unclean shutdown */
2183 r != -ESHUTDOWN /* already archived */)
2186 if ((flags & O_ACCMODE) == O_RDONLY)
2189 if (!(flags & O_CREAT))
2192 if (!endswith(fname, ".journal"))
2195 /* The file is corrupted. Rotate it away and try it again (but only once) */
2198 if (asprintf(&p, "%.*s@%016llx-%016llx.journal~",
2200 (unsigned long long) now(CLOCK_REALTIME),
2204 r = rename(fname, p);
2209 log_warning("File %s corrupted or uncleanly shut down, renaming and replacing.", fname);
2211 return journal_file_open(fname, flags, mode, compress, seal,
2212 metrics, mmap_cache, template, ret);
2216 int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint64_t p, uint64_t *seqnum, Object **ret, uint64_t *offset) {
2218 uint64_t q, xor_hash = 0;
2231 ts.monotonic = le64toh(o->entry.monotonic);
2232 ts.realtime = le64toh(o->entry.realtime);
2234 if (to->tail_entry_monotonic_valid &&
2235 ts.monotonic < le64toh(to->header->tail_entry_monotonic))
2238 n = journal_file_entry_n_items(o);
2239 items = alloca(sizeof(EntryItem) * n);
2241 for (i = 0; i < n; i++) {
2248 q = le64toh(o->entry.items[i].object_offset);
2249 le_hash = o->entry.items[i].hash;
2251 r = journal_file_move_to_object(from, OBJECT_DATA, q, &o);
2255 if (le_hash != o->data.hash)
2258 l = le64toh(o->object.size) - offsetof(Object, data.payload);
2261 /* We hit the limit on 32bit machines */
2262 if ((uint64_t) t != l)
2265 if (o->object.flags & OBJECT_COMPRESSED) {
2269 if (!uncompress_blob(o->data.payload, l, &from->compress_buffer, &from->compress_buffer_size, &rsize))
2272 data = from->compress_buffer;
2275 return -EPROTONOSUPPORT;
2278 data = o->data.payload;
2280 r = journal_file_append_data(to, data, l, &u, &h);
2284 xor_hash ^= le64toh(u->data.hash);
2285 items[i].object_offset = htole64(h);
2286 items[i].hash = u->data.hash;
2288 r = journal_file_move_to_object(from, OBJECT_ENTRY, p, &o);
2293 return journal_file_append_entry_internal(to, &ts, xor_hash, items, n, seqnum, ret, offset);
2296 void journal_default_metrics(JournalMetrics *m, int fd) {
2297 uint64_t fs_size = 0;
2299 char a[FORMAT_BYTES_MAX], b[FORMAT_BYTES_MAX], c[FORMAT_BYTES_MAX], d[FORMAT_BYTES_MAX];
2304 if (fstatvfs(fd, &ss) >= 0)
2305 fs_size = ss.f_frsize * ss.f_blocks;
2307 if (m->max_use == (uint64_t) -1) {
2310 m->max_use = PAGE_ALIGN(fs_size / 10); /* 10% of file system size */
2312 if (m->max_use > DEFAULT_MAX_USE_UPPER)
2313 m->max_use = DEFAULT_MAX_USE_UPPER;
2315 if (m->max_use < DEFAULT_MAX_USE_LOWER)
2316 m->max_use = DEFAULT_MAX_USE_LOWER;
2318 m->max_use = DEFAULT_MAX_USE_LOWER;
2320 m->max_use = PAGE_ALIGN(m->max_use);
2322 if (m->max_use < JOURNAL_FILE_SIZE_MIN*2)
2323 m->max_use = JOURNAL_FILE_SIZE_MIN*2;
2326 if (m->max_size == (uint64_t) -1) {
2327 m->max_size = PAGE_ALIGN(m->max_use / 8); /* 8 chunks */
2329 if (m->max_size > DEFAULT_MAX_SIZE_UPPER)
2330 m->max_size = DEFAULT_MAX_SIZE_UPPER;
2332 m->max_size = PAGE_ALIGN(m->max_size);
2334 if (m->max_size < JOURNAL_FILE_SIZE_MIN)
2335 m->max_size = JOURNAL_FILE_SIZE_MIN;
2337 if (m->max_size*2 > m->max_use)
2338 m->max_use = m->max_size*2;
2340 if (m->min_size == (uint64_t) -1)
2341 m->min_size = JOURNAL_FILE_SIZE_MIN;
2343 m->min_size = PAGE_ALIGN(m->min_size);
2345 if (m->min_size < JOURNAL_FILE_SIZE_MIN)
2346 m->min_size = JOURNAL_FILE_SIZE_MIN;
2348 if (m->min_size > m->max_size)
2349 m->max_size = m->min_size;
2352 if (m->keep_free == (uint64_t) -1) {
2355 m->keep_free = PAGE_ALIGN(fs_size / 20); /* 5% of file system size */
2357 if (m->keep_free > DEFAULT_KEEP_FREE_UPPER)
2358 m->keep_free = DEFAULT_KEEP_FREE_UPPER;
2361 m->keep_free = DEFAULT_KEEP_FREE;
2364 log_info("Fixed max_use=%s max_size=%s min_size=%s keep_free=%s",
2365 format_bytes(a, sizeof(a), m->max_use),
2366 format_bytes(b, sizeof(b), m->max_size),
2367 format_bytes(c, sizeof(c), m->min_size),
2368 format_bytes(d, sizeof(d), m->keep_free));
2371 int journal_file_get_cutoff_realtime_usec(JournalFile *f, usec_t *from, usec_t *to) {
2376 if (f->header->head_entry_realtime == 0)
2379 *from = le64toh(f->header->head_entry_realtime);
2383 if (f->header->tail_entry_realtime == 0)
2386 *to = le64toh(f->header->tail_entry_realtime);
2392 int journal_file_get_cutoff_monotonic_usec(JournalFile *f, sd_id128_t boot_id, usec_t *from, usec_t *to) {
2393 char t[9+32+1] = "_BOOT_ID=";
2401 sd_id128_to_string(boot_id, t + 9);
2403 r = journal_file_find_data_object(f, t, strlen(t), &o, &p);
2407 if (le64toh(o->data.n_entries) <= 0)
2411 r = journal_file_move_to_object(f, OBJECT_ENTRY, le64toh(o->data.entry_offset), &o);
2415 *from = le64toh(o->entry.monotonic);
2419 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
2423 r = generic_array_get_plus_one(f,
2424 le64toh(o->data.entry_offset),
2425 le64toh(o->data.entry_array_offset),
2426 le64toh(o->data.n_entries)-1,
2431 *to = le64toh(o->entry.monotonic);
2437 bool journal_file_rotate_suggested(JournalFile *f) {
2440 /* If we gained new header fields we gained new features,
2441 * hence suggest a rotation */
2442 if (le64toh(f->header->header_size) < sizeof(Header)) {
2443 log_debug("%s uses an outdated header, suggesting rotation.", f->path);
2447 /* Let's check if the hash tables grew over a certain fill
2448 * level (75%, borrowing this value from Java's hash table
2449 * implementation), and if so suggest a rotation. To calculate
2450 * the fill level we need the n_data field, which only exists
2451 * in newer versions. */
2453 if (JOURNAL_HEADER_CONTAINS(f->header, n_data))
2454 if (le64toh(f->header->n_data) * 4ULL > (le64toh(f->header->data_hash_table_size) / sizeof(HashItem)) * 3ULL) {
2455 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.",
2457 100.0 * (double) le64toh(f->header->n_data) / ((double) (le64toh(f->header->data_hash_table_size) / sizeof(HashItem))),
2458 (unsigned long long) le64toh(f->header->n_data),
2459 (unsigned long long) (le64toh(f->header->data_hash_table_size) / sizeof(HashItem)),
2460 (unsigned long long) (f->last_stat.st_size),
2461 (unsigned long long) (f->last_stat.st_size / le64toh(f->header->n_data)));
2465 if (JOURNAL_HEADER_CONTAINS(f->header, n_fields))
2466 if (le64toh(f->header->n_fields) * 4ULL > (le64toh(f->header->field_hash_table_size) / sizeof(HashItem)) * 3ULL) {
2467 log_debug("Field hash table of %s has a fill level at %.1f (%llu of %llu items), suggesting rotation.",
2469 100.0 * (double) le64toh(f->header->n_fields) / ((double) (le64toh(f->header->field_hash_table_size) / sizeof(HashItem))),
2470 (unsigned long long) le64toh(f->header->n_fields),
2471 (unsigned long long) (le64toh(f->header->field_hash_table_size) / sizeof(HashItem)));