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 uint64_t journal_file_hash_table_n_items(Object *o) {
803 assert(o->object.type == OBJECT_DATA_HASH_TABLE ||
804 o->object.type == OBJECT_FIELD_HASH_TABLE);
806 return (le64toh(o->object.size) - offsetof(Object, hash_table.items)) / sizeof(HashItem);
809 static int link_entry_into_array(JournalFile *f,
814 uint64_t n = 0, ap = 0, q, i, a, hidx;
823 i = hidx = le64toh(*idx);
826 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
830 n = journal_file_entry_array_n_items(o);
832 o->entry_array.items[i] = htole64(p);
833 *idx = htole64(hidx + 1);
839 a = le64toh(o->entry_array.next_entry_array_offset);
850 r = journal_file_append_object(f, OBJECT_ENTRY_ARRAY,
851 offsetof(Object, entry_array.items) + n * sizeof(uint64_t),
856 r = journal_file_hmac_put_object(f, OBJECT_ENTRY_ARRAY, q);
860 o->entry_array.items[i] = htole64(p);
865 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, ap, &o);
869 o->entry_array.next_entry_array_offset = htole64(q);
872 if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays))
873 f->header->n_entry_arrays = htole64(le64toh(f->header->n_entry_arrays) + 1);
875 *idx = htole64(hidx + 1);
880 static int link_entry_into_array_plus_one(JournalFile *f,
899 i = htole64(le64toh(*idx) - 1);
900 r = link_entry_into_array(f, first, &i, p);
905 *idx = htole64(le64toh(*idx) + 1);
909 static int journal_file_link_entry_item(JournalFile *f, Object *o, uint64_t offset, uint64_t i) {
916 p = le64toh(o->entry.items[i].object_offset);
920 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
924 return link_entry_into_array_plus_one(f,
925 &o->data.entry_offset,
926 &o->data.entry_array_offset,
931 static int journal_file_link_entry(JournalFile *f, Object *o, uint64_t offset) {
938 assert(o->object.type == OBJECT_ENTRY);
940 __sync_synchronize();
942 /* Link up the entry itself */
943 r = link_entry_into_array(f,
944 &f->header->entry_array_offset,
945 &f->header->n_entries,
950 /* log_debug("=> %s seqnr=%lu n_entries=%lu", f->path, (unsigned long) o->entry.seqnum, (unsigned long) f->header->n_entries); */
952 if (f->header->head_entry_realtime == 0)
953 f->header->head_entry_realtime = o->entry.realtime;
955 f->header->tail_entry_realtime = o->entry.realtime;
956 f->header->tail_entry_monotonic = o->entry.monotonic;
958 f->tail_entry_monotonic_valid = true;
960 /* Link up the items */
961 n = journal_file_entry_n_items(o);
962 for (i = 0; i < n; i++) {
963 r = journal_file_link_entry_item(f, o, offset, i);
971 static int journal_file_append_entry_internal(
973 const dual_timestamp *ts,
975 const EntryItem items[], unsigned n_items,
977 Object **ret, uint64_t *offset) {
984 assert(items || n_items == 0);
987 osize = offsetof(Object, entry.items) + (n_items * sizeof(EntryItem));
989 r = journal_file_append_object(f, OBJECT_ENTRY, osize, &o, &np);
993 o->entry.seqnum = htole64(journal_file_entry_seqnum(f, seqnum));
994 memcpy(o->entry.items, items, n_items * sizeof(EntryItem));
995 o->entry.realtime = htole64(ts->realtime);
996 o->entry.monotonic = htole64(ts->monotonic);
997 o->entry.xor_hash = htole64(xor_hash);
998 o->entry.boot_id = f->header->boot_id;
1000 r = journal_file_hmac_put_object(f, OBJECT_ENTRY, np);
1004 r = journal_file_link_entry(f, o, np);
1017 void journal_file_post_change(JournalFile *f) {
1020 /* inotify() does not receive IN_MODIFY events from file
1021 * accesses done via mmap(). After each access we hence
1022 * trigger IN_MODIFY by truncating the journal file to its
1023 * current size which triggers IN_MODIFY. */
1025 __sync_synchronize();
1027 if (ftruncate(f->fd, f->last_stat.st_size) < 0)
1028 log_error("Failed to to truncate file to its own size: %m");
1031 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) {
1035 uint64_t xor_hash = 0;
1036 struct dual_timestamp _ts;
1039 assert(iovec || n_iovec == 0);
1045 dual_timestamp_get(&_ts);
1049 if (f->tail_entry_monotonic_valid &&
1050 ts->monotonic < le64toh(f->header->tail_entry_monotonic))
1053 r = journal_file_maybe_append_tag(f, ts->realtime);
1057 /* alloca() can't take 0, hence let's allocate at least one */
1058 items = alloca(sizeof(EntryItem) * MAX(1, n_iovec));
1060 for (i = 0; i < n_iovec; i++) {
1064 r = journal_file_append_data(f, iovec[i].iov_base, iovec[i].iov_len, &o, &p);
1068 xor_hash ^= le64toh(o->data.hash);
1069 items[i].object_offset = htole64(p);
1070 items[i].hash = o->data.hash;
1073 r = journal_file_append_entry_internal(f, ts, xor_hash, items, n_iovec, seqnum, ret, offset);
1075 journal_file_post_change(f);
1080 static int generic_array_get(JournalFile *f,
1083 Object **ret, uint64_t *offset) {
1095 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
1099 n = journal_file_entry_array_n_items(o);
1101 p = le64toh(o->entry_array.items[i]);
1106 a = le64toh(o->entry_array.next_entry_array_offset);
1109 if (a <= 0 || p <= 0)
1112 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
1125 static int generic_array_get_plus_one(JournalFile *f,
1129 Object **ret, uint64_t *offset) {
1138 r = journal_file_move_to_object(f, OBJECT_ENTRY, extra, &o);
1151 return generic_array_get(f, first, i-1, ret, offset);
1160 static int generic_array_bisect(JournalFile *f,
1164 int (*test_object)(JournalFile *f, uint64_t p, uint64_t needle),
1165 direction_t direction,
1170 uint64_t a, p, t = 0, i = 0, last_p = 0;
1171 bool subtract_one = false;
1172 Object *o, *array = NULL;
1176 assert(test_object);
1180 uint64_t left, right, k, lp;
1182 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &array);
1186 k = journal_file_entry_array_n_items(array);
1192 lp = p = le64toh(array->entry_array.items[i]);
1196 r = test_object(f, p, needle);
1200 if (r == TEST_FOUND)
1201 r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT;
1203 if (r == TEST_RIGHT) {
1207 if (left == right) {
1208 if (direction == DIRECTION_UP)
1209 subtract_one = true;
1215 assert(left < right);
1217 i = (left + right) / 2;
1218 p = le64toh(array->entry_array.items[i]);
1222 r = test_object(f, p, needle);
1226 if (r == TEST_FOUND)
1227 r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT;
1229 if (r == TEST_RIGHT)
1237 if (direction == DIRECTION_UP) {
1239 subtract_one = true;
1250 a = le64toh(array->entry_array.next_entry_array_offset);
1256 if (subtract_one && t == 0 && i == 0)
1259 if (subtract_one && i == 0)
1261 else if (subtract_one)
1262 p = le64toh(array->entry_array.items[i-1]);
1264 p = le64toh(array->entry_array.items[i]);
1266 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
1277 *idx = t + i + (subtract_one ? -1 : 0);
1282 static int generic_array_bisect_plus_one(JournalFile *f,
1287 int (*test_object)(JournalFile *f, uint64_t p, uint64_t needle),
1288 direction_t direction,
1294 bool step_back = false;
1298 assert(test_object);
1303 /* This bisects the array in object 'first', but first checks
1305 r = test_object(f, extra, needle);
1309 if (r == TEST_FOUND)
1310 r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT;
1312 /* if we are looking with DIRECTION_UP then we need to first
1313 see if in the actual array there is a matching entry, and
1314 return the last one of that. But if there isn't any we need
1315 to return this one. Hence remember this, and return it
1318 step_back = direction == DIRECTION_UP;
1320 if (r == TEST_RIGHT) {
1321 if (direction == DIRECTION_DOWN)
1327 r = generic_array_bisect(f, first, n-1, needle, test_object, direction, ret, offset, idx);
1329 if (r == 0 && step_back)
1338 r = journal_file_move_to_object(f, OBJECT_ENTRY, extra, &o);
1354 static int test_object_offset(JournalFile *f, uint64_t p, uint64_t needle) {
1360 else if (p < needle)
1366 int journal_file_move_to_entry_by_offset(
1369 direction_t direction,
1373 return generic_array_bisect(f,
1374 le64toh(f->header->entry_array_offset),
1375 le64toh(f->header->n_entries),
1383 static int test_object_seqnum(JournalFile *f, uint64_t p, uint64_t needle) {
1390 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
1394 if (le64toh(o->entry.seqnum) == needle)
1396 else if (le64toh(o->entry.seqnum) < needle)
1402 int journal_file_move_to_entry_by_seqnum(
1405 direction_t direction,
1409 return generic_array_bisect(f,
1410 le64toh(f->header->entry_array_offset),
1411 le64toh(f->header->n_entries),
1418 static int test_object_realtime(JournalFile *f, uint64_t p, uint64_t needle) {
1425 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
1429 if (le64toh(o->entry.realtime) == needle)
1431 else if (le64toh(o->entry.realtime) < needle)
1437 int journal_file_move_to_entry_by_realtime(
1440 direction_t direction,
1444 return generic_array_bisect(f,
1445 le64toh(f->header->entry_array_offset),
1446 le64toh(f->header->n_entries),
1448 test_object_realtime,
1453 static int test_object_monotonic(JournalFile *f, uint64_t p, uint64_t needle) {
1460 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
1464 if (le64toh(o->entry.monotonic) == needle)
1466 else if (le64toh(o->entry.monotonic) < needle)
1472 int journal_file_move_to_entry_by_monotonic(
1476 direction_t direction,
1480 char t[9+32+1] = "_BOOT_ID=";
1486 sd_id128_to_string(boot_id, t + 9);
1487 r = journal_file_find_data_object(f, t, strlen(t), &o, NULL);
1493 return generic_array_bisect_plus_one(f,
1494 le64toh(o->data.entry_offset),
1495 le64toh(o->data.entry_array_offset),
1496 le64toh(o->data.n_entries),
1498 test_object_monotonic,
1503 int journal_file_next_entry(
1505 Object *o, uint64_t p,
1506 direction_t direction,
1507 Object **ret, uint64_t *offset) {
1513 assert(p > 0 || !o);
1515 n = le64toh(f->header->n_entries);
1520 i = direction == DIRECTION_DOWN ? 0 : n - 1;
1522 if (o->object.type != OBJECT_ENTRY)
1525 r = generic_array_bisect(f,
1526 le64toh(f->header->entry_array_offset),
1527 le64toh(f->header->n_entries),
1536 if (direction == DIRECTION_DOWN) {
1549 /* And jump to it */
1550 return generic_array_get(f,
1551 le64toh(f->header->entry_array_offset),
1556 int journal_file_skip_entry(
1558 Object *o, uint64_t p,
1560 Object **ret, uint64_t *offset) {
1569 if (o->object.type != OBJECT_ENTRY)
1572 r = generic_array_bisect(f,
1573 le64toh(f->header->entry_array_offset),
1574 le64toh(f->header->n_entries),
1583 /* Calculate new index */
1585 if ((uint64_t) -skip >= i)
1588 i = i - (uint64_t) -skip;
1590 i += (uint64_t) skip;
1592 n = le64toh(f->header->n_entries);
1599 return generic_array_get(f,
1600 le64toh(f->header->entry_array_offset),
1605 int journal_file_next_entry_for_data(
1607 Object *o, uint64_t p,
1608 uint64_t data_offset,
1609 direction_t direction,
1610 Object **ret, uint64_t *offset) {
1617 assert(p > 0 || !o);
1619 r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
1623 n = le64toh(d->data.n_entries);
1628 i = direction == DIRECTION_DOWN ? 0 : n - 1;
1630 if (o->object.type != OBJECT_ENTRY)
1633 r = generic_array_bisect_plus_one(f,
1634 le64toh(d->data.entry_offset),
1635 le64toh(d->data.entry_array_offset),
1636 le64toh(d->data.n_entries),
1646 if (direction == DIRECTION_DOWN) {
1660 return generic_array_get_plus_one(f,
1661 le64toh(d->data.entry_offset),
1662 le64toh(d->data.entry_array_offset),
1667 int journal_file_move_to_entry_by_offset_for_data(
1669 uint64_t data_offset,
1671 direction_t direction,
1672 Object **ret, uint64_t *offset) {
1679 r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
1683 return generic_array_bisect_plus_one(f,
1684 le64toh(d->data.entry_offset),
1685 le64toh(d->data.entry_array_offset),
1686 le64toh(d->data.n_entries),
1693 int journal_file_move_to_entry_by_monotonic_for_data(
1695 uint64_t data_offset,
1698 direction_t direction,
1699 Object **ret, uint64_t *offset) {
1701 char t[9+32+1] = "_BOOT_ID=";
1708 /* First, seek by time */
1709 sd_id128_to_string(boot_id, t + 9);
1710 r = journal_file_find_data_object(f, t, strlen(t), &o, &b);
1716 r = generic_array_bisect_plus_one(f,
1717 le64toh(o->data.entry_offset),
1718 le64toh(o->data.entry_array_offset),
1719 le64toh(o->data.n_entries),
1721 test_object_monotonic,
1727 /* And now, continue seeking until we find an entry that
1728 * exists in both bisection arrays */
1734 r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
1738 r = generic_array_bisect_plus_one(f,
1739 le64toh(d->data.entry_offset),
1740 le64toh(d->data.entry_array_offset),
1741 le64toh(d->data.n_entries),
1749 r = journal_file_move_to_object(f, OBJECT_DATA, b, &o);
1753 r = generic_array_bisect_plus_one(f,
1754 le64toh(o->data.entry_offset),
1755 le64toh(o->data.entry_array_offset),
1756 le64toh(o->data.n_entries),
1780 int journal_file_move_to_entry_by_seqnum_for_data(
1782 uint64_t data_offset,
1784 direction_t direction,
1785 Object **ret, uint64_t *offset) {
1792 r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
1796 return generic_array_bisect_plus_one(f,
1797 le64toh(d->data.entry_offset),
1798 le64toh(d->data.entry_array_offset),
1799 le64toh(d->data.n_entries),
1806 int journal_file_move_to_entry_by_realtime_for_data(
1808 uint64_t data_offset,
1810 direction_t direction,
1811 Object **ret, uint64_t *offset) {
1818 r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
1822 return generic_array_bisect_plus_one(f,
1823 le64toh(d->data.entry_offset),
1824 le64toh(d->data.entry_array_offset),
1825 le64toh(d->data.n_entries),
1827 test_object_realtime,
1832 void journal_file_dump(JournalFile *f) {
1839 journal_file_print_header(f);
1841 p = le64toh(f->header->header_size);
1843 r = journal_file_move_to_object(f, -1, p, &o);
1847 switch (o->object.type) {
1850 printf("Type: OBJECT_UNUSED\n");
1854 printf("Type: OBJECT_DATA\n");
1858 printf("Type: OBJECT_ENTRY %llu %llu %llu\n",
1859 (unsigned long long) le64toh(o->entry.seqnum),
1860 (unsigned long long) le64toh(o->entry.monotonic),
1861 (unsigned long long) le64toh(o->entry.realtime));
1864 case OBJECT_FIELD_HASH_TABLE:
1865 printf("Type: OBJECT_FIELD_HASH_TABLE\n");
1868 case OBJECT_DATA_HASH_TABLE:
1869 printf("Type: OBJECT_DATA_HASH_TABLE\n");
1872 case OBJECT_ENTRY_ARRAY:
1873 printf("Type: OBJECT_ENTRY_ARRAY\n");
1877 printf("Type: OBJECT_TAG %llu\n",
1878 (unsigned long long) le64toh(o->tag.seqnum));
1882 if (o->object.flags & OBJECT_COMPRESSED)
1883 printf("Flags: COMPRESSED\n");
1885 if (p == le64toh(f->header->tail_object_offset))
1888 p = p + ALIGN64(le64toh(o->object.size));
1893 log_error("File corrupt");
1896 void journal_file_print_header(JournalFile *f) {
1897 char a[33], b[33], c[33];
1898 char x[FORMAT_TIMESTAMP_MAX], y[FORMAT_TIMESTAMP_MAX];
1902 printf("File Path: %s\n"
1906 "Sequential Number ID: %s\n"
1908 "Compatible Flags:%s%s\n"
1909 "Incompatible Flags:%s%s\n"
1910 "Header size: %llu\n"
1911 "Arena size: %llu\n"
1912 "Data Hash Table Size: %llu\n"
1913 "Field Hash Table Size: %llu\n"
1914 "Rotate Suggested: %s\n"
1915 "Head Sequential Number: %llu\n"
1916 "Tail Sequential Number: %llu\n"
1917 "Head Realtime Timestamp: %s\n"
1918 "Tail Realtime Timestamp: %s\n"
1920 "Entry Objects: %llu\n",
1922 sd_id128_to_string(f->header->file_id, a),
1923 sd_id128_to_string(f->header->machine_id, b),
1924 sd_id128_to_string(f->header->boot_id, c),
1925 sd_id128_to_string(f->header->seqnum_id, c),
1926 f->header->state == STATE_OFFLINE ? "OFFLINE" :
1927 f->header->state == STATE_ONLINE ? "ONLINE" :
1928 f->header->state == STATE_ARCHIVED ? "ARCHIVED" : "UNKNOWN",
1929 (f->header->compatible_flags & HEADER_COMPATIBLE_SEALED) ? " SEALED" : "",
1930 (f->header->compatible_flags & ~HEADER_COMPATIBLE_SEALED) ? " ???" : "",
1931 (f->header->incompatible_flags & HEADER_INCOMPATIBLE_COMPRESSED) ? " COMPRESSED" : "",
1932 (f->header->incompatible_flags & ~HEADER_INCOMPATIBLE_COMPRESSED) ? " ???" : "",
1933 (unsigned long long) le64toh(f->header->header_size),
1934 (unsigned long long) le64toh(f->header->arena_size),
1935 (unsigned long long) le64toh(f->header->data_hash_table_size) / sizeof(HashItem),
1936 (unsigned long long) le64toh(f->header->field_hash_table_size) / sizeof(HashItem),
1937 yes_no(journal_file_rotate_suggested(f)),
1938 (unsigned long long) le64toh(f->header->head_entry_seqnum),
1939 (unsigned long long) le64toh(f->header->tail_entry_seqnum),
1940 format_timestamp(x, sizeof(x), le64toh(f->header->head_entry_realtime)),
1941 format_timestamp(y, sizeof(y), le64toh(f->header->tail_entry_realtime)),
1942 (unsigned long long) le64toh(f->header->n_objects),
1943 (unsigned long long) le64toh(f->header->n_entries));
1945 if (JOURNAL_HEADER_CONTAINS(f->header, n_data))
1946 printf("Data Objects: %llu\n"
1947 "Data Hash Table Fill: %.1f%%\n",
1948 (unsigned long long) le64toh(f->header->n_data),
1949 100.0 * (double) le64toh(f->header->n_data) / ((double) (le64toh(f->header->data_hash_table_size) / sizeof(HashItem))));
1951 if (JOURNAL_HEADER_CONTAINS(f->header, n_fields))
1952 printf("Field Objects: %llu\n"
1953 "Field Hash Table Fill: %.1f%%\n",
1954 (unsigned long long) le64toh(f->header->n_fields),
1955 100.0 * (double) le64toh(f->header->n_fields) / ((double) (le64toh(f->header->field_hash_table_size) / sizeof(HashItem))));
1957 if (JOURNAL_HEADER_CONTAINS(f->header, n_tags))
1958 printf("Tag Objects: %llu\n",
1959 (unsigned long long) le64toh(f->header->n_tags));
1960 if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays))
1961 printf("Entry Array Objects: %llu\n",
1962 (unsigned long long) le64toh(f->header->n_entry_arrays));
1965 int journal_file_open(
1971 JournalMetrics *metrics,
1972 MMapCache *mmap_cache,
1973 JournalFile *template,
1974 JournalFile **ret) {
1978 bool newly_created = false;
1982 if ((flags & O_ACCMODE) != O_RDONLY &&
1983 (flags & O_ACCMODE) != O_RDWR)
1986 if (!endswith(fname, ".journal") &&
1987 !endswith(fname, ".journal~"))
1990 f = new0(JournalFile, 1);
1998 f->prot = prot_from_flags(flags);
1999 f->writable = (flags & O_ACCMODE) != O_RDONLY;
2000 f->compress = compress;
2004 f->mmap = mmap_cache_ref(mmap_cache);
2006 f->mmap = mmap_cache_new();
2013 f->path = strdup(fname);
2019 f->fd = open(f->path, f->flags|O_CLOEXEC, f->mode);
2025 if (fstat(f->fd, &f->last_stat) < 0) {
2030 if (f->last_stat.st_size == 0 && f->writable) {
2031 newly_created = true;
2033 /* Try to load the FSPRG state, and if we can't, then
2034 * just don't do sealing */
2035 r = journal_file_fss_load(f);
2039 r = journal_file_init_header(f, template);
2043 if (fstat(f->fd, &f->last_stat) < 0) {
2049 if (f->last_stat.st_size < (off_t) HEADER_SIZE_MIN) {
2054 f->header = mmap(NULL, PAGE_ALIGN(sizeof(Header)), prot_from_flags(flags), MAP_SHARED, f->fd, 0);
2055 if (f->header == MAP_FAILED) {
2061 if (!newly_created) {
2062 r = journal_file_verify_header(f);
2067 if (!newly_created && f->writable) {
2068 r = journal_file_fss_load(f);
2075 journal_default_metrics(metrics, f->fd);
2076 f->metrics = *metrics;
2077 } else if (template)
2078 f->metrics = template->metrics;
2080 r = journal_file_refresh_header(f);
2085 r = journal_file_hmac_setup(f);
2089 if (newly_created) {
2090 r = journal_file_setup_field_hash_table(f);
2094 r = journal_file_setup_data_hash_table(f);
2098 r = journal_file_append_first_tag(f);
2103 r = journal_file_map_field_hash_table(f);
2107 r = journal_file_map_data_hash_table(f);
2117 journal_file_close(f);
2122 int journal_file_rotate(JournalFile **f, bool compress, bool seal) {
2125 JournalFile *old_file, *new_file = NULL;
2133 if (!old_file->writable)
2136 if (!endswith(old_file->path, ".journal"))
2139 l = strlen(old_file->path);
2141 p = new(char, l + 1 + 32 + 1 + 16 + 1 + 16 + 1);
2145 memcpy(p, old_file->path, l - 8);
2147 sd_id128_to_string(old_file->header->seqnum_id, p + l - 8 + 1);
2148 snprintf(p + l - 8 + 1 + 32, 1 + 16 + 1 + 16 + 8 + 1,
2149 "-%016llx-%016llx.journal",
2150 (unsigned long long) le64toh((*f)->header->tail_entry_seqnum),
2151 (unsigned long long) le64toh((*f)->header->tail_entry_realtime));
2153 r = rename(old_file->path, p);
2159 old_file->header->state = STATE_ARCHIVED;
2161 r = journal_file_open(old_file->path, old_file->flags, old_file->mode, compress, seal, NULL, old_file->mmap, old_file, &new_file);
2162 journal_file_close(old_file);
2168 int journal_file_open_reliably(
2174 JournalMetrics *metrics,
2175 MMapCache *mmap_cache,
2176 JournalFile *template,
2177 JournalFile **ret) {
2183 r = journal_file_open(fname, flags, mode, compress, seal,
2184 metrics, mmap_cache, template, ret);
2185 if (r != -EBADMSG && /* corrupted */
2186 r != -ENODATA && /* truncated */
2187 r != -EHOSTDOWN && /* other machine */
2188 r != -EPROTONOSUPPORT && /* incompatible feature */
2189 r != -EBUSY && /* unclean shutdown */
2190 r != -ESHUTDOWN /* already archived */)
2193 if ((flags & O_ACCMODE) == O_RDONLY)
2196 if (!(flags & O_CREAT))
2199 if (!endswith(fname, ".journal"))
2202 /* The file is corrupted. Rotate it away and try it again (but only once) */
2205 if (asprintf(&p, "%.*s@%016llx-%016llx.journal~",
2207 (unsigned long long) now(CLOCK_REALTIME),
2211 r = rename(fname, p);
2216 log_warning("File %s corrupted or uncleanly shut down, renaming and replacing.", fname);
2218 return journal_file_open(fname, flags, mode, compress, seal,
2219 metrics, mmap_cache, template, ret);
2223 int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint64_t p, uint64_t *seqnum, Object **ret, uint64_t *offset) {
2225 uint64_t q, xor_hash = 0;
2238 ts.monotonic = le64toh(o->entry.monotonic);
2239 ts.realtime = le64toh(o->entry.realtime);
2241 if (to->tail_entry_monotonic_valid &&
2242 ts.monotonic < le64toh(to->header->tail_entry_monotonic))
2245 n = journal_file_entry_n_items(o);
2246 items = alloca(sizeof(EntryItem) * n);
2248 for (i = 0; i < n; i++) {
2255 q = le64toh(o->entry.items[i].object_offset);
2256 le_hash = o->entry.items[i].hash;
2258 r = journal_file_move_to_object(from, OBJECT_DATA, q, &o);
2262 if (le_hash != o->data.hash)
2265 l = le64toh(o->object.size) - offsetof(Object, data.payload);
2268 /* We hit the limit on 32bit machines */
2269 if ((uint64_t) t != l)
2272 if (o->object.flags & OBJECT_COMPRESSED) {
2276 if (!uncompress_blob(o->data.payload, l, &from->compress_buffer, &from->compress_buffer_size, &rsize))
2279 data = from->compress_buffer;
2282 return -EPROTONOSUPPORT;
2285 data = o->data.payload;
2287 r = journal_file_append_data(to, data, l, &u, &h);
2291 xor_hash ^= le64toh(u->data.hash);
2292 items[i].object_offset = htole64(h);
2293 items[i].hash = u->data.hash;
2295 r = journal_file_move_to_object(from, OBJECT_ENTRY, p, &o);
2300 return journal_file_append_entry_internal(to, &ts, xor_hash, items, n, seqnum, ret, offset);
2303 void journal_default_metrics(JournalMetrics *m, int fd) {
2304 uint64_t fs_size = 0;
2306 char a[FORMAT_BYTES_MAX], b[FORMAT_BYTES_MAX], c[FORMAT_BYTES_MAX], d[FORMAT_BYTES_MAX];
2311 if (fstatvfs(fd, &ss) >= 0)
2312 fs_size = ss.f_frsize * ss.f_blocks;
2314 if (m->max_use == (uint64_t) -1) {
2317 m->max_use = PAGE_ALIGN(fs_size / 10); /* 10% of file system size */
2319 if (m->max_use > DEFAULT_MAX_USE_UPPER)
2320 m->max_use = DEFAULT_MAX_USE_UPPER;
2322 if (m->max_use < DEFAULT_MAX_USE_LOWER)
2323 m->max_use = DEFAULT_MAX_USE_LOWER;
2325 m->max_use = DEFAULT_MAX_USE_LOWER;
2327 m->max_use = PAGE_ALIGN(m->max_use);
2329 if (m->max_use < JOURNAL_FILE_SIZE_MIN*2)
2330 m->max_use = JOURNAL_FILE_SIZE_MIN*2;
2333 if (m->max_size == (uint64_t) -1) {
2334 m->max_size = PAGE_ALIGN(m->max_use / 8); /* 8 chunks */
2336 if (m->max_size > DEFAULT_MAX_SIZE_UPPER)
2337 m->max_size = DEFAULT_MAX_SIZE_UPPER;
2339 m->max_size = PAGE_ALIGN(m->max_size);
2341 if (m->max_size < JOURNAL_FILE_SIZE_MIN)
2342 m->max_size = JOURNAL_FILE_SIZE_MIN;
2344 if (m->max_size*2 > m->max_use)
2345 m->max_use = m->max_size*2;
2347 if (m->min_size == (uint64_t) -1)
2348 m->min_size = JOURNAL_FILE_SIZE_MIN;
2350 m->min_size = PAGE_ALIGN(m->min_size);
2352 if (m->min_size < JOURNAL_FILE_SIZE_MIN)
2353 m->min_size = JOURNAL_FILE_SIZE_MIN;
2355 if (m->min_size > m->max_size)
2356 m->max_size = m->min_size;
2359 if (m->keep_free == (uint64_t) -1) {
2362 m->keep_free = PAGE_ALIGN(fs_size / 20); /* 5% of file system size */
2364 if (m->keep_free > DEFAULT_KEEP_FREE_UPPER)
2365 m->keep_free = DEFAULT_KEEP_FREE_UPPER;
2368 m->keep_free = DEFAULT_KEEP_FREE;
2371 log_info("Fixed max_use=%s max_size=%s min_size=%s keep_free=%s",
2372 format_bytes(a, sizeof(a), m->max_use),
2373 format_bytes(b, sizeof(b), m->max_size),
2374 format_bytes(c, sizeof(c), m->min_size),
2375 format_bytes(d, sizeof(d), m->keep_free));
2378 int journal_file_get_cutoff_realtime_usec(JournalFile *f, usec_t *from, usec_t *to) {
2383 if (f->header->head_entry_realtime == 0)
2386 *from = le64toh(f->header->head_entry_realtime);
2390 if (f->header->tail_entry_realtime == 0)
2393 *to = le64toh(f->header->tail_entry_realtime);
2399 int journal_file_get_cutoff_monotonic_usec(JournalFile *f, sd_id128_t boot_id, usec_t *from, usec_t *to) {
2400 char t[9+32+1] = "_BOOT_ID=";
2408 sd_id128_to_string(boot_id, t + 9);
2410 r = journal_file_find_data_object(f, t, strlen(t), &o, &p);
2414 if (le64toh(o->data.n_entries) <= 0)
2418 r = journal_file_move_to_object(f, OBJECT_ENTRY, le64toh(o->data.entry_offset), &o);
2422 *from = le64toh(o->entry.monotonic);
2426 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
2430 r = generic_array_get_plus_one(f,
2431 le64toh(o->data.entry_offset),
2432 le64toh(o->data.entry_array_offset),
2433 le64toh(o->data.n_entries)-1,
2438 *to = le64toh(o->entry.monotonic);
2444 bool journal_file_rotate_suggested(JournalFile *f) {
2447 /* If we gained new header fields we gained new features,
2448 * hence suggest a rotation */
2449 if (le64toh(f->header->header_size) < sizeof(Header)) {
2450 log_debug("%s uses an outdated header, suggesting rotation.", f->path);
2454 /* Let's check if the hash tables grew over a certain fill
2455 * level (75%, borrowing this value from Java's hash table
2456 * implementation), and if so suggest a rotation. To calculate
2457 * the fill level we need the n_data field, which only exists
2458 * in newer versions. */
2460 if (JOURNAL_HEADER_CONTAINS(f->header, n_data))
2461 if (le64toh(f->header->n_data) * 4ULL > (le64toh(f->header->data_hash_table_size) / sizeof(HashItem)) * 3ULL) {
2462 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.",
2464 100.0 * (double) le64toh(f->header->n_data) / ((double) (le64toh(f->header->data_hash_table_size) / sizeof(HashItem))),
2465 (unsigned long long) le64toh(f->header->n_data),
2466 (unsigned long long) (le64toh(f->header->data_hash_table_size) / sizeof(HashItem)),
2467 (unsigned long long) (f->last_stat.st_size),
2468 (unsigned long long) (f->last_stat.st_size / le64toh(f->header->n_data)));
2472 if (JOURNAL_HEADER_CONTAINS(f->header, n_fields))
2473 if (le64toh(f->header->n_fields) * 4ULL > (le64toh(f->header->field_hash_table_size) / sizeof(HashItem)) * 3ULL) {
2474 log_debug("Field hash table of %s has a fill level at %.1f (%llu of %llu items), suggesting rotation.",
2476 100.0 * (double) le64toh(f->header->n_fields) / ((double) (le64toh(f->header->field_hash_table_size) / sizeof(HashItem))),
2477 (unsigned long long) le64toh(f->header->n_fields),
2478 (unsigned long long) (le64toh(f->header->field_hash_table_size) / sizeof(HashItem)));