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_debug("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];
1910 char bytes[FORMAT_BYTES_MAX];
1914 printf("File Path: %s\n"
1918 "Sequential Number ID: %s\n"
1920 "Compatible Flags:%s%s\n"
1921 "Incompatible Flags:%s%s\n"
1922 "Header size: %llu\n"
1923 "Arena size: %llu\n"
1924 "Data Hash Table Size: %llu\n"
1925 "Field Hash Table Size: %llu\n"
1926 "Rotate Suggested: %s\n"
1927 "Head Sequential Number: %llu\n"
1928 "Tail Sequential Number: %llu\n"
1929 "Head Realtime Timestamp: %s\n"
1930 "Tail Realtime Timestamp: %s\n"
1932 "Entry Objects: %llu\n",
1934 sd_id128_to_string(f->header->file_id, a),
1935 sd_id128_to_string(f->header->machine_id, b),
1936 sd_id128_to_string(f->header->boot_id, c),
1937 sd_id128_to_string(f->header->seqnum_id, c),
1938 f->header->state == STATE_OFFLINE ? "OFFLINE" :
1939 f->header->state == STATE_ONLINE ? "ONLINE" :
1940 f->header->state == STATE_ARCHIVED ? "ARCHIVED" : "UNKNOWN",
1941 JOURNAL_HEADER_SEALED(f->header) ? " SEALED" : "",
1942 (le32toh(f->header->compatible_flags) & ~HEADER_COMPATIBLE_SEALED) ? " ???" : "",
1943 JOURNAL_HEADER_COMPRESSED(f->header) ? " COMPRESSED" : "",
1944 (le32toh(f->header->incompatible_flags) & ~HEADER_INCOMPATIBLE_COMPRESSED) ? " ???" : "",
1945 (unsigned long long) le64toh(f->header->header_size),
1946 (unsigned long long) le64toh(f->header->arena_size),
1947 (unsigned long long) le64toh(f->header->data_hash_table_size) / sizeof(HashItem),
1948 (unsigned long long) le64toh(f->header->field_hash_table_size) / sizeof(HashItem),
1949 yes_no(journal_file_rotate_suggested(f)),
1950 (unsigned long long) le64toh(f->header->head_entry_seqnum),
1951 (unsigned long long) le64toh(f->header->tail_entry_seqnum),
1952 format_timestamp(x, sizeof(x), le64toh(f->header->head_entry_realtime)),
1953 format_timestamp(y, sizeof(y), le64toh(f->header->tail_entry_realtime)),
1954 (unsigned long long) le64toh(f->header->n_objects),
1955 (unsigned long long) le64toh(f->header->n_entries));
1957 if (JOURNAL_HEADER_CONTAINS(f->header, n_data))
1958 printf("Data Objects: %llu\n"
1959 "Data Hash Table Fill: %.1f%%\n",
1960 (unsigned long long) le64toh(f->header->n_data),
1961 100.0 * (double) le64toh(f->header->n_data) / ((double) (le64toh(f->header->data_hash_table_size) / sizeof(HashItem))));
1963 if (JOURNAL_HEADER_CONTAINS(f->header, n_fields))
1964 printf("Field Objects: %llu\n"
1965 "Field Hash Table Fill: %.1f%%\n",
1966 (unsigned long long) le64toh(f->header->n_fields),
1967 100.0 * (double) le64toh(f->header->n_fields) / ((double) (le64toh(f->header->field_hash_table_size) / sizeof(HashItem))));
1969 if (JOURNAL_HEADER_CONTAINS(f->header, n_tags))
1970 printf("Tag Objects: %llu\n",
1971 (unsigned long long) le64toh(f->header->n_tags));
1972 if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays))
1973 printf("Entry Array Objects: %llu\n",
1974 (unsigned long long) le64toh(f->header->n_entry_arrays));
1976 if (fstat(f->fd, &st) >= 0)
1977 printf("Disk usage: %s\n", format_bytes(bytes, sizeof(bytes), (off_t) st.st_blocks * 512ULL));
1980 int journal_file_open(
1986 JournalMetrics *metrics,
1987 MMapCache *mmap_cache,
1988 JournalFile *template,
1989 JournalFile **ret) {
1993 bool newly_created = false;
1997 if ((flags & O_ACCMODE) != O_RDONLY &&
1998 (flags & O_ACCMODE) != O_RDWR)
2001 if (!endswith(fname, ".journal") &&
2002 !endswith(fname, ".journal~"))
2005 f = new0(JournalFile, 1);
2013 f->prot = prot_from_flags(flags);
2014 f->writable = (flags & O_ACCMODE) != O_RDONLY;
2015 f->compress = compress;
2019 f->mmap = mmap_cache_ref(mmap_cache);
2021 f->mmap = mmap_cache_new();
2028 f->path = strdup(fname);
2034 f->fd = open(f->path, f->flags|O_CLOEXEC, f->mode);
2040 if (fstat(f->fd, &f->last_stat) < 0) {
2045 if (f->last_stat.st_size == 0 && f->writable) {
2046 newly_created = true;
2049 /* Try to load the FSPRG state, and if we can't, then
2050 * just don't do sealing */
2051 r = journal_file_fss_load(f);
2056 r = journal_file_init_header(f, template);
2060 if (fstat(f->fd, &f->last_stat) < 0) {
2066 if (f->last_stat.st_size < (off_t) HEADER_SIZE_MIN) {
2071 f->header = mmap(NULL, PAGE_ALIGN(sizeof(Header)), prot_from_flags(flags), MAP_SHARED, f->fd, 0);
2072 if (f->header == MAP_FAILED) {
2078 if (!newly_created) {
2079 r = journal_file_verify_header(f);
2085 if (!newly_created && f->writable) {
2086 r = journal_file_fss_load(f);
2094 journal_default_metrics(metrics, f->fd);
2095 f->metrics = *metrics;
2096 } else if (template)
2097 f->metrics = template->metrics;
2099 r = journal_file_refresh_header(f);
2105 r = journal_file_hmac_setup(f);
2110 if (newly_created) {
2111 r = journal_file_setup_field_hash_table(f);
2115 r = journal_file_setup_data_hash_table(f);
2120 r = journal_file_append_first_tag(f);
2126 r = journal_file_map_field_hash_table(f);
2130 r = journal_file_map_data_hash_table(f);
2140 journal_file_close(f);
2145 int journal_file_rotate(JournalFile **f, bool compress, bool seal) {
2148 JournalFile *old_file, *new_file = NULL;
2156 if (!old_file->writable)
2159 if (!endswith(old_file->path, ".journal"))
2162 l = strlen(old_file->path);
2164 p = new(char, l + 1 + 32 + 1 + 16 + 1 + 16 + 1);
2168 memcpy(p, old_file->path, l - 8);
2170 sd_id128_to_string(old_file->header->seqnum_id, p + l - 8 + 1);
2171 snprintf(p + l - 8 + 1 + 32, 1 + 16 + 1 + 16 + 8 + 1,
2172 "-%016llx-%016llx.journal",
2173 (unsigned long long) le64toh((*f)->header->tail_entry_seqnum),
2174 (unsigned long long) le64toh((*f)->header->tail_entry_realtime));
2176 r = rename(old_file->path, p);
2182 old_file->header->state = STATE_ARCHIVED;
2184 r = journal_file_open(old_file->path, old_file->flags, old_file->mode, compress, seal, NULL, old_file->mmap, old_file, &new_file);
2185 journal_file_close(old_file);
2191 int journal_file_open_reliably(
2197 JournalMetrics *metrics,
2198 MMapCache *mmap_cache,
2199 JournalFile *template,
2200 JournalFile **ret) {
2206 r = journal_file_open(fname, flags, mode, compress, seal,
2207 metrics, mmap_cache, template, ret);
2208 if (r != -EBADMSG && /* corrupted */
2209 r != -ENODATA && /* truncated */
2210 r != -EHOSTDOWN && /* other machine */
2211 r != -EPROTONOSUPPORT && /* incompatible feature */
2212 r != -EBUSY && /* unclean shutdown */
2213 r != -ESHUTDOWN /* already archived */)
2216 if ((flags & O_ACCMODE) == O_RDONLY)
2219 if (!(flags & O_CREAT))
2222 if (!endswith(fname, ".journal"))
2225 /* The file is corrupted. Rotate it away and try it again (but only once) */
2228 if (asprintf(&p, "%.*s@%016llx-%016llx.journal~",
2230 (unsigned long long) now(CLOCK_REALTIME),
2234 r = rename(fname, p);
2239 log_warning("File %s corrupted or uncleanly shut down, renaming and replacing.", fname);
2241 return journal_file_open(fname, flags, mode, compress, seal,
2242 metrics, mmap_cache, template, ret);
2246 int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint64_t p, uint64_t *seqnum, Object **ret, uint64_t *offset) {
2248 uint64_t q, xor_hash = 0;
2261 ts.monotonic = le64toh(o->entry.monotonic);
2262 ts.realtime = le64toh(o->entry.realtime);
2264 if (to->tail_entry_monotonic_valid &&
2265 ts.monotonic < le64toh(to->header->tail_entry_monotonic))
2268 n = journal_file_entry_n_items(o);
2269 items = alloca(sizeof(EntryItem) * n);
2271 for (i = 0; i < n; i++) {
2278 q = le64toh(o->entry.items[i].object_offset);
2279 le_hash = o->entry.items[i].hash;
2281 r = journal_file_move_to_object(from, OBJECT_DATA, q, &o);
2285 if (le_hash != o->data.hash)
2288 l = le64toh(o->object.size) - offsetof(Object, data.payload);
2291 /* We hit the limit on 32bit machines */
2292 if ((uint64_t) t != l)
2295 if (o->object.flags & OBJECT_COMPRESSED) {
2299 if (!uncompress_blob(o->data.payload, l, &from->compress_buffer, &from->compress_buffer_size, &rsize))
2302 data = from->compress_buffer;
2305 return -EPROTONOSUPPORT;
2308 data = o->data.payload;
2310 r = journal_file_append_data(to, data, l, &u, &h);
2314 xor_hash ^= le64toh(u->data.hash);
2315 items[i].object_offset = htole64(h);
2316 items[i].hash = u->data.hash;
2318 r = journal_file_move_to_object(from, OBJECT_ENTRY, p, &o);
2323 return journal_file_append_entry_internal(to, &ts, xor_hash, items, n, seqnum, ret, offset);
2326 void journal_default_metrics(JournalMetrics *m, int fd) {
2327 uint64_t fs_size = 0;
2329 char a[FORMAT_BYTES_MAX], b[FORMAT_BYTES_MAX], c[FORMAT_BYTES_MAX], d[FORMAT_BYTES_MAX];
2334 if (fstatvfs(fd, &ss) >= 0)
2335 fs_size = ss.f_frsize * ss.f_blocks;
2337 if (m->max_use == (uint64_t) -1) {
2340 m->max_use = PAGE_ALIGN(fs_size / 10); /* 10% of file system size */
2342 if (m->max_use > DEFAULT_MAX_USE_UPPER)
2343 m->max_use = DEFAULT_MAX_USE_UPPER;
2345 if (m->max_use < DEFAULT_MAX_USE_LOWER)
2346 m->max_use = DEFAULT_MAX_USE_LOWER;
2348 m->max_use = DEFAULT_MAX_USE_LOWER;
2350 m->max_use = PAGE_ALIGN(m->max_use);
2352 if (m->max_use < JOURNAL_FILE_SIZE_MIN*2)
2353 m->max_use = JOURNAL_FILE_SIZE_MIN*2;
2356 if (m->max_size == (uint64_t) -1) {
2357 m->max_size = PAGE_ALIGN(m->max_use / 8); /* 8 chunks */
2359 if (m->max_size > DEFAULT_MAX_SIZE_UPPER)
2360 m->max_size = DEFAULT_MAX_SIZE_UPPER;
2362 m->max_size = PAGE_ALIGN(m->max_size);
2364 if (m->max_size < JOURNAL_FILE_SIZE_MIN)
2365 m->max_size = JOURNAL_FILE_SIZE_MIN;
2367 if (m->max_size*2 > m->max_use)
2368 m->max_use = m->max_size*2;
2370 if (m->min_size == (uint64_t) -1)
2371 m->min_size = JOURNAL_FILE_SIZE_MIN;
2373 m->min_size = PAGE_ALIGN(m->min_size);
2375 if (m->min_size < JOURNAL_FILE_SIZE_MIN)
2376 m->min_size = JOURNAL_FILE_SIZE_MIN;
2378 if (m->min_size > m->max_size)
2379 m->max_size = m->min_size;
2382 if (m->keep_free == (uint64_t) -1) {
2385 m->keep_free = PAGE_ALIGN(fs_size / 20); /* 5% of file system size */
2387 if (m->keep_free > DEFAULT_KEEP_FREE_UPPER)
2388 m->keep_free = DEFAULT_KEEP_FREE_UPPER;
2391 m->keep_free = DEFAULT_KEEP_FREE;
2394 log_debug("Fixed max_use=%s max_size=%s min_size=%s keep_free=%s",
2395 format_bytes(a, sizeof(a), m->max_use),
2396 format_bytes(b, sizeof(b), m->max_size),
2397 format_bytes(c, sizeof(c), m->min_size),
2398 format_bytes(d, sizeof(d), m->keep_free));
2401 int journal_file_get_cutoff_realtime_usec(JournalFile *f, usec_t *from, usec_t *to) {
2406 if (f->header->head_entry_realtime == 0)
2409 *from = le64toh(f->header->head_entry_realtime);
2413 if (f->header->tail_entry_realtime == 0)
2416 *to = le64toh(f->header->tail_entry_realtime);
2422 int journal_file_get_cutoff_monotonic_usec(JournalFile *f, sd_id128_t boot_id, usec_t *from, usec_t *to) {
2423 char t[9+32+1] = "_BOOT_ID=";
2431 sd_id128_to_string(boot_id, t + 9);
2433 r = journal_file_find_data_object(f, t, strlen(t), &o, &p);
2437 if (le64toh(o->data.n_entries) <= 0)
2441 r = journal_file_move_to_object(f, OBJECT_ENTRY, le64toh(o->data.entry_offset), &o);
2445 *from = le64toh(o->entry.monotonic);
2449 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
2453 r = generic_array_get_plus_one(f,
2454 le64toh(o->data.entry_offset),
2455 le64toh(o->data.entry_array_offset),
2456 le64toh(o->data.n_entries)-1,
2461 *to = le64toh(o->entry.monotonic);
2467 bool journal_file_rotate_suggested(JournalFile *f) {
2470 /* If we gained new header fields we gained new features,
2471 * hence suggest a rotation */
2472 if (le64toh(f->header->header_size) < sizeof(Header)) {
2473 log_debug("%s uses an outdated header, suggesting rotation.", f->path);
2477 /* Let's check if the hash tables grew over a certain fill
2478 * level (75%, borrowing this value from Java's hash table
2479 * implementation), and if so suggest a rotation. To calculate
2480 * the fill level we need the n_data field, which only exists
2481 * in newer versions. */
2483 if (JOURNAL_HEADER_CONTAINS(f->header, n_data))
2484 if (le64toh(f->header->n_data) * 4ULL > (le64toh(f->header->data_hash_table_size) / sizeof(HashItem)) * 3ULL) {
2485 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.",
2487 100.0 * (double) le64toh(f->header->n_data) / ((double) (le64toh(f->header->data_hash_table_size) / sizeof(HashItem))),
2488 (unsigned long long) le64toh(f->header->n_data),
2489 (unsigned long long) (le64toh(f->header->data_hash_table_size) / sizeof(HashItem)),
2490 (unsigned long long) (f->last_stat.st_size),
2491 (unsigned long long) (f->last_stat.st_size / le64toh(f->header->n_data)));
2495 if (JOURNAL_HEADER_CONTAINS(f->header, n_fields))
2496 if (le64toh(f->header->n_fields) * 4ULL > (le64toh(f->header->field_hash_table_size) / sizeof(HashItem)) * 3ULL) {
2497 log_debug("Field hash table of %s has a fill level at %.1f (%llu of %llu items), suggesting rotation.",
2499 100.0 * (double) le64toh(f->header->n_fields) / ((double) (le64toh(f->header->field_hash_table_size) / sizeof(HashItem))),
2500 (unsigned long long) le64toh(f->header->n_fields),
2501 (unsigned long long) (le64toh(f->header->field_hash_table_size) / sizeof(HashItem)));