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(f->header->data_hash_table_offset) ||
225 !VALID64(f->header->field_hash_table_offset) ||
226 !VALID64(f->header->tail_object_offset) ||
227 !VALID64(f->header->entry_array_offset))
232 sd_id128_t machine_id;
235 r = sd_id128_get_machine(&machine_id);
239 if (!sd_id128_equal(machine_id, f->header->machine_id))
242 state = f->header->state;
244 if (state == STATE_ONLINE) {
245 log_debug("Journal file %s is already online. Assuming unclean closing.", f->path);
247 } else if (state == STATE_ARCHIVED)
249 else if (state != STATE_OFFLINE) {
250 log_debug("Journal file %s has unknown state %u.", f->path, state);
255 f->compress = JOURNAL_HEADER_COMPRESSED(f->header);
258 f->seal = JOURNAL_HEADER_SEALED(f->header);
263 static int journal_file_allocate(JournalFile *f, uint64_t offset, uint64_t size) {
264 uint64_t old_size, new_size;
269 /* We assume that this file is not sparse, and we know that
270 * for sure, since we always call posix_fallocate()
274 le64toh(f->header->header_size) +
275 le64toh(f->header->arena_size);
277 new_size = PAGE_ALIGN(offset + size);
278 if (new_size < le64toh(f->header->header_size))
279 new_size = le64toh(f->header->header_size);
281 if (new_size <= old_size)
284 if (f->metrics.max_size > 0 &&
285 new_size > f->metrics.max_size)
288 if (new_size > f->metrics.min_size &&
289 f->metrics.keep_free > 0) {
292 if (fstatvfs(f->fd, &svfs) >= 0) {
295 available = svfs.f_bfree * svfs.f_bsize;
297 if (available >= f->metrics.keep_free)
298 available -= f->metrics.keep_free;
302 if (new_size - old_size > available)
307 /* Note that the glibc fallocate() fallback is very
308 inefficient, hence we try to minimize the allocation area
310 r = posix_fallocate(f->fd, old_size, new_size - old_size);
314 if (fstat(f->fd, &f->last_stat) < 0)
317 f->header->arena_size = htole64(new_size - le64toh(f->header->header_size));
322 static int journal_file_move_to(JournalFile *f, int context, bool keep_always, uint64_t offset, uint64_t size, void **ret) {
326 /* Avoid SIGBUS on invalid accesses */
327 if (offset + size > (uint64_t) f->last_stat.st_size) {
328 /* Hmm, out of range? Let's refresh the fstat() data
329 * first, before we trust that check. */
331 if (fstat(f->fd, &f->last_stat) < 0 ||
332 offset + size > (uint64_t) f->last_stat.st_size)
333 return -EADDRNOTAVAIL;
336 return mmap_cache_get(f->mmap, f->fd, f->prot, context, keep_always, offset, size, &f->last_stat, ret);
339 static uint64_t minimum_header_size(Object *o) {
341 static uint64_t table[] = {
342 [OBJECT_DATA] = sizeof(DataObject),
343 [OBJECT_FIELD] = sizeof(FieldObject),
344 [OBJECT_ENTRY] = sizeof(EntryObject),
345 [OBJECT_DATA_HASH_TABLE] = sizeof(HashTableObject),
346 [OBJECT_FIELD_HASH_TABLE] = sizeof(HashTableObject),
347 [OBJECT_ENTRY_ARRAY] = sizeof(EntryArrayObject),
348 [OBJECT_TAG] = sizeof(TagObject),
351 if (o->object.type >= ELEMENTSOF(table) || table[o->object.type] <= 0)
352 return sizeof(ObjectHeader);
354 return table[o->object.type];
357 int journal_file_move_to_object(JournalFile *f, int type, uint64_t offset, Object **ret) {
367 /* Objects may only be located at multiple of 64 bit */
368 if (!VALID64(offset))
371 /* One context for each type, plus one catch-all for the rest */
372 context = type > 0 && type < _OBJECT_TYPE_MAX ? type : 0;
374 r = journal_file_move_to(f, context, false, offset, sizeof(ObjectHeader), &t);
379 s = le64toh(o->object.size);
381 if (s < sizeof(ObjectHeader))
384 if (o->object.type <= OBJECT_UNUSED)
387 if (s < minimum_header_size(o))
390 if (type >= 0 && o->object.type != type)
393 if (s > sizeof(ObjectHeader)) {
394 r = journal_file_move_to(f, o->object.type, false, offset, s, &t);
405 static uint64_t journal_file_entry_seqnum(JournalFile *f, uint64_t *seqnum) {
410 r = le64toh(f->header->tail_entry_seqnum) + 1;
413 /* If an external seqnum counter was passed, we update
414 * both the local and the external one, and set it to
415 * the maximum of both */
423 f->header->tail_entry_seqnum = htole64(r);
425 if (f->header->head_entry_seqnum == 0)
426 f->header->head_entry_seqnum = htole64(r);
431 int journal_file_append_object(JournalFile *f, int type, uint64_t size, Object **ret, uint64_t *offset) {
438 assert(type > 0 && type < _OBJECT_TYPE_MAX);
439 assert(size >= sizeof(ObjectHeader));
443 p = le64toh(f->header->tail_object_offset);
445 p = le64toh(f->header->header_size);
447 r = journal_file_move_to_object(f, -1, p, &tail);
451 p += ALIGN64(le64toh(tail->object.size));
454 r = journal_file_allocate(f, p, size);
458 r = journal_file_move_to(f, type, false, p, size, &t);
465 o->object.type = type;
466 o->object.size = htole64(size);
468 f->header->tail_object_offset = htole64(p);
469 f->header->n_objects = htole64(le64toh(f->header->n_objects) + 1);
477 static int journal_file_setup_data_hash_table(JournalFile *f) {
484 /* We estimate that we need 1 hash table entry per 768 of
485 journal file and we want to make sure we never get beyond
486 75% fill level. Calculate the hash table size for the
487 maximum file size based on these metrics. */
489 s = (f->metrics.max_size * 4 / 768 / 3) * sizeof(HashItem);
490 if (s < DEFAULT_DATA_HASH_TABLE_SIZE)
491 s = DEFAULT_DATA_HASH_TABLE_SIZE;
493 log_info("Reserving %llu entries in hash table.", (unsigned long long) (s / sizeof(HashItem)));
495 r = journal_file_append_object(f,
496 OBJECT_DATA_HASH_TABLE,
497 offsetof(Object, hash_table.items) + s,
502 memset(o->hash_table.items, 0, s);
504 f->header->data_hash_table_offset = htole64(p + offsetof(Object, hash_table.items));
505 f->header->data_hash_table_size = htole64(s);
510 static int journal_file_setup_field_hash_table(JournalFile *f) {
517 s = DEFAULT_FIELD_HASH_TABLE_SIZE;
518 r = journal_file_append_object(f,
519 OBJECT_FIELD_HASH_TABLE,
520 offsetof(Object, hash_table.items) + s,
525 memset(o->hash_table.items, 0, s);
527 f->header->field_hash_table_offset = htole64(p + offsetof(Object, hash_table.items));
528 f->header->field_hash_table_size = htole64(s);
533 static int journal_file_map_data_hash_table(JournalFile *f) {
540 p = le64toh(f->header->data_hash_table_offset);
541 s = le64toh(f->header->data_hash_table_size);
543 r = journal_file_move_to(f,
544 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,
573 f->field_hash_table = t;
577 static int journal_file_link_data(JournalFile *f, Object *o, uint64_t offset, uint64_t hash) {
584 assert(o->object.type == OBJECT_DATA);
586 /* This might alter the window we are looking at */
588 o->data.next_hash_offset = o->data.next_field_offset = 0;
589 o->data.entry_offset = o->data.entry_array_offset = 0;
590 o->data.n_entries = 0;
592 h = hash % (le64toh(f->header->data_hash_table_size) / sizeof(HashItem));
593 p = le64toh(f->data_hash_table[h].tail_hash_offset);
595 /* Only entry in the hash table is easy */
596 f->data_hash_table[h].head_hash_offset = htole64(offset);
598 /* Move back to the previous data object, to patch in
601 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
605 o->data.next_hash_offset = htole64(offset);
608 f->data_hash_table[h].tail_hash_offset = htole64(offset);
610 if (JOURNAL_HEADER_CONTAINS(f->header, n_data))
611 f->header->n_data = htole64(le64toh(f->header->n_data) + 1);
616 int journal_file_find_data_object_with_hash(
618 const void *data, uint64_t size, uint64_t hash,
619 Object **ret, uint64_t *offset) {
621 uint64_t p, osize, h;
625 assert(data || size == 0);
627 osize = offsetof(Object, data.payload) + size;
629 if (f->header->data_hash_table_size == 0)
632 h = hash % (le64toh(f->header->data_hash_table_size) / sizeof(HashItem));
633 p = le64toh(f->data_hash_table[h].head_hash_offset);
638 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
642 if (le64toh(o->data.hash) != hash)
645 if (o->object.flags & OBJECT_COMPRESSED) {
649 l = le64toh(o->object.size);
650 if (l <= offsetof(Object, data.payload))
653 l -= offsetof(Object, data.payload);
655 if (!uncompress_blob(o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize))
659 memcmp(f->compress_buffer, data, size) == 0) {
670 return -EPROTONOSUPPORT;
673 } else if (le64toh(o->object.size) == osize &&
674 memcmp(o->data.payload, data, size) == 0) {
686 p = le64toh(o->data.next_hash_offset);
692 int journal_file_find_data_object(
694 const void *data, uint64_t size,
695 Object **ret, uint64_t *offset) {
700 assert(data || size == 0);
702 hash = hash64(data, size);
704 return journal_file_find_data_object_with_hash(f,
709 static int journal_file_append_data(
711 const void *data, uint64_t size,
712 Object **ret, uint64_t *offset) {
718 bool compressed = false;
721 assert(data || size == 0);
723 hash = hash64(data, size);
725 r = journal_file_find_data_object_with_hash(f, data, size, hash, &o, &p);
739 osize = offsetof(Object, data.payload) + size;
740 r = journal_file_append_object(f, OBJECT_DATA, osize, &o, &p);
744 o->data.hash = htole64(hash);
748 size >= COMPRESSION_SIZE_THRESHOLD) {
751 compressed = compress_blob(data, size, o->data.payload, &rsize);
754 o->object.size = htole64(offsetof(Object, data.payload) + rsize);
755 o->object.flags |= OBJECT_COMPRESSED;
757 log_debug("Compressed data object %lu -> %lu", (unsigned long) size, (unsigned long) rsize);
762 if (!compressed && size > 0)
763 memcpy(o->data.payload, data, size);
765 r = journal_file_link_data(f, o, p, hash);
770 r = journal_file_hmac_put_object(f, OBJECT_DATA, p);
775 /* The linking might have altered the window, so let's
776 * refresh our pointer */
777 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
790 uint64_t journal_file_entry_n_items(Object *o) {
792 assert(o->object.type == OBJECT_ENTRY);
794 return (le64toh(o->object.size) - offsetof(Object, entry.items)) / sizeof(EntryItem);
797 uint64_t journal_file_entry_array_n_items(Object *o) {
799 assert(o->object.type == OBJECT_ENTRY_ARRAY);
801 return (le64toh(o->object.size) - offsetof(Object, entry_array.items)) / sizeof(uint64_t);
804 uint64_t journal_file_hash_table_n_items(Object *o) {
806 assert(o->object.type == OBJECT_DATA_HASH_TABLE ||
807 o->object.type == OBJECT_FIELD_HASH_TABLE);
809 return (le64toh(o->object.size) - offsetof(Object, hash_table.items)) / sizeof(HashItem);
812 static int link_entry_into_array(JournalFile *f,
817 uint64_t n = 0, ap = 0, q, i, a, hidx;
826 i = hidx = le64toh(*idx);
829 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
833 n = journal_file_entry_array_n_items(o);
835 o->entry_array.items[i] = htole64(p);
836 *idx = htole64(hidx + 1);
842 a = le64toh(o->entry_array.next_entry_array_offset);
853 r = journal_file_append_object(f, OBJECT_ENTRY_ARRAY,
854 offsetof(Object, entry_array.items) + n * sizeof(uint64_t),
860 r = journal_file_hmac_put_object(f, OBJECT_ENTRY_ARRAY, q);
865 o->entry_array.items[i] = htole64(p);
870 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, ap, &o);
874 o->entry_array.next_entry_array_offset = htole64(q);
877 if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays))
878 f->header->n_entry_arrays = htole64(le64toh(f->header->n_entry_arrays) + 1);
880 *idx = htole64(hidx + 1);
885 static int link_entry_into_array_plus_one(JournalFile *f,
904 i = htole64(le64toh(*idx) - 1);
905 r = link_entry_into_array(f, first, &i, p);
910 *idx = htole64(le64toh(*idx) + 1);
914 static int journal_file_link_entry_item(JournalFile *f, Object *o, uint64_t offset, uint64_t i) {
921 p = le64toh(o->entry.items[i].object_offset);
925 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
929 return link_entry_into_array_plus_one(f,
930 &o->data.entry_offset,
931 &o->data.entry_array_offset,
936 static int journal_file_link_entry(JournalFile *f, Object *o, uint64_t offset) {
943 assert(o->object.type == OBJECT_ENTRY);
945 __sync_synchronize();
947 /* Link up the entry itself */
948 r = link_entry_into_array(f,
949 &f->header->entry_array_offset,
950 &f->header->n_entries,
955 /* log_debug("=> %s seqnr=%lu n_entries=%lu", f->path, (unsigned long) o->entry.seqnum, (unsigned long) f->header->n_entries); */
957 if (f->header->head_entry_realtime == 0)
958 f->header->head_entry_realtime = o->entry.realtime;
960 f->header->tail_entry_realtime = o->entry.realtime;
961 f->header->tail_entry_monotonic = o->entry.monotonic;
963 f->tail_entry_monotonic_valid = true;
965 /* Link up the items */
966 n = journal_file_entry_n_items(o);
967 for (i = 0; i < n; i++) {
968 r = journal_file_link_entry_item(f, o, offset, i);
976 static int journal_file_append_entry_internal(
978 const dual_timestamp *ts,
980 const EntryItem items[], unsigned n_items,
982 Object **ret, uint64_t *offset) {
989 assert(items || n_items == 0);
992 osize = offsetof(Object, entry.items) + (n_items * sizeof(EntryItem));
994 r = journal_file_append_object(f, OBJECT_ENTRY, osize, &o, &np);
998 o->entry.seqnum = htole64(journal_file_entry_seqnum(f, seqnum));
999 memcpy(o->entry.items, items, n_items * sizeof(EntryItem));
1000 o->entry.realtime = htole64(ts->realtime);
1001 o->entry.monotonic = htole64(ts->monotonic);
1002 o->entry.xor_hash = htole64(xor_hash);
1003 o->entry.boot_id = f->header->boot_id;
1006 r = journal_file_hmac_put_object(f, OBJECT_ENTRY, np);
1011 r = journal_file_link_entry(f, o, np);
1024 void journal_file_post_change(JournalFile *f) {
1027 /* inotify() does not receive IN_MODIFY events from file
1028 * accesses done via mmap(). After each access we hence
1029 * trigger IN_MODIFY by truncating the journal file to its
1030 * current size which triggers IN_MODIFY. */
1032 __sync_synchronize();
1034 if (ftruncate(f->fd, f->last_stat.st_size) < 0)
1035 log_error("Failed to to truncate file to its own size: %m");
1038 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) {
1042 uint64_t xor_hash = 0;
1043 struct dual_timestamp _ts;
1046 assert(iovec || n_iovec == 0);
1052 dual_timestamp_get(&_ts);
1056 if (f->tail_entry_monotonic_valid &&
1057 ts->monotonic < le64toh(f->header->tail_entry_monotonic))
1061 r = journal_file_maybe_append_tag(f, ts->realtime);
1066 /* alloca() can't take 0, hence let's allocate at least one */
1067 items = alloca(sizeof(EntryItem) * MAX(1, n_iovec));
1069 for (i = 0; i < n_iovec; i++) {
1073 r = journal_file_append_data(f, iovec[i].iov_base, iovec[i].iov_len, &o, &p);
1077 xor_hash ^= le64toh(o->data.hash);
1078 items[i].object_offset = htole64(p);
1079 items[i].hash = o->data.hash;
1082 r = journal_file_append_entry_internal(f, ts, xor_hash, items, n_iovec, seqnum, ret, offset);
1084 journal_file_post_change(f);
1089 static int generic_array_get(JournalFile *f,
1092 Object **ret, uint64_t *offset) {
1104 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
1108 n = journal_file_entry_array_n_items(o);
1110 p = le64toh(o->entry_array.items[i]);
1115 a = le64toh(o->entry_array.next_entry_array_offset);
1118 if (a <= 0 || p <= 0)
1121 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
1134 static int generic_array_get_plus_one(JournalFile *f,
1138 Object **ret, uint64_t *offset) {
1147 r = journal_file_move_to_object(f, OBJECT_ENTRY, extra, &o);
1160 return generic_array_get(f, first, i-1, ret, offset);
1169 static int generic_array_bisect(JournalFile *f,
1173 int (*test_object)(JournalFile *f, uint64_t p, uint64_t needle),
1174 direction_t direction,
1179 uint64_t a, p, t = 0, i = 0, last_p = 0;
1180 bool subtract_one = false;
1181 Object *o, *array = NULL;
1185 assert(test_object);
1189 uint64_t left, right, k, lp;
1191 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &array);
1195 k = journal_file_entry_array_n_items(array);
1201 lp = p = le64toh(array->entry_array.items[i]);
1205 r = test_object(f, p, needle);
1209 if (r == TEST_FOUND)
1210 r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT;
1212 if (r == TEST_RIGHT) {
1216 if (left == right) {
1217 if (direction == DIRECTION_UP)
1218 subtract_one = true;
1224 assert(left < right);
1226 i = (left + right) / 2;
1227 p = le64toh(array->entry_array.items[i]);
1231 r = test_object(f, p, needle);
1235 if (r == TEST_FOUND)
1236 r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT;
1238 if (r == TEST_RIGHT)
1246 if (direction == DIRECTION_UP) {
1248 subtract_one = true;
1259 a = le64toh(array->entry_array.next_entry_array_offset);
1265 if (subtract_one && t == 0 && i == 0)
1268 if (subtract_one && i == 0)
1270 else if (subtract_one)
1271 p = le64toh(array->entry_array.items[i-1]);
1273 p = le64toh(array->entry_array.items[i]);
1275 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
1286 *idx = t + i + (subtract_one ? -1 : 0);
1291 static int generic_array_bisect_plus_one(JournalFile *f,
1296 int (*test_object)(JournalFile *f, uint64_t p, uint64_t needle),
1297 direction_t direction,
1303 bool step_back = false;
1307 assert(test_object);
1312 /* This bisects the array in object 'first', but first checks
1314 r = test_object(f, extra, needle);
1318 if (r == TEST_FOUND)
1319 r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT;
1321 /* if we are looking with DIRECTION_UP then we need to first
1322 see if in the actual array there is a matching entry, and
1323 return the last one of that. But if there isn't any we need
1324 to return this one. Hence remember this, and return it
1327 step_back = direction == DIRECTION_UP;
1329 if (r == TEST_RIGHT) {
1330 if (direction == DIRECTION_DOWN)
1336 r = generic_array_bisect(f, first, n-1, needle, test_object, direction, ret, offset, idx);
1338 if (r == 0 && step_back)
1347 r = journal_file_move_to_object(f, OBJECT_ENTRY, extra, &o);
1363 static int test_object_offset(JournalFile *f, uint64_t p, uint64_t needle) {
1369 else if (p < needle)
1375 int journal_file_move_to_entry_by_offset(
1378 direction_t direction,
1382 return generic_array_bisect(f,
1383 le64toh(f->header->entry_array_offset),
1384 le64toh(f->header->n_entries),
1392 static int test_object_seqnum(JournalFile *f, uint64_t p, uint64_t needle) {
1399 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
1403 if (le64toh(o->entry.seqnum) == needle)
1405 else if (le64toh(o->entry.seqnum) < needle)
1411 int journal_file_move_to_entry_by_seqnum(
1414 direction_t direction,
1418 return generic_array_bisect(f,
1419 le64toh(f->header->entry_array_offset),
1420 le64toh(f->header->n_entries),
1427 static int test_object_realtime(JournalFile *f, uint64_t p, uint64_t needle) {
1434 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
1438 if (le64toh(o->entry.realtime) == needle)
1440 else if (le64toh(o->entry.realtime) < needle)
1446 int journal_file_move_to_entry_by_realtime(
1449 direction_t direction,
1453 return generic_array_bisect(f,
1454 le64toh(f->header->entry_array_offset),
1455 le64toh(f->header->n_entries),
1457 test_object_realtime,
1462 static int test_object_monotonic(JournalFile *f, uint64_t p, uint64_t needle) {
1469 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
1473 if (le64toh(o->entry.monotonic) == needle)
1475 else if (le64toh(o->entry.monotonic) < needle)
1481 int journal_file_move_to_entry_by_monotonic(
1485 direction_t direction,
1489 char t[9+32+1] = "_BOOT_ID=";
1495 sd_id128_to_string(boot_id, t + 9);
1496 r = journal_file_find_data_object(f, t, strlen(t), &o, NULL);
1502 return generic_array_bisect_plus_one(f,
1503 le64toh(o->data.entry_offset),
1504 le64toh(o->data.entry_array_offset),
1505 le64toh(o->data.n_entries),
1507 test_object_monotonic,
1512 int journal_file_next_entry(
1514 Object *o, uint64_t p,
1515 direction_t direction,
1516 Object **ret, uint64_t *offset) {
1522 assert(p > 0 || !o);
1524 n = le64toh(f->header->n_entries);
1529 i = direction == DIRECTION_DOWN ? 0 : n - 1;
1531 if (o->object.type != OBJECT_ENTRY)
1534 r = generic_array_bisect(f,
1535 le64toh(f->header->entry_array_offset),
1536 le64toh(f->header->n_entries),
1545 if (direction == DIRECTION_DOWN) {
1558 /* And jump to it */
1559 return generic_array_get(f,
1560 le64toh(f->header->entry_array_offset),
1565 int journal_file_skip_entry(
1567 Object *o, uint64_t p,
1569 Object **ret, uint64_t *offset) {
1578 if (o->object.type != OBJECT_ENTRY)
1581 r = generic_array_bisect(f,
1582 le64toh(f->header->entry_array_offset),
1583 le64toh(f->header->n_entries),
1592 /* Calculate new index */
1594 if ((uint64_t) -skip >= i)
1597 i = i - (uint64_t) -skip;
1599 i += (uint64_t) skip;
1601 n = le64toh(f->header->n_entries);
1608 return generic_array_get(f,
1609 le64toh(f->header->entry_array_offset),
1614 int journal_file_next_entry_for_data(
1616 Object *o, uint64_t p,
1617 uint64_t data_offset,
1618 direction_t direction,
1619 Object **ret, uint64_t *offset) {
1626 assert(p > 0 || !o);
1628 r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
1632 n = le64toh(d->data.n_entries);
1637 i = direction == DIRECTION_DOWN ? 0 : n - 1;
1639 if (o->object.type != OBJECT_ENTRY)
1642 r = generic_array_bisect_plus_one(f,
1643 le64toh(d->data.entry_offset),
1644 le64toh(d->data.entry_array_offset),
1645 le64toh(d->data.n_entries),
1655 if (direction == DIRECTION_DOWN) {
1669 return generic_array_get_plus_one(f,
1670 le64toh(d->data.entry_offset),
1671 le64toh(d->data.entry_array_offset),
1676 int journal_file_move_to_entry_by_offset_for_data(
1678 uint64_t data_offset,
1680 direction_t direction,
1681 Object **ret, uint64_t *offset) {
1688 r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
1692 return generic_array_bisect_plus_one(f,
1693 le64toh(d->data.entry_offset),
1694 le64toh(d->data.entry_array_offset),
1695 le64toh(d->data.n_entries),
1702 int journal_file_move_to_entry_by_monotonic_for_data(
1704 uint64_t data_offset,
1707 direction_t direction,
1708 Object **ret, uint64_t *offset) {
1710 char t[9+32+1] = "_BOOT_ID=";
1717 /* First, seek by time */
1718 sd_id128_to_string(boot_id, t + 9);
1719 r = journal_file_find_data_object(f, t, strlen(t), &o, &b);
1725 r = generic_array_bisect_plus_one(f,
1726 le64toh(o->data.entry_offset),
1727 le64toh(o->data.entry_array_offset),
1728 le64toh(o->data.n_entries),
1730 test_object_monotonic,
1736 /* And now, continue seeking until we find an entry that
1737 * exists in both bisection arrays */
1743 r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
1747 r = generic_array_bisect_plus_one(f,
1748 le64toh(d->data.entry_offset),
1749 le64toh(d->data.entry_array_offset),
1750 le64toh(d->data.n_entries),
1758 r = journal_file_move_to_object(f, OBJECT_DATA, b, &o);
1762 r = generic_array_bisect_plus_one(f,
1763 le64toh(o->data.entry_offset),
1764 le64toh(o->data.entry_array_offset),
1765 le64toh(o->data.n_entries),
1789 int journal_file_move_to_entry_by_seqnum_for_data(
1791 uint64_t data_offset,
1793 direction_t direction,
1794 Object **ret, uint64_t *offset) {
1801 r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
1805 return generic_array_bisect_plus_one(f,
1806 le64toh(d->data.entry_offset),
1807 le64toh(d->data.entry_array_offset),
1808 le64toh(d->data.n_entries),
1815 int journal_file_move_to_entry_by_realtime_for_data(
1817 uint64_t data_offset,
1819 direction_t direction,
1820 Object **ret, uint64_t *offset) {
1827 r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
1831 return generic_array_bisect_plus_one(f,
1832 le64toh(d->data.entry_offset),
1833 le64toh(d->data.entry_array_offset),
1834 le64toh(d->data.n_entries),
1836 test_object_realtime,
1841 void journal_file_dump(JournalFile *f) {
1848 journal_file_print_header(f);
1850 p = le64toh(f->header->header_size);
1852 r = journal_file_move_to_object(f, -1, p, &o);
1856 switch (o->object.type) {
1859 printf("Type: OBJECT_UNUSED\n");
1863 printf("Type: OBJECT_DATA\n");
1867 printf("Type: OBJECT_ENTRY seqnum=%llu monotonic=%llu realtime=%llu\n",
1868 (unsigned long long) le64toh(o->entry.seqnum),
1869 (unsigned long long) le64toh(o->entry.monotonic),
1870 (unsigned long long) le64toh(o->entry.realtime));
1873 case OBJECT_FIELD_HASH_TABLE:
1874 printf("Type: OBJECT_FIELD_HASH_TABLE\n");
1877 case OBJECT_DATA_HASH_TABLE:
1878 printf("Type: OBJECT_DATA_HASH_TABLE\n");
1881 case OBJECT_ENTRY_ARRAY:
1882 printf("Type: OBJECT_ENTRY_ARRAY\n");
1886 printf("Type: OBJECT_TAG seqnum=%llu epoch=%llu\n",
1887 (unsigned long long) le64toh(o->tag.seqnum),
1888 (unsigned long long) le64toh(o->tag.epoch));
1892 if (o->object.flags & OBJECT_COMPRESSED)
1893 printf("Flags: COMPRESSED\n");
1895 if (p == le64toh(f->header->tail_object_offset))
1898 p = p + ALIGN64(le64toh(o->object.size));
1903 log_error("File corrupt");
1906 void journal_file_print_header(JournalFile *f) {
1907 char a[33], b[33], c[33];
1908 char x[FORMAT_TIMESTAMP_MAX], y[FORMAT_TIMESTAMP_MAX];
1912 printf("File Path: %s\n"
1916 "Sequential Number ID: %s\n"
1918 "Compatible Flags:%s%s\n"
1919 "Incompatible Flags:%s%s\n"
1920 "Header size: %llu\n"
1921 "Arena size: %llu\n"
1922 "Data Hash Table Size: %llu\n"
1923 "Field Hash Table Size: %llu\n"
1924 "Rotate Suggested: %s\n"
1925 "Head Sequential Number: %llu\n"
1926 "Tail Sequential Number: %llu\n"
1927 "Head Realtime Timestamp: %s\n"
1928 "Tail Realtime Timestamp: %s\n"
1930 "Entry Objects: %llu\n",
1932 sd_id128_to_string(f->header->file_id, a),
1933 sd_id128_to_string(f->header->machine_id, b),
1934 sd_id128_to_string(f->header->boot_id, c),
1935 sd_id128_to_string(f->header->seqnum_id, c),
1936 f->header->state == STATE_OFFLINE ? "OFFLINE" :
1937 f->header->state == STATE_ONLINE ? "ONLINE" :
1938 f->header->state == STATE_ARCHIVED ? "ARCHIVED" : "UNKNOWN",
1939 JOURNAL_HEADER_SEALED(f->header) ? " SEALED" : "",
1940 (le32toh(f->header->compatible_flags) & ~HEADER_COMPATIBLE_SEALED) ? " ???" : "",
1941 JOURNAL_HEADER_COMPRESSED(f->header) ? " COMPRESSED" : "",
1942 (le32toh(f->header->incompatible_flags) & ~HEADER_INCOMPATIBLE_COMPRESSED) ? " ???" : "",
1943 (unsigned long long) le64toh(f->header->header_size),
1944 (unsigned long long) le64toh(f->header->arena_size),
1945 (unsigned long long) le64toh(f->header->data_hash_table_size) / sizeof(HashItem),
1946 (unsigned long long) le64toh(f->header->field_hash_table_size) / sizeof(HashItem),
1947 yes_no(journal_file_rotate_suggested(f)),
1948 (unsigned long long) le64toh(f->header->head_entry_seqnum),
1949 (unsigned long long) le64toh(f->header->tail_entry_seqnum),
1950 format_timestamp(x, sizeof(x), le64toh(f->header->head_entry_realtime)),
1951 format_timestamp(y, sizeof(y), le64toh(f->header->tail_entry_realtime)),
1952 (unsigned long long) le64toh(f->header->n_objects),
1953 (unsigned long long) le64toh(f->header->n_entries));
1955 if (JOURNAL_HEADER_CONTAINS(f->header, n_data))
1956 printf("Data Objects: %llu\n"
1957 "Data Hash Table Fill: %.1f%%\n",
1958 (unsigned long long) le64toh(f->header->n_data),
1959 100.0 * (double) le64toh(f->header->n_data) / ((double) (le64toh(f->header->data_hash_table_size) / sizeof(HashItem))));
1961 if (JOURNAL_HEADER_CONTAINS(f->header, n_fields))
1962 printf("Field Objects: %llu\n"
1963 "Field Hash Table Fill: %.1f%%\n",
1964 (unsigned long long) le64toh(f->header->n_fields),
1965 100.0 * (double) le64toh(f->header->n_fields) / ((double) (le64toh(f->header->field_hash_table_size) / sizeof(HashItem))));
1967 if (JOURNAL_HEADER_CONTAINS(f->header, n_tags))
1968 printf("Tag Objects: %llu\n",
1969 (unsigned long long) le64toh(f->header->n_tags));
1970 if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays))
1971 printf("Entry Array Objects: %llu\n",
1972 (unsigned long long) le64toh(f->header->n_entry_arrays));
1975 int journal_file_open(
1981 JournalMetrics *metrics,
1982 MMapCache *mmap_cache,
1983 JournalFile *template,
1984 JournalFile **ret) {
1988 bool newly_created = false;
1992 if ((flags & O_ACCMODE) != O_RDONLY &&
1993 (flags & O_ACCMODE) != O_RDWR)
1996 if (!endswith(fname, ".journal") &&
1997 !endswith(fname, ".journal~"))
2000 f = new0(JournalFile, 1);
2008 f->prot = prot_from_flags(flags);
2009 f->writable = (flags & O_ACCMODE) != O_RDONLY;
2010 f->compress = compress;
2014 f->mmap = mmap_cache_ref(mmap_cache);
2016 f->mmap = mmap_cache_new();
2023 f->path = strdup(fname);
2029 f->fd = open(f->path, f->flags|O_CLOEXEC, f->mode);
2035 if (fstat(f->fd, &f->last_stat) < 0) {
2040 if (f->last_stat.st_size == 0 && f->writable) {
2041 newly_created = true;
2044 /* Try to load the FSPRG state, and if we can't, then
2045 * just don't do sealing */
2046 r = journal_file_fss_load(f);
2051 r = journal_file_init_header(f, template);
2055 if (fstat(f->fd, &f->last_stat) < 0) {
2061 if (f->last_stat.st_size < (off_t) HEADER_SIZE_MIN) {
2066 f->header = mmap(NULL, PAGE_ALIGN(sizeof(Header)), prot_from_flags(flags), MAP_SHARED, f->fd, 0);
2067 if (f->header == MAP_FAILED) {
2073 if (!newly_created) {
2074 r = journal_file_verify_header(f);
2080 if (!newly_created && f->writable) {
2081 r = journal_file_fss_load(f);
2089 journal_default_metrics(metrics, f->fd);
2090 f->metrics = *metrics;
2091 } else if (template)
2092 f->metrics = template->metrics;
2094 r = journal_file_refresh_header(f);
2100 r = journal_file_hmac_setup(f);
2105 if (newly_created) {
2106 r = journal_file_setup_field_hash_table(f);
2110 r = journal_file_setup_data_hash_table(f);
2115 r = journal_file_append_first_tag(f);
2121 r = journal_file_map_field_hash_table(f);
2125 r = journal_file_map_data_hash_table(f);
2135 journal_file_close(f);
2140 int journal_file_rotate(JournalFile **f, bool compress, bool seal) {
2143 JournalFile *old_file, *new_file = NULL;
2151 if (!old_file->writable)
2154 if (!endswith(old_file->path, ".journal"))
2157 l = strlen(old_file->path);
2159 p = new(char, l + 1 + 32 + 1 + 16 + 1 + 16 + 1);
2163 memcpy(p, old_file->path, l - 8);
2165 sd_id128_to_string(old_file->header->seqnum_id, p + l - 8 + 1);
2166 snprintf(p + l - 8 + 1 + 32, 1 + 16 + 1 + 16 + 8 + 1,
2167 "-%016llx-%016llx.journal",
2168 (unsigned long long) le64toh((*f)->header->tail_entry_seqnum),
2169 (unsigned long long) le64toh((*f)->header->tail_entry_realtime));
2171 r = rename(old_file->path, p);
2177 old_file->header->state = STATE_ARCHIVED;
2179 r = journal_file_open(old_file->path, old_file->flags, old_file->mode, compress, seal, NULL, old_file->mmap, old_file, &new_file);
2180 journal_file_close(old_file);
2186 int journal_file_open_reliably(
2192 JournalMetrics *metrics,
2193 MMapCache *mmap_cache,
2194 JournalFile *template,
2195 JournalFile **ret) {
2201 r = journal_file_open(fname, flags, mode, compress, seal,
2202 metrics, mmap_cache, template, ret);
2203 if (r != -EBADMSG && /* corrupted */
2204 r != -ENODATA && /* truncated */
2205 r != -EHOSTDOWN && /* other machine */
2206 r != -EPROTONOSUPPORT && /* incompatible feature */
2207 r != -EBUSY && /* unclean shutdown */
2208 r != -ESHUTDOWN /* already archived */)
2211 if ((flags & O_ACCMODE) == O_RDONLY)
2214 if (!(flags & O_CREAT))
2217 if (!endswith(fname, ".journal"))
2220 /* The file is corrupted. Rotate it away and try it again (but only once) */
2223 if (asprintf(&p, "%.*s@%016llx-%016llx.journal~",
2225 (unsigned long long) now(CLOCK_REALTIME),
2229 r = rename(fname, p);
2234 log_warning("File %s corrupted or uncleanly shut down, renaming and replacing.", fname);
2236 return journal_file_open(fname, flags, mode, compress, seal,
2237 metrics, mmap_cache, template, ret);
2241 int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint64_t p, uint64_t *seqnum, Object **ret, uint64_t *offset) {
2243 uint64_t q, xor_hash = 0;
2256 ts.monotonic = le64toh(o->entry.monotonic);
2257 ts.realtime = le64toh(o->entry.realtime);
2259 if (to->tail_entry_monotonic_valid &&
2260 ts.monotonic < le64toh(to->header->tail_entry_monotonic))
2263 n = journal_file_entry_n_items(o);
2264 items = alloca(sizeof(EntryItem) * n);
2266 for (i = 0; i < n; i++) {
2273 q = le64toh(o->entry.items[i].object_offset);
2274 le_hash = o->entry.items[i].hash;
2276 r = journal_file_move_to_object(from, OBJECT_DATA, q, &o);
2280 if (le_hash != o->data.hash)
2283 l = le64toh(o->object.size) - offsetof(Object, data.payload);
2286 /* We hit the limit on 32bit machines */
2287 if ((uint64_t) t != l)
2290 if (o->object.flags & OBJECT_COMPRESSED) {
2294 if (!uncompress_blob(o->data.payload, l, &from->compress_buffer, &from->compress_buffer_size, &rsize))
2297 data = from->compress_buffer;
2300 return -EPROTONOSUPPORT;
2303 data = o->data.payload;
2305 r = journal_file_append_data(to, data, l, &u, &h);
2309 xor_hash ^= le64toh(u->data.hash);
2310 items[i].object_offset = htole64(h);
2311 items[i].hash = u->data.hash;
2313 r = journal_file_move_to_object(from, OBJECT_ENTRY, p, &o);
2318 return journal_file_append_entry_internal(to, &ts, xor_hash, items, n, seqnum, ret, offset);
2321 void journal_default_metrics(JournalMetrics *m, int fd) {
2322 uint64_t fs_size = 0;
2324 char a[FORMAT_BYTES_MAX], b[FORMAT_BYTES_MAX], c[FORMAT_BYTES_MAX], d[FORMAT_BYTES_MAX];
2329 if (fstatvfs(fd, &ss) >= 0)
2330 fs_size = ss.f_frsize * ss.f_blocks;
2332 if (m->max_use == (uint64_t) -1) {
2335 m->max_use = PAGE_ALIGN(fs_size / 10); /* 10% of file system size */
2337 if (m->max_use > DEFAULT_MAX_USE_UPPER)
2338 m->max_use = DEFAULT_MAX_USE_UPPER;
2340 if (m->max_use < DEFAULT_MAX_USE_LOWER)
2341 m->max_use = DEFAULT_MAX_USE_LOWER;
2343 m->max_use = DEFAULT_MAX_USE_LOWER;
2345 m->max_use = PAGE_ALIGN(m->max_use);
2347 if (m->max_use < JOURNAL_FILE_SIZE_MIN*2)
2348 m->max_use = JOURNAL_FILE_SIZE_MIN*2;
2351 if (m->max_size == (uint64_t) -1) {
2352 m->max_size = PAGE_ALIGN(m->max_use / 8); /* 8 chunks */
2354 if (m->max_size > DEFAULT_MAX_SIZE_UPPER)
2355 m->max_size = DEFAULT_MAX_SIZE_UPPER;
2357 m->max_size = PAGE_ALIGN(m->max_size);
2359 if (m->max_size < JOURNAL_FILE_SIZE_MIN)
2360 m->max_size = JOURNAL_FILE_SIZE_MIN;
2362 if (m->max_size*2 > m->max_use)
2363 m->max_use = m->max_size*2;
2365 if (m->min_size == (uint64_t) -1)
2366 m->min_size = JOURNAL_FILE_SIZE_MIN;
2368 m->min_size = PAGE_ALIGN(m->min_size);
2370 if (m->min_size < JOURNAL_FILE_SIZE_MIN)
2371 m->min_size = JOURNAL_FILE_SIZE_MIN;
2373 if (m->min_size > m->max_size)
2374 m->max_size = m->min_size;
2377 if (m->keep_free == (uint64_t) -1) {
2380 m->keep_free = PAGE_ALIGN(fs_size / 20); /* 5% of file system size */
2382 if (m->keep_free > DEFAULT_KEEP_FREE_UPPER)
2383 m->keep_free = DEFAULT_KEEP_FREE_UPPER;
2386 m->keep_free = DEFAULT_KEEP_FREE;
2389 log_info("Fixed max_use=%s max_size=%s min_size=%s keep_free=%s",
2390 format_bytes(a, sizeof(a), m->max_use),
2391 format_bytes(b, sizeof(b), m->max_size),
2392 format_bytes(c, sizeof(c), m->min_size),
2393 format_bytes(d, sizeof(d), m->keep_free));
2396 int journal_file_get_cutoff_realtime_usec(JournalFile *f, usec_t *from, usec_t *to) {
2401 if (f->header->head_entry_realtime == 0)
2404 *from = le64toh(f->header->head_entry_realtime);
2408 if (f->header->tail_entry_realtime == 0)
2411 *to = le64toh(f->header->tail_entry_realtime);
2417 int journal_file_get_cutoff_monotonic_usec(JournalFile *f, sd_id128_t boot_id, usec_t *from, usec_t *to) {
2418 char t[9+32+1] = "_BOOT_ID=";
2426 sd_id128_to_string(boot_id, t + 9);
2428 r = journal_file_find_data_object(f, t, strlen(t), &o, &p);
2432 if (le64toh(o->data.n_entries) <= 0)
2436 r = journal_file_move_to_object(f, OBJECT_ENTRY, le64toh(o->data.entry_offset), &o);
2440 *from = le64toh(o->entry.monotonic);
2444 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
2448 r = generic_array_get_plus_one(f,
2449 le64toh(o->data.entry_offset),
2450 le64toh(o->data.entry_array_offset),
2451 le64toh(o->data.n_entries)-1,
2456 *to = le64toh(o->entry.monotonic);
2462 bool journal_file_rotate_suggested(JournalFile *f) {
2465 /* If we gained new header fields we gained new features,
2466 * hence suggest a rotation */
2467 if (le64toh(f->header->header_size) < sizeof(Header)) {
2468 log_debug("%s uses an outdated header, suggesting rotation.", f->path);
2472 /* Let's check if the hash tables grew over a certain fill
2473 * level (75%, borrowing this value from Java's hash table
2474 * implementation), and if so suggest a rotation. To calculate
2475 * the fill level we need the n_data field, which only exists
2476 * in newer versions. */
2478 if (JOURNAL_HEADER_CONTAINS(f->header, n_data))
2479 if (le64toh(f->header->n_data) * 4ULL > (le64toh(f->header->data_hash_table_size) / sizeof(HashItem)) * 3ULL) {
2480 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.",
2482 100.0 * (double) le64toh(f->header->n_data) / ((double) (le64toh(f->header->data_hash_table_size) / sizeof(HashItem))),
2483 (unsigned long long) le64toh(f->header->n_data),
2484 (unsigned long long) (le64toh(f->header->data_hash_table_size) / sizeof(HashItem)),
2485 (unsigned long long) (f->last_stat.st_size),
2486 (unsigned long long) (f->last_stat.st_size / le64toh(f->header->n_data)));
2490 if (JOURNAL_HEADER_CONTAINS(f->header, n_fields))
2491 if (le64toh(f->header->n_fields) * 4ULL > (le64toh(f->header->field_hash_table_size) / sizeof(HashItem)) * 3ULL) {
2492 log_debug("Field hash table of %s has a fill level at %.1f (%llu of %llu items), suggesting rotation.",
2494 100.0 * (double) le64toh(f->header->n_fields) / ((double) (le64toh(f->header->field_hash_table_size) / sizeof(HashItem))),
2495 (unsigned long long) le64toh(f->header->n_fields),
2496 (unsigned long long) (le64toh(f->header->field_hash_table_size) / sizeof(HashItem)));