]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/journal-file.c
tree-wide: make use of new relative time events in sd-event.h
[thirdparty/systemd.git] / src / journal / journal-file.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
cec736d2 2
cec736d2 3#include <errno.h>
cec736d2 4#include <fcntl.h>
11689d2a 5#include <linux/fs.h>
ac2e41f5 6#include <pthread.h>
07630cea
LP
7#include <stddef.h>
8#include <sys/mman.h>
9#include <sys/statvfs.h>
10#include <sys/uio.h>
11#include <unistd.h>
fb0951b0 12
a03d4359
ZJS
13#include "sd-event.h"
14
b5efdb8a 15#include "alloc-util.h"
f27a3864 16#include "btrfs-util.h"
c8b3094d 17#include "chattr-util.h"
07630cea 18#include "compress.h"
4ce534f4 19#include "env-util.h"
3ffd4af2 20#include "fd-util.h"
aa892669 21#include "format-util.h"
11b29a96 22#include "fs-util.h"
0284adc6 23#include "journal-authenticate.h"
cec736d2
LP
24#include "journal-def.h"
25#include "journal-file.h"
26#include "lookup3.h"
0a970718 27#include "memory-util.h"
5d1ce257 28#include "path-util.h"
3df3e884 29#include "random-util.h"
b58c888f 30#include "set.h"
760877e9 31#include "sort-util.h"
3cc44114 32#include "stat-util.h"
07630cea 33#include "string-util.h"
4761fd0f 34#include "strv.h"
89a5a90c 35#include "xattr-util.h"
cec736d2 36
4a92baf3
LP
37#define DEFAULT_DATA_HASH_TABLE_SIZE (2047ULL*sizeof(HashItem))
38#define DEFAULT_FIELD_HASH_TABLE_SIZE (333ULL*sizeof(HashItem))
cec736d2 39
57850536
AG
40#define DEFAULT_COMPRESS_THRESHOLD (512ULL)
41#define MIN_COMPRESS_THRESHOLD (8ULL)
807e17f0 42
babfc091 43/* This is the minimum journal file size */
6aae0b1a 44#define JOURNAL_FILE_SIZE_MIN (512 * 1024ULL) /* 512 KiB */
babfc091
LP
45
46/* These are the lower and upper bounds if we deduce the max_use value
47 * from the file system size */
6aae0b1a
ZJS
48#define MAX_USE_LOWER (1 * 1024 * 1024ULL) /* 1 MiB */
49#define MAX_USE_UPPER (4 * 1024 * 1024 * 1024ULL) /* 4 GiB */
babfc091 50
6aae0b1a
ZJS
51/* Those are the lower and upper bounds for the minimal use limit,
52 * i.e. how much we'll use even if keep_free suggests otherwise. */
53#define MIN_USE_LOW (1 * 1024 * 1024ULL) /* 1 MiB */
54#define MIN_USE_HIGH (16 * 1024 * 1024ULL) /* 16 MiB */
8580d1f7 55
babfc091 56/* This is the upper bound if we deduce max_size from max_use */
6aae0b1a 57#define MAX_SIZE_UPPER (128 * 1024 * 1024ULL) /* 128 MiB */
babfc091
LP
58
59/* This is the upper bound if we deduce the keep_free value from the
60 * file system size */
6aae0b1a 61#define KEEP_FREE_UPPER (4 * 1024 * 1024 * 1024ULL) /* 4 GiB */
babfc091
LP
62
63/* This is the keep_free value when we can't determine the system
64 * size */
6aae0b1a 65#define DEFAULT_KEEP_FREE (1024 * 1024ULL) /* 1 MB */
babfc091 66
8580d1f7 67/* This is the default maximum number of journal files to keep around. */
6aae0b1a 68#define DEFAULT_N_MAX_FILES 100
8580d1f7 69
dca6219e
LP
70/* n_data was the first entry we added after the initial file format design */
71#define HEADER_SIZE_MIN ALIGN64(offsetof(Header, n_data))
cec736d2 72
a4bcff5b
LP
73/* How many entries to keep in the entry array chain cache at max */
74#define CHAIN_CACHE_MAX 20
75
a676e665 76/* How much to increase the journal file size at once each time we allocate something new. */
6aae0b1a 77#define FILE_SIZE_INCREASE (8 * 1024 * 1024ULL) /* 8MB */
a676e665 78
2678031a
LP
79/* Reread fstat() of the file for detecting deletions at least this often */
80#define LAST_STAT_REFRESH_USEC (5*USEC_PER_SEC)
81
fa6ac760
LP
82/* The mmap context to use for the header we pick as one above the last defined typed */
83#define CONTEXT_HEADER _OBJECT_TYPE_MAX
84
0dbe57ee
LP
85/* Longest hash chain to rotate after */
86#define HASH_CHAIN_DEPTH_MAX 100
87
51804460
ZJS
88#ifdef __clang__
89# pragma GCC diagnostic ignored "-Waddress-of-packed-member"
90#endif
91
ac2e41f5
VC
92/* This may be called from a separate thread to prevent blocking the caller for the duration of fsync().
93 * As a result we use atomic operations on f->offline_state for inter-thread communications with
94 * journal_file_set_offline() and journal_file_set_online(). */
95static void journal_file_set_offline_internal(JournalFile *f) {
26687bf8 96 assert(f);
ac2e41f5
VC
97 assert(f->fd >= 0);
98 assert(f->header);
99
100 for (;;) {
101 switch (f->offline_state) {
102 case OFFLINE_CANCEL:
103 if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_CANCEL, OFFLINE_DONE))
104 continue;
105 return;
106
107 case OFFLINE_AGAIN_FROM_SYNCING:
108 if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_AGAIN_FROM_SYNCING, OFFLINE_SYNCING))
109 continue;
110 break;
111
112 case OFFLINE_AGAIN_FROM_OFFLINING:
113 if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_AGAIN_FROM_OFFLINING, OFFLINE_SYNCING))
114 continue;
115 break;
116
117 case OFFLINE_SYNCING:
118 (void) fsync(f->fd);
26687bf8 119
ac2e41f5
VC
120 if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_SYNCING, OFFLINE_OFFLINING))
121 continue;
26687bf8 122
8eb85171 123 f->header->state = f->archive ? STATE_ARCHIVED : STATE_OFFLINE;
ac2e41f5
VC
124 (void) fsync(f->fd);
125 break;
126
127 case OFFLINE_OFFLINING:
128 if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_OFFLINING, OFFLINE_DONE))
129 continue;
4831981d 130 _fallthrough_;
ac2e41f5
VC
131 case OFFLINE_DONE:
132 return;
133
134 case OFFLINE_JOINED:
135 log_debug("OFFLINE_JOINED unexpected offline state for journal_file_set_offline_internal()");
136 return;
137 }
138 }
139}
140
141static void * journal_file_set_offline_thread(void *arg) {
142 JournalFile *f = arg;
143
fa7ff4cf
LP
144 (void) pthread_setname_np(pthread_self(), "journal-offline");
145
ac2e41f5
VC
146 journal_file_set_offline_internal(f);
147
148 return NULL;
149}
150
151static int journal_file_set_offline_thread_join(JournalFile *f) {
152 int r;
153
154 assert(f);
155
156 if (f->offline_state == OFFLINE_JOINED)
157 return 0;
158
159 r = pthread_join(f->offline_thread, NULL);
160 if (r)
161 return -r;
162
163 f->offline_state = OFFLINE_JOINED;
26687bf8 164
be7cdd8e 165 if (mmap_cache_got_sigbus(f->mmap, f->cache_fd))
fa6ac760
LP
166 return -EIO;
167
ac2e41f5
VC
168 return 0;
169}
26687bf8 170
ac2e41f5
VC
171/* Trigger a restart if the offline thread is mid-flight in a restartable state. */
172static bool journal_file_set_offline_try_restart(JournalFile *f) {
173 for (;;) {
174 switch (f->offline_state) {
175 case OFFLINE_AGAIN_FROM_SYNCING:
176 case OFFLINE_AGAIN_FROM_OFFLINING:
177 return true;
178
179 case OFFLINE_CANCEL:
180 if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_CANCEL, OFFLINE_AGAIN_FROM_SYNCING))
181 continue;
182 return true;
183
184 case OFFLINE_SYNCING:
185 if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_SYNCING, OFFLINE_AGAIN_FROM_SYNCING))
186 continue;
187 return true;
188
189 case OFFLINE_OFFLINING:
190 if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_OFFLINING, OFFLINE_AGAIN_FROM_OFFLINING))
191 continue;
192 return true;
26687bf8
OS
193
194 default:
ac2e41f5
VC
195 return false;
196 }
26687bf8
OS
197 }
198}
199
ac2e41f5
VC
200/* Sets a journal offline.
201 *
202 * If wait is false then an offline is dispatched in a separate thread for a
203 * subsequent journal_file_set_offline() or journal_file_set_online() of the
204 * same journal to synchronize with.
205 *
206 * If wait is true, then either an existing offline thread will be restarted
207 * and joined, or if none exists the offline is simply performed in this
208 * context without involving another thread.
209 */
210int journal_file_set_offline(JournalFile *f, bool wait) {
211 bool restarted;
212 int r;
213
26687bf8
OS
214 assert(f);
215
216 if (!f->writable)
217 return -EPERM;
218
846e5418 219 if (f->fd < 0 || !f->header)
26687bf8
OS
220 return -EINVAL;
221
b8f99e27
VC
222 /* An offlining journal is implicitly online and may modify f->header->state,
223 * we must also join any potentially lingering offline thread when not online. */
224 if (!journal_file_is_offlining(f) && f->header->state != STATE_ONLINE)
225 return journal_file_set_offline_thread_join(f);
26687bf8 226
ac2e41f5
VC
227 /* Restart an in-flight offline thread and wait if needed, or join a lingering done one. */
228 restarted = journal_file_set_offline_try_restart(f);
229 if ((restarted && wait) || !restarted) {
230 r = journal_file_set_offline_thread_join(f);
231 if (r < 0)
232 return r;
233 }
26687bf8 234
ac2e41f5
VC
235 if (restarted)
236 return 0;
237
238 /* Initiate a new offline. */
239 f->offline_state = OFFLINE_SYNCING;
fa6ac760 240
ac2e41f5
VC
241 if (wait) /* Without using a thread if waiting. */
242 journal_file_set_offline_internal(f);
243 else {
5e9f01e8
LP
244 sigset_t ss, saved_ss;
245 int k;
246
cd2a429e 247 assert_se(sigfillset(&ss) >= 0);
08f9e80b
CM
248 /* Don't block SIGBUS since the offlining thread accesses a memory mapped file.
249 * Asynchronous SIGBUS signals can safely be handled by either thread. */
250 assert_se(sigdelset(&ss, SIGBUS) >= 0);
5e9f01e8
LP
251
252 r = pthread_sigmask(SIG_BLOCK, &ss, &saved_ss);
253 if (r > 0)
254 return -r;
255
ac2e41f5 256 r = pthread_create(&f->offline_thread, NULL, journal_file_set_offline_thread, f);
5e9f01e8
LP
257
258 k = pthread_sigmask(SIG_SETMASK, &saved_ss, NULL);
ec9ffa2c
VC
259 if (r > 0) {
260 f->offline_state = OFFLINE_JOINED;
ac2e41f5 261 return -r;
ec9ffa2c 262 }
5e9f01e8
LP
263 if (k > 0)
264 return -k;
ac2e41f5
VC
265 }
266
267 return 0;
268}
269
270static int journal_file_set_online(JournalFile *f) {
83bf6b67 271 bool wait = true;
ac2e41f5
VC
272
273 assert(f);
274
275 if (!f->writable)
276 return -EPERM;
277
846e5418 278 if (f->fd < 0 || !f->header)
ac2e41f5
VC
279 return -EINVAL;
280
83bf6b67 281 while (wait) {
ac2e41f5
VC
282 switch (f->offline_state) {
283 case OFFLINE_JOINED:
284 /* No offline thread, no need to wait. */
83bf6b67 285 wait = false;
ac2e41f5
VC
286 break;
287
288 case OFFLINE_SYNCING:
289 if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_SYNCING, OFFLINE_CANCEL))
290 continue;
291 /* Canceled syncing prior to offlining, no need to wait. */
83bf6b67 292 wait = false;
ac2e41f5
VC
293 break;
294
295 case OFFLINE_AGAIN_FROM_SYNCING:
296 if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_AGAIN_FROM_SYNCING, OFFLINE_CANCEL))
297 continue;
298 /* Canceled restart from syncing, no need to wait. */
83bf6b67 299 wait = false;
ac2e41f5
VC
300 break;
301
302 case OFFLINE_AGAIN_FROM_OFFLINING:
303 if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_AGAIN_FROM_OFFLINING, OFFLINE_CANCEL))
304 continue;
305 /* Canceled restart from offlining, must wait for offlining to complete however. */
4831981d 306 _fallthrough_;
ac2e41f5
VC
307 default: {
308 int r;
309
310 r = journal_file_set_offline_thread_join(f);
311 if (r < 0)
312 return r;
313
83bf6b67 314 wait = false;
ac2e41f5
VC
315 break;
316 }
317 }
318 }
26687bf8 319
be7cdd8e 320 if (mmap_cache_got_sigbus(f->mmap, f->cache_fd))
fa6ac760
LP
321 return -EIO;
322
ac2e41f5
VC
323 switch (f->header->state) {
324 case STATE_ONLINE:
325 return 0;
26687bf8 326
ac2e41f5
VC
327 case STATE_OFFLINE:
328 f->header->state = STATE_ONLINE;
329 (void) fsync(f->fd);
330 return 0;
331
332 default:
333 return -EINVAL;
334 }
26687bf8
OS
335}
336
b58c888f
VC
337bool journal_file_is_offlining(JournalFile *f) {
338 assert(f);
339
340 __sync_synchronize();
341
3742095b 342 if (IN_SET(f->offline_state, OFFLINE_DONE, OFFLINE_JOINED))
b58c888f
VC
343 return false;
344
345 return true;
346}
347
804ae586 348JournalFile* journal_file_close(JournalFile *f) {
c377a6f3
YW
349 if (!f)
350 return NULL;
cec736d2 351
349cc4a5 352#if HAVE_GCRYPT
b0af6f41 353 /* Write the final tag */
43cd8794
FB
354 if (f->seal && f->writable) {
355 int r;
356
357 r = journal_file_append_tag(f);
358 if (r < 0)
359 log_error_errno(r, "Failed to append tag when closing journal: %m");
360 }
feb12d3e 361#endif
b0af6f41 362
7a24f3bf 363 if (f->post_change_timer) {
b6cdfbe5
ZJS
364 if (sd_event_source_get_enabled(f->post_change_timer, NULL) > 0)
365 journal_file_post_change(f);
7a24f3bf 366
1d3fe304 367 sd_event_source_disable_unref(f->post_change_timer);
7a24f3bf
VC
368 }
369
ac2e41f5 370 journal_file_set_offline(f, true);
cec736d2 371
be7cdd8e
VC
372 if (f->mmap && f->cache_fd)
373 mmap_cache_free_fd(f->mmap, f->cache_fd);
cec736d2 374
11689d2a
LP
375 if (f->fd >= 0 && f->defrag_on_close) {
376
377 /* Be friendly to btrfs: turn COW back on again now,
378 * and defragment the file. We won't write to the file
379 * ever again, hence remove all fragmentation, and
380 * reenable all the good bits COW usually provides
381 * (such as data checksumming). */
382
db9a4254 383 (void) chattr_fd(f->fd, 0, FS_NOCOW_FL, NULL);
11689d2a
LP
384 (void) btrfs_defrag_fd(f->fd);
385 }
f27a3864 386
5d1ce257
LP
387 if (f->close_fd)
388 safe_close(f->fd);
cec736d2 389 free(f->path);
807e17f0 390
f649045c 391 mmap_cache_unref(f->mmap);
16e9f408 392
4743015d 393 ordered_hashmap_free_free(f->chain_cache);
a4bcff5b 394
d80b051c 395#if HAVE_COMPRESSION
807e17f0
LP
396 free(f->compress_buffer);
397#endif
398
349cc4a5 399#if HAVE_GCRYPT
baed47c3
LP
400 if (f->fss_file)
401 munmap(f->fss_file, PAGE_ALIGN(f->fss_file_size));
dc4ebc07 402 else
b7c9ae91
LP
403 free(f->fsprg_state);
404
405 free(f->fsprg_seed);
7560fffc
LP
406
407 if (f->hmac)
408 gcry_md_close(f->hmac);
409#endif
410
6b430fdb 411 return mfree(f);
cec736d2
LP
412}
413
0ac38b70 414static int journal_file_init_header(JournalFile *f, JournalFile *template) {
d89c8fdf 415 Header h = {};
cec736d2
LP
416 ssize_t k;
417 int r;
418
419 assert(f);
420
7560fffc 421 memcpy(h.signature, HEADER_SIGNATURE, 8);
23b0b2b2 422 h.header_size = htole64(ALIGN64(sizeof(h)));
cec736d2 423
d89c8fdf
ZJS
424 h.incompatible_flags |= htole32(
425 f->compress_xz * HEADER_INCOMPATIBLE_COMPRESSED_XZ |
4ce534f4 426 f->compress_lz4 * HEADER_INCOMPATIBLE_COMPRESSED_LZ4 |
8653185a 427 f->compress_zstd * HEADER_INCOMPATIBLE_COMPRESSED_ZSTD |
4ce534f4 428 f->keyed_hash * HEADER_INCOMPATIBLE_KEYED_HASH);
7560fffc 429
d89c8fdf
ZJS
430 h.compatible_flags = htole32(
431 f->seal * HEADER_COMPATIBLE_SEALED);
7560fffc 432
cec736d2
LP
433 r = sd_id128_randomize(&h.file_id);
434 if (r < 0)
435 return r;
436
0ac38b70
LP
437 if (template) {
438 h.seqnum_id = template->header->seqnum_id;
beec0085 439 h.tail_entry_seqnum = template->header->tail_entry_seqnum;
0ac38b70
LP
440 } else
441 h.seqnum_id = h.file_id;
cec736d2
LP
442
443 k = pwrite(f->fd, &h, sizeof(h), 0);
444 if (k < 0)
445 return -errno;
446
447 if (k != sizeof(h))
448 return -EIO;
449
450 return 0;
451}
452
453static int journal_file_refresh_header(JournalFile *f) {
fa6ac760 454 int r;
cec736d2
LP
455
456 assert(f);
c88cc6af 457 assert(f->header);
cec736d2
LP
458
459 r = sd_id128_get_machine(&f->header->machine_id);
fd4885df
ZJS
460 if (IN_SET(r, -ENOENT, -ENOMEDIUM))
461 /* We don't have a machine-id, let's continue without */
462 zero(f->header->machine_id);
463 else if (r < 0)
cec736d2
LP
464 return r;
465
e958c057 466 r = sd_id128_get_boot(&f->header->boot_id);
cec736d2
LP
467 if (r < 0)
468 return r;
469
fa6ac760 470 r = journal_file_set_online(f);
b788cc23 471
7560fffc 472 /* Sync the online state to disk */
fb426037 473 (void) fsync(f->fd);
b788cc23 474
a0fe2a2d
LP
475 /* We likely just created a new file, also sync the directory this file is located in. */
476 (void) fsync_directory_of_file(f->fd);
477
fa6ac760 478 return r;
cec736d2
LP
479}
480
4214009f
ZJS
481static bool warn_wrong_flags(const JournalFile *f, bool compatible) {
482 const uint32_t any = compatible ? HEADER_COMPATIBLE_ANY : HEADER_INCOMPATIBLE_ANY,
483 supported = compatible ? HEADER_COMPATIBLE_SUPPORTED : HEADER_INCOMPATIBLE_SUPPORTED;
484 const char *type = compatible ? "compatible" : "incompatible";
d89c8fdf
ZJS
485 uint32_t flags;
486
4214009f
ZJS
487 flags = le32toh(compatible ? f->header->compatible_flags : f->header->incompatible_flags);
488
489 if (flags & ~supported) {
490 if (flags & ~any)
4761fd0f 491 log_debug("Journal file %s has unknown %s flags 0x%"PRIx32,
4214009f
ZJS
492 f->path, type, flags & ~any);
493 flags = (flags & any) & ~supported;
4761fd0f 494 if (flags) {
8653185a 495 const char* strv[5];
4761fd0f
ZJS
496 unsigned n = 0;
497 _cleanup_free_ char *t = NULL;
498
4ce534f4
LP
499 if (compatible) {
500 if (flags & HEADER_COMPATIBLE_SEALED)
501 strv[n++] = "sealed";
502 } else {
503 if (flags & HEADER_INCOMPATIBLE_COMPRESSED_XZ)
504 strv[n++] = "xz-compressed";
505 if (flags & HEADER_INCOMPATIBLE_COMPRESSED_LZ4)
506 strv[n++] = "lz4-compressed";
8653185a
LP
507 if (flags & HEADER_INCOMPATIBLE_COMPRESSED_ZSTD)
508 strv[n++] = "zstd-compressed";
4ce534f4
LP
509 if (flags & HEADER_INCOMPATIBLE_KEYED_HASH)
510 strv[n++] = "keyed-hash";
511 }
4761fd0f
ZJS
512 strv[n] = NULL;
513 assert(n < ELEMENTSOF(strv));
514
515 t = strv_join((char**) strv, ", ");
516 log_debug("Journal file %s uses %s %s %s disabled at compilation time.",
517 f->path, type, n > 1 ? "flags" : "flag", strnull(t));
518 }
4214009f
ZJS
519 return true;
520 }
521
522 return false;
523}
524
525static int journal_file_verify_header(JournalFile *f) {
6f94e420
TS
526 uint64_t arena_size, header_size;
527
cec736d2 528 assert(f);
c88cc6af 529 assert(f->header);
cec736d2 530
7560fffc 531 if (memcmp(f->header->signature, HEADER_SIGNATURE, 8))
cec736d2
LP
532 return -EBADMSG;
533
4214009f
ZJS
534 /* In both read and write mode we refuse to open files with incompatible
535 * flags we don't know. */
536 if (warn_wrong_flags(f, false))
cec736d2
LP
537 return -EPROTONOSUPPORT;
538
4214009f
ZJS
539 /* When open for writing we refuse to open files with compatible flags, too. */
540 if (f->writable && warn_wrong_flags(f, true))
d89c8fdf 541 return -EPROTONOSUPPORT;
7560fffc 542
db11ac1a
LP
543 if (f->header->state >= _STATE_MAX)
544 return -EBADMSG;
545
893e0f8f 546 header_size = le64toh(READ_NOW(f->header->header_size));
6f94e420 547
dca6219e 548 /* The first addition was n_data, so check that we are at least this large */
6f94e420 549 if (header_size < HEADER_SIZE_MIN)
23b0b2b2
LP
550 return -EBADMSG;
551
8088cbd3 552 if (JOURNAL_HEADER_SEALED(f->header) && !JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays))
beec0085
LP
553 return -EBADMSG;
554
893e0f8f 555 arena_size = le64toh(READ_NOW(f->header->arena_size));
6f94e420
TS
556
557 if (UINT64_MAX - header_size < arena_size || header_size + arena_size > (uint64_t) f->last_stat.st_size)
db11ac1a
LP
558 return -ENODATA;
559
6f94e420 560 if (le64toh(f->header->tail_object_offset) > header_size + arena_size)
db11ac1a
LP
561 return -ENODATA;
562
7762e02b
LP
563 if (!VALID64(le64toh(f->header->data_hash_table_offset)) ||
564 !VALID64(le64toh(f->header->field_hash_table_offset)) ||
565 !VALID64(le64toh(f->header->tail_object_offset)) ||
566 !VALID64(le64toh(f->header->entry_array_offset)))
567 return -ENODATA;
568
cec736d2 569 if (f->writable) {
cec736d2 570 sd_id128_t machine_id;
ae739cc1 571 uint8_t state;
cec736d2
LP
572 int r;
573
574 r = sd_id128_get_machine(&machine_id);
575 if (r < 0)
576 return r;
577
578 if (!sd_id128_equal(machine_id, f->header->machine_id))
579 return -EHOSTDOWN;
580
de190aef 581 state = f->header->state;
cec736d2 582
b288cdeb
ZJS
583 if (state == STATE_ARCHIVED)
584 return -ESHUTDOWN; /* Already archived */
baaa35ad
ZJS
585 else if (state == STATE_ONLINE)
586 return log_debug_errno(SYNTHETIC_ERRNO(EBUSY),
587 "Journal file %s is already online. Assuming unclean closing.",
588 f->path);
589 else if (state != STATE_OFFLINE)
590 return log_debug_errno(SYNTHETIC_ERRNO(EBUSY),
591 "Journal file %s has unknown state %i.",
592 f->path, state);
ae739cc1 593
5b3cc0c8
YN
594 if (f->header->field_hash_table_size == 0 || f->header->data_hash_table_size == 0)
595 return -EBADMSG;
596
ae739cc1
LP
597 /* Don't permit appending to files from the future. Because otherwise the realtime timestamps wouldn't
598 * be strictly ordered in the entries in the file anymore, and we can't have that since it breaks
599 * bisection. */
baaa35ad
ZJS
600 if (le64toh(f->header->tail_entry_realtime) > now(CLOCK_REALTIME))
601 return log_debug_errno(SYNTHETIC_ERRNO(ETXTBSY),
602 "Journal file %s is from the future, refusing to append new data to it that'd be older.",
603 f->path);
cec736d2
LP
604 }
605
d89c8fdf
ZJS
606 f->compress_xz = JOURNAL_HEADER_COMPRESSED_XZ(f->header);
607 f->compress_lz4 = JOURNAL_HEADER_COMPRESSED_LZ4(f->header);
8653185a 608 f->compress_zstd = JOURNAL_HEADER_COMPRESSED_ZSTD(f->header);
c586dbf1 609
f1889c91 610 f->seal = JOURNAL_HEADER_SEALED(f->header);
7560fffc 611
4ce534f4
LP
612 f->keyed_hash = JOURNAL_HEADER_KEYED_HASH(f->header);
613
cec736d2
LP
614 return 0;
615}
616
28ca867a 617int journal_file_fstat(JournalFile *f) {
3cc44114
LP
618 int r;
619
2678031a
LP
620 assert(f);
621 assert(f->fd >= 0);
622
623 if (fstat(f->fd, &f->last_stat) < 0)
624 return -errno;
625
626 f->last_stat_usec = now(CLOCK_MONOTONIC);
627
e9dd6984 628 /* Refuse dealing with files that aren't regular */
3cc44114
LP
629 r = stat_verify_regular(&f->last_stat);
630 if (r < 0)
631 return r;
8d6a4d33 632
2678031a
LP
633 /* Refuse appending to files that are already deleted */
634 if (f->last_stat.st_nlink <= 0)
635 return -EIDRM;
636
637 return 0;
638}
639
cec736d2 640static int journal_file_allocate(JournalFile *f, uint64_t offset, uint64_t size) {
893e0f8f 641 uint64_t old_size, new_size, old_header_size, old_arena_size;
fec2aa2f 642 int r;
cec736d2
LP
643
644 assert(f);
c88cc6af 645 assert(f->header);
cec736d2 646
893e0f8f
LP
647 /* We assume that this file is not sparse, and we know that for sure, since we always call
648 * posix_fallocate() ourselves */
649
650 if (size > PAGE_ALIGN_DOWN(UINT64_MAX) - offset)
651 return -EINVAL;
cec736d2 652
be7cdd8e 653 if (mmap_cache_got_sigbus(f->mmap, f->cache_fd))
fa6ac760
LP
654 return -EIO;
655
893e0f8f
LP
656 old_header_size = le64toh(READ_NOW(f->header->header_size));
657 old_arena_size = le64toh(READ_NOW(f->header->arena_size));
658 if (old_arena_size > PAGE_ALIGN_DOWN(UINT64_MAX) - old_header_size)
659 return -EBADMSG;
660
661 old_size = old_header_size + old_arena_size;
cec736d2 662
893e0f8f 663 new_size = MAX(PAGE_ALIGN(offset + size), old_header_size);
bc85bfee 664
2678031a
LP
665 if (new_size <= old_size) {
666
667 /* We already pre-allocated enough space, but before
668 * we write to it, let's check with fstat() if the
669 * file got deleted, in order make sure we don't throw
670 * away the data immediately. Don't check fstat() for
671 * all writes though, but only once ever 10s. */
672
673 if (f->last_stat_usec + LAST_STAT_REFRESH_USEC > now(CLOCK_MONOTONIC))
674 return 0;
675
676 return journal_file_fstat(f);
677 }
678
679 /* Allocate more space. */
cec736d2 680
a676e665 681 if (f->metrics.max_size > 0 && new_size > f->metrics.max_size)
bc85bfee 682 return -E2BIG;
cec736d2 683
a676e665 684 if (new_size > f->metrics.min_size && f->metrics.keep_free > 0) {
cec736d2
LP
685 struct statvfs svfs;
686
687 if (fstatvfs(f->fd, &svfs) >= 0) {
688 uint64_t available;
689
070052ab 690 available = LESS_BY((uint64_t) svfs.f_bfree * (uint64_t) svfs.f_bsize, f->metrics.keep_free);
cec736d2
LP
691
692 if (new_size - old_size > available)
693 return -E2BIG;
694 }
695 }
696
eda4b58b 697 /* Increase by larger blocks at once */
be6b0c21 698 new_size = DIV_ROUND_UP(new_size, FILE_SIZE_INCREASE) * FILE_SIZE_INCREASE;
eda4b58b
LP
699 if (f->metrics.max_size > 0 && new_size > f->metrics.max_size)
700 new_size = f->metrics.max_size;
701
bc85bfee
LP
702 /* Note that the glibc fallocate() fallback is very
703 inefficient, hence we try to minimize the allocation area
704 as we can. */
fec2aa2f
GV
705 r = posix_fallocate(f->fd, old_size, new_size - old_size);
706 if (r != 0)
707 return -r;
cec736d2 708
893e0f8f 709 f->header->arena_size = htole64(new_size - old_header_size);
cec736d2 710
2678031a 711 return journal_file_fstat(f);
cec736d2
LP
712}
713
78519831 714static unsigned type_to_context(ObjectType type) {
d3d3208f 715 /* One context for each type, plus one catch-all for the rest */
69adae51 716 assert_cc(_OBJECT_TYPE_MAX <= MMAP_CACHE_MAX_CONTEXTS);
fa6ac760 717 assert_cc(CONTEXT_HEADER < MMAP_CACHE_MAX_CONTEXTS);
d05089d8 718 return type > OBJECT_UNUSED && type < _OBJECT_TYPE_MAX ? type : 0;
d3d3208f
MS
719}
720
71139898
LP
721static int journal_file_move_to(
722 JournalFile *f,
723 ObjectType type,
724 bool keep_always,
725 uint64_t offset,
726 uint64_t size,
727 void **ret,
728 size_t *ret_size) {
729
2678031a
LP
730 int r;
731
cec736d2 732 assert(f);
cec736d2
LP
733 assert(ret);
734
7762e02b
LP
735 if (size <= 0)
736 return -EINVAL;
737
893e0f8f
LP
738 if (size > UINT64_MAX - offset)
739 return -EBADMSG;
740
2a59ea54 741 /* Avoid SIGBUS on invalid accesses */
4bbdcdb3
LP
742 if (offset + size > (uint64_t) f->last_stat.st_size) {
743 /* Hmm, out of range? Let's refresh the fstat() data
744 * first, before we trust that check. */
745
2678031a
LP
746 r = journal_file_fstat(f);
747 if (r < 0)
748 return r;
749
750 if (offset + size > (uint64_t) f->last_stat.st_size)
4bbdcdb3
LP
751 return -EADDRNOTAVAIL;
752 }
753
b439282e 754 return mmap_cache_get(f->mmap, f->cache_fd, f->prot, type_to_context(type), keep_always, offset, size, &f->last_stat, ret, ret_size);
cec736d2
LP
755}
756
16e9f408
LP
757static uint64_t minimum_header_size(Object *o) {
758
b8e891e6 759 static const uint64_t table[] = {
16e9f408
LP
760 [OBJECT_DATA] = sizeof(DataObject),
761 [OBJECT_FIELD] = sizeof(FieldObject),
762 [OBJECT_ENTRY] = sizeof(EntryObject),
763 [OBJECT_DATA_HASH_TABLE] = sizeof(HashTableObject),
764 [OBJECT_FIELD_HASH_TABLE] = sizeof(HashTableObject),
765 [OBJECT_ENTRY_ARRAY] = sizeof(EntryArrayObject),
766 [OBJECT_TAG] = sizeof(TagObject),
767 };
768
769 if (o->object.type >= ELEMENTSOF(table) || table[o->object.type] <= 0)
770 return sizeof(ObjectHeader);
771
772 return table[o->object.type];
773}
774
24754f36
TR
775/* Lightweight object checks. We want this to be fast, so that we won't
776 * slowdown every journal_file_move_to_object() call too much. */
777static int journal_file_check_object(JournalFile *f, uint64_t offset, Object *o) {
778 assert(f);
779 assert(o);
780
781 switch (o->object.type) {
782
a602d93e 783 case OBJECT_DATA:
baaa35ad
ZJS
784 if ((le64toh(o->data.entry_offset) == 0) ^ (le64toh(o->data.n_entries) == 0))
785 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
786 "Bad n_entries: %" PRIu64 ": %" PRIu64,
787 le64toh(o->data.n_entries),
788 offset);
789
20ee282b 790 if (le64toh(o->object.size) <= offsetof(DataObject, payload))
baaa35ad
ZJS
791 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
792 "Bad object size (<= %zu): %" PRIu64 ": %" PRIu64,
793 offsetof(DataObject, payload),
794 le64toh(o->object.size),
795 offset);
24754f36 796
10e8445b
TR
797 if (!VALID64(le64toh(o->data.next_hash_offset)) ||
798 !VALID64(le64toh(o->data.next_field_offset)) ||
799 !VALID64(le64toh(o->data.entry_offset)) ||
baaa35ad
ZJS
800 !VALID64(le64toh(o->data.entry_array_offset)))
801 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
802 "Invalid offset, next_hash_offset=" OFSfmt ", next_field_offset=" OFSfmt ", entry_offset=" OFSfmt ", entry_array_offset=" OFSfmt ": %" PRIu64,
803 le64toh(o->data.next_hash_offset),
804 le64toh(o->data.next_field_offset),
805 le64toh(o->data.entry_offset),
806 le64toh(o->data.entry_array_offset),
807 offset);
24754f36
TR
808
809 break;
24754f36
TR
810
811 case OBJECT_FIELD:
20ee282b 812 if (le64toh(o->object.size) <= offsetof(FieldObject, payload))
baaa35ad
ZJS
813 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
814 "Bad field size (<= %zu): %" PRIu64 ": %" PRIu64,
815 offsetof(FieldObject, payload),
816 le64toh(o->object.size),
817 offset);
24754f36 818
10e8445b 819 if (!VALID64(le64toh(o->field.next_hash_offset)) ||
baaa35ad
ZJS
820 !VALID64(le64toh(o->field.head_data_offset)))
821 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
822 "Invalid offset, next_hash_offset=" OFSfmt ", head_data_offset=" OFSfmt ": %" PRIu64,
823 le64toh(o->field.next_hash_offset),
824 le64toh(o->field.head_data_offset),
825 offset);
24754f36
TR
826 break;
827
893e0f8f
LP
828 case OBJECT_ENTRY: {
829 uint64_t sz;
830
831 sz = le64toh(READ_NOW(o->object.size));
832 if (sz < offsetof(EntryObject, items) ||
833 (sz - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0)
baaa35ad
ZJS
834 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
835 "Bad entry size (<= %zu): %" PRIu64 ": %" PRIu64,
836 offsetof(EntryObject, items),
893e0f8f 837 sz,
baaa35ad
ZJS
838 offset);
839
893e0f8f 840 if ((sz - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0)
baaa35ad
ZJS
841 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
842 "Invalid number items in entry: %" PRIu64 ": %" PRIu64,
893e0f8f 843 (sz - offsetof(EntryObject, items)) / sizeof(EntryItem),
baaa35ad
ZJS
844 offset);
845
846 if (le64toh(o->entry.seqnum) <= 0)
847 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
848 "Invalid entry seqnum: %" PRIx64 ": %" PRIu64,
849 le64toh(o->entry.seqnum),
850 offset);
851
852 if (!VALID_REALTIME(le64toh(o->entry.realtime)))
853 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
854 "Invalid entry realtime timestamp: %" PRIu64 ": %" PRIu64,
855 le64toh(o->entry.realtime),
856 offset);
857
858 if (!VALID_MONOTONIC(le64toh(o->entry.monotonic)))
859 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
860 "Invalid entry monotonic timestamp: %" PRIu64 ": %" PRIu64,
861 le64toh(o->entry.monotonic),
862 offset);
24754f36
TR
863
864 break;
893e0f8f 865 }
24754f36
TR
866
867 case OBJECT_DATA_HASH_TABLE:
893e0f8f
LP
868 case OBJECT_FIELD_HASH_TABLE: {
869 uint64_t sz;
870
871 sz = le64toh(READ_NOW(o->object.size));
872 if (sz < offsetof(HashTableObject, items) ||
873 (sz - offsetof(HashTableObject, items)) % sizeof(HashItem) != 0 ||
874 (sz - offsetof(HashTableObject, items)) / sizeof(HashItem) <= 0)
baaa35ad
ZJS
875 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
876 "Invalid %s hash table size: %" PRIu64 ": %" PRIu64,
877 o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field",
893e0f8f 878 sz,
baaa35ad 879 offset);
24754f36
TR
880
881 break;
893e0f8f 882 }
24754f36 883
893e0f8f
LP
884 case OBJECT_ENTRY_ARRAY: {
885 uint64_t sz;
886
887 sz = le64toh(READ_NOW(o->object.size));
888 if (sz < offsetof(EntryArrayObject, items) ||
889 (sz - offsetof(EntryArrayObject, items)) % sizeof(le64_t) != 0 ||
890 (sz - offsetof(EntryArrayObject, items)) / sizeof(le64_t) <= 0)
baaa35ad
ZJS
891 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
892 "Invalid object entry array size: %" PRIu64 ": %" PRIu64,
893e0f8f 893 sz,
baaa35ad
ZJS
894 offset);
895
896 if (!VALID64(le64toh(o->entry_array.next_entry_array_offset)))
897 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
898 "Invalid object entry array next_entry_array_offset: " OFSfmt ": %" PRIu64,
899 le64toh(o->entry_array.next_entry_array_offset),
900 offset);
24754f36
TR
901
902 break;
893e0f8f 903 }
24754f36
TR
904
905 case OBJECT_TAG:
baaa35ad
ZJS
906 if (le64toh(o->object.size) != sizeof(TagObject))
907 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
908 "Invalid object tag size: %" PRIu64 ": %" PRIu64,
909 le64toh(o->object.size),
910 offset);
24754f36 911
baaa35ad
ZJS
912 if (!VALID_EPOCH(le64toh(o->tag.epoch)))
913 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
914 "Invalid object tag epoch: %" PRIu64 ": %" PRIu64,
915 le64toh(o->tag.epoch), offset);
24754f36
TR
916
917 break;
918 }
919
920 return 0;
921}
922
78519831 923int journal_file_move_to_object(JournalFile *f, ObjectType type, uint64_t offset, Object **ret) {
cec736d2
LP
924 int r;
925 void *t;
b439282e 926 size_t tsize;
cec736d2
LP
927 Object *o;
928 uint64_t s;
929
930 assert(f);
931 assert(ret);
932
db11ac1a 933 /* Objects may only be located at multiple of 64 bit */
baaa35ad
ZJS
934 if (!VALID64(offset))
935 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
936 "Attempt to move to object at non-64bit boundary: %" PRIu64,
937 offset);
db11ac1a 938
50809d7a 939 /* Object may not be located in the file header */
baaa35ad
ZJS
940 if (offset < le64toh(f->header->header_size))
941 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
942 "Attempt to move to object located in file header: %" PRIu64,
943 offset);
50809d7a 944
b439282e 945 r = journal_file_move_to(f, type, false, offset, sizeof(ObjectHeader), &t, &tsize);
cec736d2
LP
946 if (r < 0)
947 return r;
948
949 o = (Object*) t;
893e0f8f 950 s = le64toh(READ_NOW(o->object.size));
cec736d2 951
baaa35ad
ZJS
952 if (s == 0)
953 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
954 "Attempt to move to uninitialized object: %" PRIu64,
955 offset);
956 if (s < sizeof(ObjectHeader))
957 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
958 "Attempt to move to overly short object: %" PRIu64,
959 offset);
960
961 if (o->object.type <= OBJECT_UNUSED)
962 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
963 "Attempt to move to object with invalid type: %" PRIu64,
964 offset);
965
966 if (s < minimum_header_size(o))
967 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
968 "Attempt to move to truncated object: %" PRIu64,
969 offset);
970
971 if (type > OBJECT_UNUSED && o->object.type != type)
972 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
973 "Attempt to move to object of unexpected type: %" PRIu64,
974 offset);
cec736d2 975
b439282e
VC
976 if (s > tsize) {
977 r = journal_file_move_to(f, type, false, offset, s, &t, NULL);
cec736d2
LP
978 if (r < 0)
979 return r;
980
981 o = (Object*) t;
982 }
983
24754f36
TR
984 r = journal_file_check_object(f, offset, o);
985 if (r < 0)
986 return r;
987
cec736d2
LP
988 *ret = o;
989 return 0;
990}
991
d98cc1f2 992static uint64_t journal_file_entry_seqnum(JournalFile *f, uint64_t *seqnum) {
cec736d2
LP
993 uint64_t r;
994
995 assert(f);
c88cc6af 996 assert(f->header);
cec736d2 997
beec0085 998 r = le64toh(f->header->tail_entry_seqnum) + 1;
c2373f84
LP
999
1000 if (seqnum) {
de190aef 1001 /* If an external seqnum counter was passed, we update
c2373f84
LP
1002 * both the local and the external one, and set it to
1003 * the maximum of both */
1004
1005 if (*seqnum + 1 > r)
1006 r = *seqnum + 1;
1007
1008 *seqnum = r;
1009 }
1010
beec0085 1011 f->header->tail_entry_seqnum = htole64(r);
cec736d2 1012
beec0085
LP
1013 if (f->header->head_entry_seqnum == 0)
1014 f->header->head_entry_seqnum = htole64(r);
de190aef 1015
cec736d2
LP
1016 return r;
1017}
1018
f4474e00
LP
1019int journal_file_append_object(
1020 JournalFile *f,
1021 ObjectType type,
1022 uint64_t size,
1023 Object **ret,
1024 uint64_t *ret_offset) {
1025
cec736d2
LP
1026 int r;
1027 uint64_t p;
1028 Object *tail, *o;
1029 void *t;
1030
1031 assert(f);
c88cc6af 1032 assert(f->header);
d05089d8 1033 assert(type > OBJECT_UNUSED && type < _OBJECT_TYPE_MAX);
cec736d2 1034 assert(size >= sizeof(ObjectHeader));
cec736d2 1035
26687bf8
OS
1036 r = journal_file_set_online(f);
1037 if (r < 0)
1038 return r;
1039
cec736d2 1040 p = le64toh(f->header->tail_object_offset);
cec736d2 1041 if (p == 0)
23b0b2b2 1042 p = le64toh(f->header->header_size);
cec736d2 1043 else {
893e0f8f
LP
1044 uint64_t sz;
1045
d05089d8 1046 r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &tail);
cec736d2
LP
1047 if (r < 0)
1048 return r;
1049
893e0f8f
LP
1050 sz = le64toh(READ_NOW(tail->object.size));
1051 if (sz > UINT64_MAX - sizeof(uint64_t) + 1)
1052 return -EBADMSG;
1053
1054 sz = ALIGN64(sz);
1055 if (p > UINT64_MAX - sz)
1056 return -EBADMSG;
1057
1058 p += sz;
cec736d2
LP
1059 }
1060
1061 r = journal_file_allocate(f, p, size);
1062 if (r < 0)
1063 return r;
1064
b439282e 1065 r = journal_file_move_to(f, type, false, p, size, &t, NULL);
cec736d2
LP
1066 if (r < 0)
1067 return r;
1068
1069 o = (Object*) t;
71139898
LP
1070 o->object = (ObjectHeader) {
1071 .type = type,
1072 .size = htole64(size),
1073 };
cec736d2
LP
1074
1075 f->header->tail_object_offset = htole64(p);
cec736d2
LP
1076 f->header->n_objects = htole64(le64toh(f->header->n_objects) + 1);
1077
f4474e00
LP
1078 if (ret)
1079 *ret = o;
1080
1081 if (ret_offset)
1082 *ret_offset = p;
cec736d2
LP
1083
1084 return 0;
1085}
1086
de190aef 1087static int journal_file_setup_data_hash_table(JournalFile *f) {
cec736d2
LP
1088 uint64_t s, p;
1089 Object *o;
1090 int r;
1091
1092 assert(f);
c88cc6af 1093 assert(f->header);
cec736d2 1094
070052ab
LP
1095 /* We estimate that we need 1 hash table entry per 768 bytes
1096 of journal file and we want to make sure we never get
1097 beyond 75% fill level. Calculate the hash table size for
1098 the maximum file size based on these metrics. */
4a92baf3 1099
dfabe643 1100 s = (f->metrics.max_size * 4 / 768 / 3) * sizeof(HashItem);
4a92baf3
LP
1101 if (s < DEFAULT_DATA_HASH_TABLE_SIZE)
1102 s = DEFAULT_DATA_HASH_TABLE_SIZE;
1103
5030c85a 1104 log_debug("Reserving %"PRIu64" entries in data hash table.", s / sizeof(HashItem));
4a92baf3 1105
de190aef
LP
1106 r = journal_file_append_object(f,
1107 OBJECT_DATA_HASH_TABLE,
1108 offsetof(Object, hash_table.items) + s,
1109 &o, &p);
cec736d2
LP
1110 if (r < 0)
1111 return r;
1112
29804cc1 1113 memzero(o->hash_table.items, s);
cec736d2 1114
de190aef
LP
1115 f->header->data_hash_table_offset = htole64(p + offsetof(Object, hash_table.items));
1116 f->header->data_hash_table_size = htole64(s);
cec736d2
LP
1117
1118 return 0;
1119}
1120
de190aef 1121static int journal_file_setup_field_hash_table(JournalFile *f) {
cec736d2
LP
1122 uint64_t s, p;
1123 Object *o;
1124 int r;
1125
1126 assert(f);
c88cc6af 1127 assert(f->header);
cec736d2 1128
3c1668da
LP
1129 /* We use a fixed size hash table for the fields as this
1130 * number should grow very slowly only */
1131
de190aef 1132 s = DEFAULT_FIELD_HASH_TABLE_SIZE;
5030c85a
LP
1133 log_debug("Reserving %"PRIu64" entries in field hash table.", s / sizeof(HashItem));
1134
de190aef
LP
1135 r = journal_file_append_object(f,
1136 OBJECT_FIELD_HASH_TABLE,
1137 offsetof(Object, hash_table.items) + s,
1138 &o, &p);
cec736d2
LP
1139 if (r < 0)
1140 return r;
1141
29804cc1 1142 memzero(o->hash_table.items, s);
cec736d2 1143
de190aef
LP
1144 f->header->field_hash_table_offset = htole64(p + offsetof(Object, hash_table.items));
1145 f->header->field_hash_table_size = htole64(s);
cec736d2
LP
1146
1147 return 0;
1148}
1149
dade37d4 1150int journal_file_map_data_hash_table(JournalFile *f) {
cec736d2
LP
1151 uint64_t s, p;
1152 void *t;
1153 int r;
1154
1155 assert(f);
c88cc6af 1156 assert(f->header);
cec736d2 1157
dade37d4
LP
1158 if (f->data_hash_table)
1159 return 0;
1160
de190aef
LP
1161 p = le64toh(f->header->data_hash_table_offset);
1162 s = le64toh(f->header->data_hash_table_size);
cec736d2 1163
de190aef 1164 r = journal_file_move_to(f,
16e9f408 1165 OBJECT_DATA_HASH_TABLE,
fcde2389 1166 true,
de190aef 1167 p, s,
b42549ad 1168 &t, NULL);
cec736d2
LP
1169 if (r < 0)
1170 return r;
1171
de190aef 1172 f->data_hash_table = t;
cec736d2
LP
1173 return 0;
1174}
1175
dade37d4 1176int journal_file_map_field_hash_table(JournalFile *f) {
cec736d2
LP
1177 uint64_t s, p;
1178 void *t;
1179 int r;
1180
1181 assert(f);
c88cc6af 1182 assert(f->header);
cec736d2 1183
dade37d4
LP
1184 if (f->field_hash_table)
1185 return 0;
1186
de190aef
LP
1187 p = le64toh(f->header->field_hash_table_offset);
1188 s = le64toh(f->header->field_hash_table_size);
cec736d2 1189
de190aef 1190 r = journal_file_move_to(f,
16e9f408 1191 OBJECT_FIELD_HASH_TABLE,
fcde2389 1192 true,
de190aef 1193 p, s,
b42549ad 1194 &t, NULL);
cec736d2
LP
1195 if (r < 0)
1196 return r;
1197
de190aef 1198 f->field_hash_table = t;
cec736d2
LP
1199 return 0;
1200}
1201
3c1668da
LP
1202static int journal_file_link_field(
1203 JournalFile *f,
1204 Object *o,
1205 uint64_t offset,
1206 uint64_t hash) {
1207
805d1486 1208 uint64_t p, h, m;
3c1668da
LP
1209 int r;
1210
1211 assert(f);
c88cc6af 1212 assert(f->header);
90d222c1 1213 assert(f->field_hash_table);
3c1668da
LP
1214 assert(o);
1215 assert(offset > 0);
1216
1217 if (o->object.type != OBJECT_FIELD)
1218 return -EINVAL;
1219
893e0f8f 1220 m = le64toh(READ_NOW(f->header->field_hash_table_size)) / sizeof(HashItem);
805d1486
LP
1221 if (m <= 0)
1222 return -EBADMSG;
3c1668da 1223
805d1486 1224 /* This might alter the window we are looking at */
3c1668da
LP
1225 o->field.next_hash_offset = o->field.head_data_offset = 0;
1226
805d1486 1227 h = hash % m;
3c1668da
LP
1228 p = le64toh(f->field_hash_table[h].tail_hash_offset);
1229 if (p == 0)
1230 f->field_hash_table[h].head_hash_offset = htole64(offset);
1231 else {
1232 r = journal_file_move_to_object(f, OBJECT_FIELD, p, &o);
1233 if (r < 0)
1234 return r;
1235
1236 o->field.next_hash_offset = htole64(offset);
1237 }
1238
1239 f->field_hash_table[h].tail_hash_offset = htole64(offset);
1240
1241 if (JOURNAL_HEADER_CONTAINS(f->header, n_fields))
1242 f->header->n_fields = htole64(le64toh(f->header->n_fields) + 1);
1243
1244 return 0;
1245}
1246
1247static int journal_file_link_data(
1248 JournalFile *f,
1249 Object *o,
1250 uint64_t offset,
1251 uint64_t hash) {
1252
805d1486 1253 uint64_t p, h, m;
cec736d2
LP
1254 int r;
1255
1256 assert(f);
c88cc6af 1257 assert(f->header);
90d222c1 1258 assert(f->data_hash_table);
cec736d2
LP
1259 assert(o);
1260 assert(offset > 0);
b588975f
LP
1261
1262 if (o->object.type != OBJECT_DATA)
1263 return -EINVAL;
cec736d2 1264
893e0f8f 1265 m = le64toh(READ_NOW(f->header->data_hash_table_size)) / sizeof(HashItem);
805d1486
LP
1266 if (m <= 0)
1267 return -EBADMSG;
48496df6 1268
805d1486 1269 /* This might alter the window we are looking at */
de190aef
LP
1270 o->data.next_hash_offset = o->data.next_field_offset = 0;
1271 o->data.entry_offset = o->data.entry_array_offset = 0;
1272 o->data.n_entries = 0;
cec736d2 1273
805d1486 1274 h = hash % m;
8db4213e 1275 p = le64toh(f->data_hash_table[h].tail_hash_offset);
3c1668da 1276 if (p == 0)
cec736d2 1277 /* Only entry in the hash table is easy */
de190aef 1278 f->data_hash_table[h].head_hash_offset = htole64(offset);
3c1668da 1279 else {
48496df6
LP
1280 /* Move back to the previous data object, to patch in
1281 * pointer */
cec736d2 1282
de190aef 1283 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
cec736d2
LP
1284 if (r < 0)
1285 return r;
1286
de190aef 1287 o->data.next_hash_offset = htole64(offset);
cec736d2
LP
1288 }
1289
de190aef 1290 f->data_hash_table[h].tail_hash_offset = htole64(offset);
cec736d2 1291
dca6219e
LP
1292 if (JOURNAL_HEADER_CONTAINS(f->header, n_data))
1293 f->header->n_data = htole64(le64toh(f->header->n_data) + 1);
1294
cec736d2
LP
1295 return 0;
1296}
1297
0dbe57ee
LP
1298static int next_hash_offset(
1299 JournalFile *f,
1300 uint64_t *p,
1301 le64_t *next_hash_offset,
1302 uint64_t *depth,
1303 le64_t *header_max_depth) {
1304
1305 uint64_t nextp;
1306
1307 nextp = le64toh(READ_NOW(*next_hash_offset));
1308 if (nextp > 0) {
1309 if (nextp <= *p) /* Refuse going in loops */
1310 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
1311 "Detected hash item loop in %s, refusing.", f->path);
1312
1313 (*depth)++;
1314
1315 /* If the depth of this hash chain is larger than all others we have seen so far, record it */
1316 if (header_max_depth && f->writable)
1317 *header_max_depth = htole64(MAX(*depth, le64toh(*header_max_depth)));
1318 }
1319
1320 *p = nextp;
1321 return 0;
1322}
1323
3c1668da
LP
1324int journal_file_find_field_object_with_hash(
1325 JournalFile *f,
1326 const void *field, uint64_t size, uint64_t hash,
f4474e00 1327 Object **ret, uint64_t *ret_offset) {
3c1668da 1328
0dbe57ee 1329 uint64_t p, osize, h, m, depth = 0;
3c1668da
LP
1330 int r;
1331
1332 assert(f);
c88cc6af 1333 assert(f->header);
3c1668da
LP
1334 assert(field && size > 0);
1335
dade37d4
LP
1336 /* If the field hash table is empty, we can't find anything */
1337 if (le64toh(f->header->field_hash_table_size) <= 0)
1338 return 0;
1339
1340 /* Map the field hash table, if it isn't mapped yet. */
1341 r = journal_file_map_field_hash_table(f);
1342 if (r < 0)
1343 return r;
1344
3c1668da
LP
1345 osize = offsetof(Object, field.payload) + size;
1346
893e0f8f 1347 m = le64toh(READ_NOW(f->header->field_hash_table_size)) / sizeof(HashItem);
805d1486 1348 if (m <= 0)
3c1668da
LP
1349 return -EBADMSG;
1350
805d1486 1351 h = hash % m;
3c1668da 1352 p = le64toh(f->field_hash_table[h].head_hash_offset);
3c1668da
LP
1353 while (p > 0) {
1354 Object *o;
1355
1356 r = journal_file_move_to_object(f, OBJECT_FIELD, p, &o);
1357 if (r < 0)
1358 return r;
1359
1360 if (le64toh(o->field.hash) == hash &&
1361 le64toh(o->object.size) == osize &&
1362 memcmp(o->field.payload, field, size) == 0) {
1363
1364 if (ret)
1365 *ret = o;
f4474e00
LP
1366 if (ret_offset)
1367 *ret_offset = p;
3c1668da
LP
1368
1369 return 1;
1370 }
1371
0dbe57ee
LP
1372 r = next_hash_offset(
1373 f,
1374 &p,
1375 &o->field.next_hash_offset,
1376 &depth,
1377 JOURNAL_HEADER_CONTAINS(f->header, field_hash_chain_depth) ? &f->header->field_hash_chain_depth : NULL);
1378 if (r < 0)
1379 return r;
3c1668da
LP
1380 }
1381
1382 return 0;
1383}
1384
4ce534f4
LP
1385uint64_t journal_file_hash_data(
1386 JournalFile *f,
1387 const void *data,
1388 size_t sz) {
1389
1390 assert(f);
1391 assert(data || sz == 0);
1392
1393 /* We try to unify our codebase on siphash, hence new-styled journal files utilizing the keyed hash
1394 * function use siphash. Old journal files use the Jenkins hash. */
1395
1396 if (JOURNAL_HEADER_KEYED_HASH(f->header))
1397 return siphash24(data, sz, f->header->file_id.bytes);
1398
1399 return jenkins_hash64(data, sz);
1400}
1401
3c1668da
LP
1402int journal_file_find_field_object(
1403 JournalFile *f,
1404 const void *field, uint64_t size,
f4474e00 1405 Object **ret, uint64_t *ret_offset) {
3c1668da 1406
3c1668da
LP
1407 assert(f);
1408 assert(field && size > 0);
1409
f4474e00
LP
1410 return journal_file_find_field_object_with_hash(
1411 f,
4ce534f4
LP
1412 field, size,
1413 journal_file_hash_data(f, field, size),
f4474e00 1414 ret, ret_offset);
3c1668da
LP
1415}
1416
de190aef
LP
1417int journal_file_find_data_object_with_hash(
1418 JournalFile *f,
1419 const void *data, uint64_t size, uint64_t hash,
f4474e00 1420 Object **ret, uint64_t *ret_offset) {
48496df6 1421
0dbe57ee 1422 uint64_t p, osize, h, m, depth = 0;
cec736d2
LP
1423 int r;
1424
1425 assert(f);
c88cc6af 1426 assert(f->header);
cec736d2
LP
1427 assert(data || size == 0);
1428
dade37d4
LP
1429 /* If there's no data hash table, then there's no entry. */
1430 if (le64toh(f->header->data_hash_table_size) <= 0)
1431 return 0;
1432
1433 /* Map the data hash table, if it isn't mapped yet. */
1434 r = journal_file_map_data_hash_table(f);
1435 if (r < 0)
1436 return r;
1437
cec736d2
LP
1438 osize = offsetof(Object, data.payload) + size;
1439
893e0f8f 1440 m = le64toh(READ_NOW(f->header->data_hash_table_size)) / sizeof(HashItem);
805d1486 1441 if (m <= 0)
bc85bfee
LP
1442 return -EBADMSG;
1443
805d1486 1444 h = hash % m;
de190aef 1445 p = le64toh(f->data_hash_table[h].head_hash_offset);
cec736d2 1446
de190aef
LP
1447 while (p > 0) {
1448 Object *o;
cec736d2 1449
de190aef 1450 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
cec736d2
LP
1451 if (r < 0)
1452 return r;
1453
807e17f0 1454 if (le64toh(o->data.hash) != hash)
85a131e8 1455 goto next;
807e17f0 1456
d89c8fdf 1457 if (o->object.flags & OBJECT_COMPRESSION_MASK) {
d80b051c 1458#if HAVE_COMPRESSION
fa1c4b51 1459 uint64_t l;
a7f7d1bd 1460 size_t rsize = 0;
cec736d2 1461
893e0f8f 1462 l = le64toh(READ_NOW(o->object.size));
807e17f0 1463 if (l <= offsetof(Object, data.payload))
cec736d2
LP
1464 return -EBADMSG;
1465
807e17f0
LP
1466 l -= offsetof(Object, data.payload);
1467
d89c8fdf
ZJS
1468 r = decompress_blob(o->object.flags & OBJECT_COMPRESSION_MASK,
1469 o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize, 0);
1470 if (r < 0)
1471 return r;
807e17f0 1472
b785c858 1473 if (rsize == size &&
807e17f0
LP
1474 memcmp(f->compress_buffer, data, size) == 0) {
1475
1476 if (ret)
1477 *ret = o;
1478
f4474e00
LP
1479 if (ret_offset)
1480 *ret_offset = p;
807e17f0
LP
1481
1482 return 1;
1483 }
3b1a55e1
ZJS
1484#else
1485 return -EPROTONOSUPPORT;
1486#endif
807e17f0
LP
1487 } else if (le64toh(o->object.size) == osize &&
1488 memcmp(o->data.payload, data, size) == 0) {
1489
cec736d2
LP
1490 if (ret)
1491 *ret = o;
1492
f4474e00
LP
1493 if (ret_offset)
1494 *ret_offset = p;
cec736d2 1495
de190aef 1496 return 1;
cec736d2
LP
1497 }
1498
85a131e8 1499 next:
0dbe57ee
LP
1500 r = next_hash_offset(
1501 f,
1502 &p,
1503 &o->data.next_hash_offset,
1504 &depth,
1505 JOURNAL_HEADER_CONTAINS(f->header, data_hash_chain_depth) ? &f->header->data_hash_chain_depth : NULL);
1506 if (r < 0)
1507 return r;
cec736d2
LP
1508 }
1509
de190aef
LP
1510 return 0;
1511}
1512
1513int journal_file_find_data_object(
1514 JournalFile *f,
1515 const void *data, uint64_t size,
f4474e00 1516 Object **ret, uint64_t *ret_offset) {
de190aef 1517
de190aef
LP
1518 assert(f);
1519 assert(data || size == 0);
1520
f4474e00
LP
1521 return journal_file_find_data_object_with_hash(
1522 f,
4ce534f4
LP
1523 data, size,
1524 journal_file_hash_data(f, data, size),
f4474e00 1525 ret, ret_offset);
de190aef
LP
1526}
1527
3c1668da
LP
1528static int journal_file_append_field(
1529 JournalFile *f,
1530 const void *field, uint64_t size,
f4474e00 1531 Object **ret, uint64_t *ret_offset) {
3c1668da
LP
1532
1533 uint64_t hash, p;
1534 uint64_t osize;
1535 Object *o;
1536 int r;
1537
1538 assert(f);
1539 assert(field && size > 0);
1540
4ce534f4 1541 hash = journal_file_hash_data(f, field, size);
3c1668da
LP
1542
1543 r = journal_file_find_field_object_with_hash(f, field, size, hash, &o, &p);
1544 if (r < 0)
1545 return r;
1546 else if (r > 0) {
1547
1548 if (ret)
1549 *ret = o;
1550
f4474e00
LP
1551 if (ret_offset)
1552 *ret_offset = p;
3c1668da
LP
1553
1554 return 0;
1555 }
1556
1557 osize = offsetof(Object, field.payload) + size;
1558 r = journal_file_append_object(f, OBJECT_FIELD, osize, &o, &p);
8c92d4bb
LP
1559 if (r < 0)
1560 return r;
3c1668da
LP
1561
1562 o->field.hash = htole64(hash);
1563 memcpy(o->field.payload, field, size);
1564
1565 r = journal_file_link_field(f, o, p, hash);
1566 if (r < 0)
1567 return r;
1568
1569 /* The linking might have altered the window, so let's
1570 * refresh our pointer */
1571 r = journal_file_move_to_object(f, OBJECT_FIELD, p, &o);
1572 if (r < 0)
1573 return r;
1574
349cc4a5 1575#if HAVE_GCRYPT
3c1668da
LP
1576 r = journal_file_hmac_put_object(f, OBJECT_FIELD, o, p);
1577 if (r < 0)
1578 return r;
1579#endif
1580
1581 if (ret)
1582 *ret = o;
1583
f4474e00
LP
1584 if (ret_offset)
1585 *ret_offset = p;
3c1668da
LP
1586
1587 return 0;
1588}
1589
48496df6
LP
1590static int journal_file_append_data(
1591 JournalFile *f,
1592 const void *data, uint64_t size,
f4474e00 1593 Object **ret, uint64_t *ret_offset) {
48496df6 1594
de190aef
LP
1595 uint64_t hash, p;
1596 uint64_t osize;
1597 Object *o;
d89c8fdf 1598 int r, compression = 0;
3c1668da 1599 const void *eq;
de190aef
LP
1600
1601 assert(f);
1602 assert(data || size == 0);
1603
4ce534f4 1604 hash = journal_file_hash_data(f, data, size);
de190aef
LP
1605
1606 r = journal_file_find_data_object_with_hash(f, data, size, hash, &o, &p);
1607 if (r < 0)
1608 return r;
0240c603 1609 if (r > 0) {
de190aef
LP
1610
1611 if (ret)
1612 *ret = o;
1613
f4474e00
LP
1614 if (ret_offset)
1615 *ret_offset = p;
de190aef
LP
1616
1617 return 0;
1618 }
1619
1620 osize = offsetof(Object, data.payload) + size;
1621 r = journal_file_append_object(f, OBJECT_DATA, osize, &o, &p);
cec736d2
LP
1622 if (r < 0)
1623 return r;
1624
cec736d2 1625 o->data.hash = htole64(hash);
807e17f0 1626
d80b051c 1627#if HAVE_COMPRESSION
57850536 1628 if (JOURNAL_FILE_COMPRESS(f) && size >= f->compress_threshold_bytes) {
a7f7d1bd 1629 size_t rsize = 0;
807e17f0 1630
5d6f46b6 1631 compression = compress_blob(data, size, o->data.payload, size - 1, &rsize);
807e17f0 1632
d1afbcd2 1633 if (compression >= 0) {
807e17f0 1634 o->object.size = htole64(offsetof(Object, data.payload) + rsize);
d89c8fdf 1635 o->object.flags |= compression;
807e17f0 1636
fa1c4b51 1637 log_debug("Compressed data object %"PRIu64" -> %zu using %s",
d89c8fdf 1638 size, rsize, object_compressed_to_string(compression));
d1afbcd2
LP
1639 } else
1640 /* Compression didn't work, we don't really care why, let's continue without compression */
1641 compression = 0;
807e17f0
LP
1642 }
1643#endif
1644
75f32f04
ZJS
1645 if (compression == 0)
1646 memcpy_safe(o->data.payload, data, size);
cec736d2 1647
de190aef 1648 r = journal_file_link_data(f, o, p, hash);
cec736d2
LP
1649 if (r < 0)
1650 return r;
1651
349cc4a5 1652#if HAVE_GCRYPT
33685a5a
FB
1653 r = journal_file_hmac_put_object(f, OBJECT_DATA, o, p);
1654 if (r < 0)
1655 return r;
1656#endif
1657
48496df6
LP
1658 /* The linking might have altered the window, so let's
1659 * refresh our pointer */
1660 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
1661 if (r < 0)
1662 return r;
1663
08c6f819
SL
1664 if (!data)
1665 eq = NULL;
1666 else
1667 eq = memchr(data, '=', size);
3c1668da 1668 if (eq && eq > data) {
748db592 1669 Object *fo = NULL;
3c1668da 1670 uint64_t fp;
3c1668da
LP
1671
1672 /* Create field object ... */
1673 r = journal_file_append_field(f, data, (uint8_t*) eq - (uint8_t*) data, &fo, &fp);
1674 if (r < 0)
1675 return r;
1676
1677 /* ... and link it in. */
1678 o->data.next_field_offset = fo->field.head_data_offset;
1679 fo->field.head_data_offset = le64toh(p);
1680 }
1681
cec736d2
LP
1682 if (ret)
1683 *ret = o;
1684
f4474e00
LP
1685 if (ret_offset)
1686 *ret_offset = p;
cec736d2
LP
1687
1688 return 0;
1689}
1690
1691uint64_t journal_file_entry_n_items(Object *o) {
893e0f8f 1692 uint64_t sz;
cec736d2 1693 assert(o);
b588975f
LP
1694
1695 if (o->object.type != OBJECT_ENTRY)
1696 return 0;
cec736d2 1697
893e0f8f
LP
1698 sz = le64toh(READ_NOW(o->object.size));
1699 if (sz < offsetof(Object, entry.items))
1700 return 0;
1701
1702 return (sz - offsetof(Object, entry.items)) / sizeof(EntryItem);
cec736d2
LP
1703}
1704
0284adc6 1705uint64_t journal_file_entry_array_n_items(Object *o) {
893e0f8f
LP
1706 uint64_t sz;
1707
de190aef 1708 assert(o);
b588975f
LP
1709
1710 if (o->object.type != OBJECT_ENTRY_ARRAY)
1711 return 0;
de190aef 1712
893e0f8f
LP
1713 sz = le64toh(READ_NOW(o->object.size));
1714 if (sz < offsetof(Object, entry_array.items))
1715 return 0;
1716
1717 return (sz - offsetof(Object, entry_array.items)) / sizeof(uint64_t);
de190aef
LP
1718}
1719
fb9a24b6 1720uint64_t journal_file_hash_table_n_items(Object *o) {
893e0f8f
LP
1721 uint64_t sz;
1722
fb9a24b6 1723 assert(o);
b588975f 1724
ec2ce0c5 1725 if (!IN_SET(o->object.type, OBJECT_DATA_HASH_TABLE, OBJECT_FIELD_HASH_TABLE))
b588975f 1726 return 0;
fb9a24b6 1727
893e0f8f
LP
1728 sz = le64toh(READ_NOW(o->object.size));
1729 if (sz < offsetof(Object, hash_table.items))
1730 return 0;
1731
1732 return (sz - offsetof(Object, hash_table.items)) / sizeof(HashItem);
fb9a24b6
LP
1733}
1734
de190aef 1735static int link_entry_into_array(JournalFile *f,
4fd052ae
FC
1736 le64_t *first,
1737 le64_t *idx,
de190aef 1738 uint64_t p) {
cec736d2 1739 int r;
de190aef
LP
1740 uint64_t n = 0, ap = 0, q, i, a, hidx;
1741 Object *o;
1742
cec736d2 1743 assert(f);
c88cc6af 1744 assert(f->header);
de190aef
LP
1745 assert(first);
1746 assert(idx);
1747 assert(p > 0);
cec736d2 1748
de190aef 1749 a = le64toh(*first);
893e0f8f 1750 i = hidx = le64toh(READ_NOW(*idx));
de190aef
LP
1751 while (a > 0) {
1752
1753 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
1754 if (r < 0)
1755 return r;
cec736d2 1756
de190aef
LP
1757 n = journal_file_entry_array_n_items(o);
1758 if (i < n) {
1759 o->entry_array.items[i] = htole64(p);
1760 *idx = htole64(hidx + 1);
1761 return 0;
1762 }
cec736d2 1763
de190aef
LP
1764 i -= n;
1765 ap = a;
1766 a = le64toh(o->entry_array.next_entry_array_offset);
1767 }
1768
1769 if (hidx > n)
1770 n = (hidx+1) * 2;
1771 else
1772 n = n * 2;
1773
1774 if (n < 4)
1775 n = 4;
1776
1777 r = journal_file_append_object(f, OBJECT_ENTRY_ARRAY,
1778 offsetof(Object, entry_array.items) + n * sizeof(uint64_t),
1779 &o, &q);
cec736d2
LP
1780 if (r < 0)
1781 return r;
1782
349cc4a5 1783#if HAVE_GCRYPT
5996c7c2 1784 r = journal_file_hmac_put_object(f, OBJECT_ENTRY_ARRAY, o, q);
b0af6f41
LP
1785 if (r < 0)
1786 return r;
feb12d3e 1787#endif
b0af6f41 1788
de190aef 1789 o->entry_array.items[i] = htole64(p);
cec736d2 1790
de190aef 1791 if (ap == 0)
7be3aa17 1792 *first = htole64(q);
cec736d2 1793 else {
de190aef 1794 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, ap, &o);
cec736d2
LP
1795 if (r < 0)
1796 return r;
1797
de190aef
LP
1798 o->entry_array.next_entry_array_offset = htole64(q);
1799 }
cec736d2 1800
2dee23eb
LP
1801 if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays))
1802 f->header->n_entry_arrays = htole64(le64toh(f->header->n_entry_arrays) + 1);
1803
de190aef
LP
1804 *idx = htole64(hidx + 1);
1805
1806 return 0;
1807}
cec736d2 1808
de190aef 1809static int link_entry_into_array_plus_one(JournalFile *f,
4fd052ae
FC
1810 le64_t *extra,
1811 le64_t *first,
1812 le64_t *idx,
de190aef
LP
1813 uint64_t p) {
1814
893e0f8f 1815 uint64_t hidx;
de190aef
LP
1816 int r;
1817
1818 assert(f);
1819 assert(extra);
1820 assert(first);
1821 assert(idx);
1822 assert(p > 0);
1823
893e0f8f
LP
1824 hidx = le64toh(READ_NOW(*idx));
1825 if (hidx == UINT64_MAX)
1826 return -EBADMSG;
1827 if (hidx == 0)
de190aef
LP
1828 *extra = htole64(p);
1829 else {
4fd052ae 1830 le64_t i;
de190aef 1831
893e0f8f 1832 i = htole64(hidx - 1);
de190aef
LP
1833 r = link_entry_into_array(f, first, &i, p);
1834 if (r < 0)
1835 return r;
cec736d2
LP
1836 }
1837
893e0f8f 1838 *idx = htole64(hidx + 1);
de190aef
LP
1839 return 0;
1840}
1841
1842static int journal_file_link_entry_item(JournalFile *f, Object *o, uint64_t offset, uint64_t i) {
1843 uint64_t p;
1844 int r;
bfbd5be0 1845
de190aef
LP
1846 assert(f);
1847 assert(o);
1848 assert(offset > 0);
1849
1850 p = le64toh(o->entry.items[i].object_offset);
de190aef 1851 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
cec736d2
LP
1852 if (r < 0)
1853 return r;
1854
de190aef
LP
1855 return link_entry_into_array_plus_one(f,
1856 &o->data.entry_offset,
1857 &o->data.entry_array_offset,
1858 &o->data.n_entries,
1859 offset);
cec736d2
LP
1860}
1861
1862static int journal_file_link_entry(JournalFile *f, Object *o, uint64_t offset) {
de190aef 1863 uint64_t n, i;
cec736d2
LP
1864 int r;
1865
1866 assert(f);
c88cc6af 1867 assert(f->header);
cec736d2
LP
1868 assert(o);
1869 assert(offset > 0);
b588975f
LP
1870
1871 if (o->object.type != OBJECT_ENTRY)
1872 return -EINVAL;
cec736d2 1873
b788cc23
LP
1874 __sync_synchronize();
1875
cec736d2 1876 /* Link up the entry itself */
de190aef
LP
1877 r = link_entry_into_array(f,
1878 &f->header->entry_array_offset,
1879 &f->header->n_entries,
1880 offset);
1881 if (r < 0)
1882 return r;
cec736d2 1883
507f22bd 1884 /* log_debug("=> %s seqnr=%"PRIu64" n_entries=%"PRIu64, f->path, o->entry.seqnum, f->header->n_entries); */
cec736d2 1885
de190aef 1886 if (f->header->head_entry_realtime == 0)
0ac38b70 1887 f->header->head_entry_realtime = o->entry.realtime;
cec736d2 1888
0ac38b70 1889 f->header->tail_entry_realtime = o->entry.realtime;
de190aef
LP
1890 f->header->tail_entry_monotonic = o->entry.monotonic;
1891
cec736d2
LP
1892 /* Link up the items */
1893 n = journal_file_entry_n_items(o);
1894 for (i = 0; i < n; i++) {
1895 r = journal_file_link_entry_item(f, o, offset, i);
1896 if (r < 0)
1897 return r;
1898 }
1899
cec736d2
LP
1900 return 0;
1901}
1902
1903static int journal_file_append_entry_internal(
1904 JournalFile *f,
1905 const dual_timestamp *ts,
d180c349 1906 const sd_id128_t *boot_id,
cec736d2
LP
1907 uint64_t xor_hash,
1908 const EntryItem items[], unsigned n_items,
de190aef 1909 uint64_t *seqnum,
f4474e00 1910 Object **ret, uint64_t *ret_offset) {
cec736d2
LP
1911 uint64_t np;
1912 uint64_t osize;
1913 Object *o;
1914 int r;
1915
1916 assert(f);
c88cc6af 1917 assert(f->header);
cec736d2 1918 assert(items || n_items == 0);
de190aef 1919 assert(ts);
cec736d2
LP
1920
1921 osize = offsetof(Object, entry.items) + (n_items * sizeof(EntryItem));
1922
de190aef 1923 r = journal_file_append_object(f, OBJECT_ENTRY, osize, &o, &np);
cec736d2
LP
1924 if (r < 0)
1925 return r;
1926
d98cc1f2 1927 o->entry.seqnum = htole64(journal_file_entry_seqnum(f, seqnum));
75f32f04 1928 memcpy_safe(o->entry.items, items, n_items * sizeof(EntryItem));
de190aef
LP
1929 o->entry.realtime = htole64(ts->realtime);
1930 o->entry.monotonic = htole64(ts->monotonic);
cec736d2 1931 o->entry.xor_hash = htole64(xor_hash);
924426a7
CM
1932 if (boot_id)
1933 f->header->boot_id = *boot_id;
1934 o->entry.boot_id = f->header->boot_id;
cec736d2 1935
349cc4a5 1936#if HAVE_GCRYPT
5996c7c2 1937 r = journal_file_hmac_put_object(f, OBJECT_ENTRY, o, np);
b0af6f41
LP
1938 if (r < 0)
1939 return r;
feb12d3e 1940#endif
b0af6f41 1941
cec736d2
LP
1942 r = journal_file_link_entry(f, o, np);
1943 if (r < 0)
1944 return r;
1945
1946 if (ret)
1947 *ret = o;
1948
f4474e00
LP
1949 if (ret_offset)
1950 *ret_offset = np;
cec736d2
LP
1951
1952 return 0;
1953}
1954
cf244689 1955void journal_file_post_change(JournalFile *f) {
50f20cfd
LP
1956 assert(f);
1957
c5236850
DT
1958 if (f->fd < 0)
1959 return;
1960
50f20cfd
LP
1961 /* inotify() does not receive IN_MODIFY events from file
1962 * accesses done via mmap(). After each access we hence
1963 * trigger IN_MODIFY by truncating the journal file to its
1964 * current size which triggers IN_MODIFY. */
1965
bc85bfee
LP
1966 __sync_synchronize();
1967
50f20cfd 1968 if (ftruncate(f->fd, f->last_stat.st_size) < 0)
e167d7fd 1969 log_debug_errno(errno, "Failed to truncate file to its own size: %m");
50f20cfd
LP
1970}
1971
7a24f3bf
VC
1972static int post_change_thunk(sd_event_source *timer, uint64_t usec, void *userdata) {
1973 assert(userdata);
1974
1975 journal_file_post_change(userdata);
1976
1977 return 1;
1978}
1979
1980static void schedule_post_change(JournalFile *f) {
b6cdfbe5 1981 int r;
7a24f3bf
VC
1982
1983 assert(f);
1984 assert(f->post_change_timer);
1985
b6cdfbe5 1986 r = sd_event_source_get_enabled(f->post_change_timer, NULL);
7a24f3bf 1987 if (r < 0) {
e167d7fd
LP
1988 log_debug_errno(r, "Failed to get ftruncate timer state: %m");
1989 goto fail;
7a24f3bf 1990 }
b6cdfbe5 1991 if (r > 0)
7a24f3bf
VC
1992 return;
1993
39cf0351 1994 r = sd_event_source_set_time_relative(f->post_change_timer, f->post_change_timer_period);
7a24f3bf 1995 if (r < 0) {
e167d7fd
LP
1996 log_debug_errno(r, "Failed to set time for scheduling ftruncate: %m");
1997 goto fail;
7a24f3bf
VC
1998 }
1999
ca5d90d4 2000 r = sd_event_source_set_enabled(f->post_change_timer, SD_EVENT_ONESHOT);
7a24f3bf 2001 if (r < 0) {
e167d7fd
LP
2002 log_debug_errno(r, "Failed to enable scheduled ftruncate: %m");
2003 goto fail;
7a24f3bf 2004 }
e167d7fd
LP
2005
2006 return;
2007
2008fail:
2009 /* On failure, let's simply post the change immediately. */
2010 journal_file_post_change(f);
7a24f3bf
VC
2011}
2012
2013/* Enable coalesced change posting in a timer on the provided sd_event instance */
2014int journal_file_enable_post_change_timer(JournalFile *f, sd_event *e, usec_t t) {
2015 _cleanup_(sd_event_source_unrefp) sd_event_source *timer = NULL;
2016 int r;
2017
2018 assert(f);
2019 assert_return(!f->post_change_timer, -EINVAL);
2020 assert(e);
2021 assert(t);
2022
2023 r = sd_event_add_time(e, &timer, CLOCK_MONOTONIC, 0, 0, post_change_thunk, f);
2024 if (r < 0)
2025 return r;
2026
2027 r = sd_event_source_set_enabled(timer, SD_EVENT_OFF);
2028 if (r < 0)
2029 return r;
2030
1cc6c93a 2031 f->post_change_timer = TAKE_PTR(timer);
7a24f3bf
VC
2032 f->post_change_timer_period = t;
2033
2034 return r;
2035}
2036
93bab288
YW
2037static int entry_item_cmp(const EntryItem *a, const EntryItem *b) {
2038 return CMP(le64toh(a->object_offset), le64toh(b->object_offset));
1f2da9ec
LP
2039}
2040
d180c349
ZJS
2041int journal_file_append_entry(
2042 JournalFile *f,
2043 const dual_timestamp *ts,
2044 const sd_id128_t *boot_id,
2045 const struct iovec iovec[], unsigned n_iovec,
2046 uint64_t *seqnum,
f4474e00 2047 Object **ret, uint64_t *ret_offset) {
d180c349 2048
cec736d2
LP
2049 unsigned i;
2050 EntryItem *items;
2051 int r;
2052 uint64_t xor_hash = 0;
de190aef 2053 struct dual_timestamp _ts;
cec736d2
LP
2054
2055 assert(f);
c88cc6af 2056 assert(f->header);
cec736d2
LP
2057 assert(iovec || n_iovec == 0);
2058
c6273953 2059 if (ts) {
baaa35ad
ZJS
2060 if (!VALID_REALTIME(ts->realtime))
2061 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
2062 "Invalid realtime timestamp %" PRIu64 ", refusing entry.",
2063 ts->realtime);
2064 if (!VALID_MONOTONIC(ts->monotonic))
2065 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
2066 "Invalid monotomic timestamp %" PRIu64 ", refusing entry.",
2067 ts->monotonic);
c6273953 2068 } else {
de190aef
LP
2069 dual_timestamp_get(&_ts);
2070 ts = &_ts;
2071 }
2072
349cc4a5 2073#if HAVE_GCRYPT
7560fffc
LP
2074 r = journal_file_maybe_append_tag(f, ts->realtime);
2075 if (r < 0)
2076 return r;
feb12d3e 2077#endif
7560fffc 2078
64825d3c 2079 /* alloca() can't take 0, hence let's allocate at least one */
cf409d15 2080 items = newa(EntryItem, MAX(1u, n_iovec));
cec736d2
LP
2081
2082 for (i = 0; i < n_iovec; i++) {
2083 uint64_t p;
2084 Object *o;
2085
2086 r = journal_file_append_data(f, iovec[i].iov_base, iovec[i].iov_len, &o, &p);
2087 if (r < 0)
cf244689 2088 return r;
cec736d2 2089
4ce534f4
LP
2090 /* When calculating the XOR hash field, we need to take special care if the "keyed-hash"
2091 * journal file flag is on. We use the XOR hash field to quickly determine the identity of a
2092 * specific record, and give records with otherwise identical position (i.e. match in seqno,
2093 * timestamp, …) a stable ordering. But for that we can't have it that the hash of the
2094 * objects in each file is different since they are keyed. Hence let's calculate the Jenkins
2095 * hash here for that. This also has the benefit that cursors for old and new journal files
2096 * are completely identical (they include the XOR hash after all). For classic Jenkins-hash
2097 * files things are easier, we can just take the value from the stored record directly. */
2098
2099 if (JOURNAL_HEADER_KEYED_HASH(f->header))
2100 xor_hash ^= jenkins_hash64(iovec[i].iov_base, iovec[i].iov_len);
2101 else
2102 xor_hash ^= le64toh(o->data.hash);
2103
cec736d2 2104 items[i].object_offset = htole64(p);
de7b95cd 2105 items[i].hash = o->data.hash;
cec736d2
LP
2106 }
2107
1f2da9ec
LP
2108 /* Order by the position on disk, in order to improve seek
2109 * times for rotating media. */
93bab288 2110 typesafe_qsort(items, n_iovec, entry_item_cmp);
1f2da9ec 2111
f4474e00 2112 r = journal_file_append_entry_internal(f, ts, boot_id, xor_hash, items, n_iovec, seqnum, ret, ret_offset);
cec736d2 2113
fa6ac760
LP
2114 /* If the memory mapping triggered a SIGBUS then we return an
2115 * IO error and ignore the error code passed down to us, since
2116 * it is very likely just an effect of a nullified replacement
2117 * mapping page */
2118
be7cdd8e 2119 if (mmap_cache_got_sigbus(f->mmap, f->cache_fd))
fa6ac760
LP
2120 r = -EIO;
2121
7a24f3bf
VC
2122 if (f->post_change_timer)
2123 schedule_post_change(f);
2124 else
2125 journal_file_post_change(f);
50f20cfd 2126
cec736d2
LP
2127 return r;
2128}
2129
a4bcff5b 2130typedef struct ChainCacheItem {
fb099c8d 2131 uint64_t first; /* the array at the beginning of the chain */
a4bcff5b
LP
2132 uint64_t array; /* the cached array */
2133 uint64_t begin; /* the first item in the cached array */
2134 uint64_t total; /* the total number of items in all arrays before this one in the chain */
f268980d 2135 uint64_t last_index; /* the last index we looked at, to optimize locality when bisecting */
a4bcff5b
LP
2136} ChainCacheItem;
2137
2138static void chain_cache_put(
4743015d 2139 OrderedHashmap *h,
a4bcff5b
LP
2140 ChainCacheItem *ci,
2141 uint64_t first,
2142 uint64_t array,
2143 uint64_t begin,
f268980d
LP
2144 uint64_t total,
2145 uint64_t last_index) {
a4bcff5b
LP
2146
2147 if (!ci) {
34741aa3
LP
2148 /* If the chain item to cache for this chain is the
2149 * first one it's not worth caching anything */
2150 if (array == first)
2151 return;
2152
29433089 2153 if (ordered_hashmap_size(h) >= CHAIN_CACHE_MAX) {
4743015d 2154 ci = ordered_hashmap_steal_first(h);
29433089
LP
2155 assert(ci);
2156 } else {
a4bcff5b
LP
2157 ci = new(ChainCacheItem, 1);
2158 if (!ci)
2159 return;
2160 }
2161
2162 ci->first = first;
2163
4743015d 2164 if (ordered_hashmap_put(h, &ci->first, ci) < 0) {
a4bcff5b
LP
2165 free(ci);
2166 return;
2167 }
2168 } else
2169 assert(ci->first == first);
2170
2171 ci->array = array;
2172 ci->begin = begin;
2173 ci->total = total;
f268980d 2174 ci->last_index = last_index;
a4bcff5b
LP
2175}
2176
f268980d
LP
2177static int generic_array_get(
2178 JournalFile *f,
2179 uint64_t first,
2180 uint64_t i,
f4474e00 2181 Object **ret, uint64_t *ret_offset) {
de190aef 2182
cec736d2 2183 Object *o;
a4bcff5b 2184 uint64_t p = 0, a, t = 0;
cec736d2 2185 int r;
a4bcff5b 2186 ChainCacheItem *ci;
cec736d2
LP
2187
2188 assert(f);
2189
de190aef 2190 a = first;
a4bcff5b
LP
2191
2192 /* Try the chain cache first */
4743015d 2193 ci = ordered_hashmap_get(f->chain_cache, &first);
a4bcff5b
LP
2194 if (ci && i > ci->total) {
2195 a = ci->array;
2196 i -= ci->total;
2197 t = ci->total;
2198 }
2199
de190aef 2200 while (a > 0) {
a4bcff5b 2201 uint64_t k;
cec736d2 2202
de190aef
LP
2203 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
2204 if (r < 0)
2205 return r;
cec736d2 2206
a4bcff5b
LP
2207 k = journal_file_entry_array_n_items(o);
2208 if (i < k) {
de190aef 2209 p = le64toh(o->entry_array.items[i]);
a4bcff5b 2210 goto found;
cec736d2
LP
2211 }
2212
a4bcff5b
LP
2213 i -= k;
2214 t += k;
de190aef
LP
2215 a = le64toh(o->entry_array.next_entry_array_offset);
2216 }
2217
a4bcff5b
LP
2218 return 0;
2219
2220found:
2221 /* Let's cache this item for the next invocation */
af13a6b0 2222 chain_cache_put(f->chain_cache, ci, first, a, le64toh(o->entry_array.items[0]), t, i);
de190aef
LP
2223
2224 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
2225 if (r < 0)
2226 return r;
2227
2228 if (ret)
2229 *ret = o;
2230
f4474e00
LP
2231 if (ret_offset)
2232 *ret_offset = p;
de190aef
LP
2233
2234 return 1;
2235}
2236
f268980d
LP
2237static int generic_array_get_plus_one(
2238 JournalFile *f,
2239 uint64_t extra,
2240 uint64_t first,
2241 uint64_t i,
f4474e00 2242 Object **ret, uint64_t *ret_offset) {
de190aef
LP
2243
2244 Object *o;
2245
2246 assert(f);
2247
2248 if (i == 0) {
2249 int r;
2250
2251 r = journal_file_move_to_object(f, OBJECT_ENTRY, extra, &o);
cec736d2
LP
2252 if (r < 0)
2253 return r;
2254
de190aef
LP
2255 if (ret)
2256 *ret = o;
cec736d2 2257
f4474e00
LP
2258 if (ret_offset)
2259 *ret_offset = extra;
cec736d2 2260
de190aef 2261 return 1;
cec736d2
LP
2262 }
2263
f4474e00 2264 return generic_array_get(f, first, i-1, ret, ret_offset);
de190aef 2265}
cec736d2 2266
de190aef
LP
2267enum {
2268 TEST_FOUND,
2269 TEST_LEFT,
2270 TEST_RIGHT
2271};
cec736d2 2272
f268980d
LP
2273static int generic_array_bisect(
2274 JournalFile *f,
2275 uint64_t first,
2276 uint64_t n,
2277 uint64_t needle,
2278 int (*test_object)(JournalFile *f, uint64_t p, uint64_t needle),
2279 direction_t direction,
2280 Object **ret,
f4474e00
LP
2281 uint64_t *ret_offset,
2282 uint64_t *ret_idx) {
f268980d
LP
2283
2284 uint64_t a, p, t = 0, i = 0, last_p = 0, last_index = (uint64_t) -1;
de190aef
LP
2285 bool subtract_one = false;
2286 Object *o, *array = NULL;
2287 int r;
a4bcff5b 2288 ChainCacheItem *ci;
cec736d2 2289
de190aef
LP
2290 assert(f);
2291 assert(test_object);
cec736d2 2292
a4bcff5b 2293 /* Start with the first array in the chain */
de190aef 2294 a = first;
a4bcff5b 2295
4743015d 2296 ci = ordered_hashmap_get(f->chain_cache, &first);
96d4d024 2297 if (ci && n > ci->total && ci->begin != 0) {
a4bcff5b
LP
2298 /* Ah, we have iterated this bisection array chain
2299 * previously! Let's see if we can skip ahead in the
2300 * chain, as far as the last time. But we can't jump
2301 * backwards in the chain, so let's check that
2302 * first. */
2303
2304 r = test_object(f, ci->begin, needle);
2305 if (r < 0)
2306 return r;
2307
2308 if (r == TEST_LEFT) {
f268980d 2309 /* OK, what we are looking for is right of the
a4bcff5b
LP
2310 * begin of this EntryArray, so let's jump
2311 * straight to previously cached array in the
2312 * chain */
2313
2314 a = ci->array;
2315 n -= ci->total;
2316 t = ci->total;
f268980d 2317 last_index = ci->last_index;
a4bcff5b
LP
2318 }
2319 }
2320
de190aef
LP
2321 while (a > 0) {
2322 uint64_t left, right, k, lp;
2323
2324 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &array);
cec736d2
LP
2325 if (r < 0)
2326 return r;
2327
de190aef
LP
2328 k = journal_file_entry_array_n_items(array);
2329 right = MIN(k, n);
2330 if (right <= 0)
2331 return 0;
cec736d2 2332
de190aef
LP
2333 i = right - 1;
2334 lp = p = le64toh(array->entry_array.items[i]);
2335 if (p <= 0)
bee6a291
LP
2336 r = -EBADMSG;
2337 else
2338 r = test_object(f, p, needle);
2339 if (r == -EBADMSG) {
2340 log_debug_errno(r, "Encountered invalid entry while bisecting, cutting algorithm short. (1)");
2341 n = i;
2342 continue;
2343 }
de190aef
LP
2344 if (r < 0)
2345 return r;
cec736d2 2346
de190aef
LP
2347 if (r == TEST_FOUND)
2348 r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT;
2349
2350 if (r == TEST_RIGHT) {
2351 left = 0;
2352 right -= 1;
f268980d
LP
2353
2354 if (last_index != (uint64_t) -1) {
2355 assert(last_index <= right);
2356
2357 /* If we cached the last index we
2358 * looked at, let's try to not to jump
2359 * too wildly around and see if we can
2360 * limit the range to look at early to
2361 * the immediate neighbors of the last
2362 * index we looked at. */
2363
2364 if (last_index > 0) {
2365 uint64_t x = last_index - 1;
2366
2367 p = le64toh(array->entry_array.items[x]);
2368 if (p <= 0)
2369 return -EBADMSG;
2370
2371 r = test_object(f, p, needle);
2372 if (r < 0)
2373 return r;
2374
2375 if (r == TEST_FOUND)
2376 r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT;
2377
2378 if (r == TEST_RIGHT)
2379 right = x;
2380 else
2381 left = x + 1;
2382 }
2383
2384 if (last_index < right) {
2385 uint64_t y = last_index + 1;
2386
2387 p = le64toh(array->entry_array.items[y]);
2388 if (p <= 0)
2389 return -EBADMSG;
2390
2391 r = test_object(f, p, needle);
2392 if (r < 0)
2393 return r;
2394
2395 if (r == TEST_FOUND)
2396 r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT;
2397
2398 if (r == TEST_RIGHT)
2399 right = y;
2400 else
2401 left = y + 1;
2402 }
f268980d
LP
2403 }
2404
de190aef
LP
2405 for (;;) {
2406 if (left == right) {
2407 if (direction == DIRECTION_UP)
2408 subtract_one = true;
2409
2410 i = left;
2411 goto found;
2412 }
2413
2414 assert(left < right);
de190aef 2415 i = (left + right) / 2;
f268980d 2416
de190aef
LP
2417 p = le64toh(array->entry_array.items[i]);
2418 if (p <= 0)
bee6a291
LP
2419 r = -EBADMSG;
2420 else
2421 r = test_object(f, p, needle);
2422 if (r == -EBADMSG) {
2423 log_debug_errno(r, "Encountered invalid entry while bisecting, cutting algorithm short. (2)");
2424 right = n = i;
2425 continue;
2426 }
de190aef
LP
2427 if (r < 0)
2428 return r;
cec736d2 2429
de190aef
LP
2430 if (r == TEST_FOUND)
2431 r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT;
2432
2433 if (r == TEST_RIGHT)
2434 right = i;
2435 else
2436 left = i + 1;
2437 }
2438 }
2439
2173cbf8 2440 if (k >= n) {
cbdca852
LP
2441 if (direction == DIRECTION_UP) {
2442 i = n;
2443 subtract_one = true;
2444 goto found;
2445 }
2446
cec736d2 2447 return 0;
cbdca852 2448 }
cec736d2 2449
de190aef
LP
2450 last_p = lp;
2451
2452 n -= k;
2453 t += k;
f268980d 2454 last_index = (uint64_t) -1;
de190aef 2455 a = le64toh(array->entry_array.next_entry_array_offset);
cec736d2
LP
2456 }
2457
2458 return 0;
de190aef
LP
2459
2460found:
2461 if (subtract_one && t == 0 && i == 0)
2462 return 0;
2463
a4bcff5b 2464 /* Let's cache this item for the next invocation */
af13a6b0 2465 chain_cache_put(f->chain_cache, ci, first, a, le64toh(array->entry_array.items[0]), t, subtract_one ? (i > 0 ? i-1 : (uint64_t) -1) : i);
a4bcff5b 2466
de190aef
LP
2467 if (subtract_one && i == 0)
2468 p = last_p;
2469 else if (subtract_one)
2470 p = le64toh(array->entry_array.items[i-1]);
2471 else
2472 p = le64toh(array->entry_array.items[i]);
2473
2474 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
2475 if (r < 0)
2476 return r;
2477
2478 if (ret)
2479 *ret = o;
2480
f4474e00
LP
2481 if (ret_offset)
2482 *ret_offset = p;
de190aef 2483
f4474e00
LP
2484 if (ret_idx)
2485 *ret_idx = t + i + (subtract_one ? -1 : 0);
de190aef
LP
2486
2487 return 1;
cec736d2
LP
2488}
2489
f268980d
LP
2490static int generic_array_bisect_plus_one(
2491 JournalFile *f,
2492 uint64_t extra,
2493 uint64_t first,
2494 uint64_t n,
2495 uint64_t needle,
2496 int (*test_object)(JournalFile *f, uint64_t p, uint64_t needle),
2497 direction_t direction,
2498 Object **ret,
f4474e00
LP
2499 uint64_t *ret_offset,
2500 uint64_t *ret_idx) {
de190aef 2501
cec736d2 2502 int r;
cbdca852
LP
2503 bool step_back = false;
2504 Object *o;
cec736d2
LP
2505
2506 assert(f);
de190aef 2507 assert(test_object);
cec736d2 2508
de190aef
LP
2509 if (n <= 0)
2510 return 0;
cec736d2 2511
de190aef
LP
2512 /* This bisects the array in object 'first', but first checks
2513 * an extra */
de190aef
LP
2514 r = test_object(f, extra, needle);
2515 if (r < 0)
2516 return r;
a536e261
LP
2517
2518 if (r == TEST_FOUND)
2519 r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT;
2520
cbdca852
LP
2521 /* if we are looking with DIRECTION_UP then we need to first
2522 see if in the actual array there is a matching entry, and
2523 return the last one of that. But if there isn't any we need
2524 to return this one. Hence remember this, and return it
2525 below. */
2526 if (r == TEST_LEFT)
2527 step_back = direction == DIRECTION_UP;
de190aef 2528
cbdca852
LP
2529 if (r == TEST_RIGHT) {
2530 if (direction == DIRECTION_DOWN)
2531 goto found;
2532 else
2533 return 0;
a536e261 2534 }
cec736d2 2535
f4474e00 2536 r = generic_array_bisect(f, first, n-1, needle, test_object, direction, ret, ret_offset, ret_idx);
de190aef 2537
cbdca852
LP
2538 if (r == 0 && step_back)
2539 goto found;
2540
f4474e00
LP
2541 if (r > 0 && ret_idx)
2542 (*ret_idx)++;
de190aef
LP
2543
2544 return r;
cbdca852
LP
2545
2546found:
2547 r = journal_file_move_to_object(f, OBJECT_ENTRY, extra, &o);
2548 if (r < 0)
2549 return r;
2550
2551 if (ret)
2552 *ret = o;
2553
f4474e00
LP
2554 if (ret_offset)
2555 *ret_offset = extra;
cbdca852 2556
f4474e00
LP
2557 if (ret_idx)
2558 *ret_idx = 0;
cbdca852
LP
2559
2560 return 1;
2561}
2562
44a6b1b6 2563_pure_ static int test_object_offset(JournalFile *f, uint64_t p, uint64_t needle) {
cbdca852
LP
2564 assert(f);
2565 assert(p > 0);
2566
2567 if (p == needle)
2568 return TEST_FOUND;
2569 else if (p < needle)
2570 return TEST_LEFT;
2571 else
2572 return TEST_RIGHT;
2573}
2574
de190aef 2575static int test_object_seqnum(JournalFile *f, uint64_t p, uint64_t needle) {
893e0f8f 2576 uint64_t sq;
de190aef
LP
2577 Object *o;
2578 int r;
2579
2580 assert(f);
2581 assert(p > 0);
2582
2583 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
cec736d2
LP
2584 if (r < 0)
2585 return r;
2586
893e0f8f
LP
2587 sq = le64toh(READ_NOW(o->entry.seqnum));
2588 if (sq == needle)
de190aef 2589 return TEST_FOUND;
893e0f8f 2590 else if (sq < needle)
de190aef
LP
2591 return TEST_LEFT;
2592 else
2593 return TEST_RIGHT;
2594}
cec736d2 2595
de190aef
LP
2596int journal_file_move_to_entry_by_seqnum(
2597 JournalFile *f,
2598 uint64_t seqnum,
2599 direction_t direction,
2600 Object **ret,
f4474e00 2601 uint64_t *ret_offset) {
c88cc6af
VC
2602 assert(f);
2603 assert(f->header);
de190aef 2604
f4474e00
LP
2605 return generic_array_bisect(
2606 f,
2607 le64toh(f->header->entry_array_offset),
2608 le64toh(f->header->n_entries),
2609 seqnum,
2610 test_object_seqnum,
2611 direction,
2612 ret, ret_offset, NULL);
de190aef 2613}
cec736d2 2614
de190aef
LP
2615static int test_object_realtime(JournalFile *f, uint64_t p, uint64_t needle) {
2616 Object *o;
893e0f8f 2617 uint64_t rt;
de190aef
LP
2618 int r;
2619
2620 assert(f);
2621 assert(p > 0);
2622
2623 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
2624 if (r < 0)
2625 return r;
2626
893e0f8f
LP
2627 rt = le64toh(READ_NOW(o->entry.realtime));
2628 if (rt == needle)
de190aef 2629 return TEST_FOUND;
893e0f8f 2630 else if (rt < needle)
de190aef
LP
2631 return TEST_LEFT;
2632 else
2633 return TEST_RIGHT;
cec736d2
LP
2634}
2635
de190aef
LP
2636int journal_file_move_to_entry_by_realtime(
2637 JournalFile *f,
2638 uint64_t realtime,
2639 direction_t direction,
2640 Object **ret,
f4474e00 2641 uint64_t *ret_offset) {
c88cc6af
VC
2642 assert(f);
2643 assert(f->header);
de190aef 2644
f4474e00
LP
2645 return generic_array_bisect(
2646 f,
2647 le64toh(f->header->entry_array_offset),
2648 le64toh(f->header->n_entries),
2649 realtime,
2650 test_object_realtime,
2651 direction,
2652 ret, ret_offset, NULL);
de190aef
LP
2653}
2654
2655static int test_object_monotonic(JournalFile *f, uint64_t p, uint64_t needle) {
2656 Object *o;
893e0f8f 2657 uint64_t m;
de190aef
LP
2658 int r;
2659
2660 assert(f);
2661 assert(p > 0);
2662
2663 r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
2664 if (r < 0)
2665 return r;
2666
893e0f8f
LP
2667 m = le64toh(READ_NOW(o->entry.monotonic));
2668 if (m == needle)
de190aef 2669 return TEST_FOUND;
893e0f8f 2670 else if (m < needle)
de190aef
LP
2671 return TEST_LEFT;
2672 else
2673 return TEST_RIGHT;
2674}
2675
2a560338 2676static int find_data_object_by_boot_id(
47838ab3
ZJS
2677 JournalFile *f,
2678 sd_id128_t boot_id,
2679 Object **o,
2680 uint64_t *b) {
2a560338 2681
fbd0b64f 2682 char t[STRLEN("_BOOT_ID=") + 32 + 1] = "_BOOT_ID=";
47838ab3
ZJS
2683
2684 sd_id128_to_string(boot_id, t + 9);
2685 return journal_file_find_data_object(f, t, sizeof(t) - 1, o, b);
2686}
2687
de190aef
LP
2688int journal_file_move_to_entry_by_monotonic(
2689 JournalFile *f,
2690 sd_id128_t boot_id,
2691 uint64_t monotonic,
2692 direction_t direction,
2693 Object **ret,
f4474e00 2694 uint64_t *ret_offset) {
de190aef 2695
de190aef
LP
2696 Object *o;
2697 int r;
2698
cbdca852 2699 assert(f);
de190aef 2700
47838ab3 2701 r = find_data_object_by_boot_id(f, boot_id, &o, NULL);
de190aef
LP
2702 if (r < 0)
2703 return r;
cbdca852 2704 if (r == 0)
de190aef
LP
2705 return -ENOENT;
2706
f4474e00
LP
2707 return generic_array_bisect_plus_one(
2708 f,
2709 le64toh(o->data.entry_offset),
2710 le64toh(o->data.entry_array_offset),
2711 le64toh(o->data.n_entries),
2712 monotonic,
2713 test_object_monotonic,
2714 direction,
2715 ret, ret_offset, NULL);
de190aef
LP
2716}
2717
1fc605b0 2718void journal_file_reset_location(JournalFile *f) {
6573ef05 2719 f->location_type = LOCATION_HEAD;
1fc605b0 2720 f->current_offset = 0;
6573ef05
MS
2721 f->current_seqnum = 0;
2722 f->current_realtime = 0;
2723 f->current_monotonic = 0;
2724 zero(f->current_boot_id);
2725 f->current_xor_hash = 0;
2726}
2727
950c07d4 2728void journal_file_save_location(JournalFile *f, Object *o, uint64_t offset) {
6573ef05
MS
2729 f->location_type = LOCATION_SEEK;
2730 f->current_offset = offset;
2731 f->current_seqnum = le64toh(o->entry.seqnum);
2732 f->current_realtime = le64toh(o->entry.realtime);
2733 f->current_monotonic = le64toh(o->entry.monotonic);
2734 f->current_boot_id = o->entry.boot_id;
2735 f->current_xor_hash = le64toh(o->entry.xor_hash);
1fc605b0
MS
2736}
2737
d8ae66d7 2738int journal_file_compare_locations(JournalFile *af, JournalFile *bf) {
90c88092
YW
2739 int r;
2740
d8ae66d7 2741 assert(af);
c88cc6af 2742 assert(af->header);
d8ae66d7 2743 assert(bf);
c88cc6af 2744 assert(bf->header);
d8ae66d7
MS
2745 assert(af->location_type == LOCATION_SEEK);
2746 assert(bf->location_type == LOCATION_SEEK);
2747
2748 /* If contents and timestamps match, these entries are
2749 * identical, even if the seqnum does not match */
2750 if (sd_id128_equal(af->current_boot_id, bf->current_boot_id) &&
2751 af->current_monotonic == bf->current_monotonic &&
2752 af->current_realtime == bf->current_realtime &&
2753 af->current_xor_hash == bf->current_xor_hash)
2754 return 0;
2755
2756 if (sd_id128_equal(af->header->seqnum_id, bf->header->seqnum_id)) {
2757
2758 /* If this is from the same seqnum source, compare
2759 * seqnums */
90c88092
YW
2760 r = CMP(af->current_seqnum, bf->current_seqnum);
2761 if (r != 0)
2762 return r;
d8ae66d7
MS
2763
2764 /* Wow! This is weird, different data but the same
2765 * seqnums? Something is borked, but let's make the
2766 * best of it and compare by time. */
2767 }
2768
2769 if (sd_id128_equal(af->current_boot_id, bf->current_boot_id)) {
2770
2771 /* If the boot id matches, compare monotonic time */
90c88092
YW
2772 r = CMP(af->current_monotonic, bf->current_monotonic);
2773 if (r != 0)
2774 return r;
d8ae66d7
MS
2775 }
2776
2777 /* Otherwise, compare UTC time */
90c88092
YW
2778 r = CMP(af->current_realtime, bf->current_realtime);
2779 if (r != 0)
2780 return r;
d8ae66d7
MS
2781
2782 /* Finally, compare by contents */
6dd91b36 2783 return CMP(af->current_xor_hash, bf->current_xor_hash);
d8ae66d7
MS
2784}
2785
aa598ba5
LP
2786static int bump_array_index(uint64_t *i, direction_t direction, uint64_t n) {
2787
2788 /* Increase or decrease the specified index, in the right direction. */
2789
2790 if (direction == DIRECTION_DOWN) {
2791 if (*i >= n - 1)
2792 return 0;
2793
2794 (*i) ++;
2795 } else {
2796 if (*i <= 0)
2797 return 0;
2798
2799 (*i) --;
2800 }
2801
2802 return 1;
2803}
2804
b6da4ed0
LP
2805static bool check_properly_ordered(uint64_t new_offset, uint64_t old_offset, direction_t direction) {
2806
2807 /* Consider it an error if any of the two offsets is uninitialized */
2808 if (old_offset == 0 || new_offset == 0)
2809 return false;
2810
2811 /* If we go down, the new offset must be larger than the old one. */
2812 return direction == DIRECTION_DOWN ?
2813 new_offset > old_offset :
2814 new_offset < old_offset;
2815}
2816
de190aef
LP
2817int journal_file_next_entry(
2818 JournalFile *f,
f534928a 2819 uint64_t p,
de190aef 2820 direction_t direction,
f4474e00 2821 Object **ret, uint64_t *ret_offset) {
de190aef 2822
fb099c8d 2823 uint64_t i, n, ofs;
cec736d2
LP
2824 int r;
2825
2826 assert(f);
c88cc6af 2827 assert(f->header);
de190aef 2828
893e0f8f 2829 n = le64toh(READ_NOW(f->header->n_entries));
de190aef
LP
2830 if (n <= 0)
2831 return 0;
cec736d2 2832
f534928a 2833 if (p == 0)
de190aef 2834 i = direction == DIRECTION_DOWN ? 0 : n - 1;
cec736d2 2835 else {
de190aef
LP
2836 r = generic_array_bisect(f,
2837 le64toh(f->header->entry_array_offset),
2838 le64toh(f->header->n_entries),
2839 p,
2840 test_object_offset,
2841 DIRECTION_DOWN,
2842 NULL, NULL,
2843 &i);
2844 if (r <= 0)
2845 return r;
2846
aa598ba5
LP
2847 r = bump_array_index(&i, direction, n);
2848 if (r <= 0)
2849 return r;
cec736d2
LP
2850 }
2851
de190aef 2852 /* And jump to it */
989793d3
LP
2853 for (;;) {
2854 r = generic_array_get(f,
2855 le64toh(f->header->entry_array_offset),
2856 i,
2857 ret, &ofs);
2858 if (r > 0)
2859 break;
2860 if (r != -EBADMSG)
2861 return r;
2862
2863 /* OK, so this entry is borked. Most likely some entry didn't get synced to disk properly, let's see if
2864 * the next one might work for us instead. */
2865 log_debug_errno(r, "Entry item %" PRIu64 " is bad, skipping over it.", i);
2866
2867 r = bump_array_index(&i, direction, n);
2868 if (r <= 0)
2869 return r;
caeab8f6 2870 }
fb099c8d 2871
b6da4ed0 2872 /* Ensure our array is properly ordered. */
baaa35ad
ZJS
2873 if (p > 0 && !check_properly_ordered(ofs, p, direction))
2874 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
2875 "%s: entry array not properly ordered at entry %" PRIu64,
2876 f->path, i);
fb099c8d 2877
f4474e00
LP
2878 if (ret_offset)
2879 *ret_offset = ofs;
fb099c8d
ZJS
2880
2881 return 1;
de190aef 2882}
cec736d2 2883
de190aef
LP
2884int journal_file_next_entry_for_data(
2885 JournalFile *f,
2886 Object *o, uint64_t p,
2887 uint64_t data_offset,
2888 direction_t direction,
f4474e00 2889 Object **ret, uint64_t *ret_offset) {
de190aef 2890
ded5034e 2891 uint64_t i, n, ofs;
de190aef 2892 Object *d;
989793d3 2893 int r;
cec736d2
LP
2894
2895 assert(f);
de190aef 2896 assert(p > 0 || !o);
cec736d2 2897
de190aef 2898 r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
466ccd92 2899 if (r < 0)
de190aef 2900 return r;
cec736d2 2901
893e0f8f 2902 n = le64toh(READ_NOW(d->data.n_entries));
de190aef
LP
2903 if (n <= 0)
2904 return n;
cec736d2 2905
de190aef
LP
2906 if (!o)
2907 i = direction == DIRECTION_DOWN ? 0 : n - 1;
2908 else {
2909 if (o->object.type != OBJECT_ENTRY)
2910 return -EINVAL;
cec736d2 2911
de190aef
LP
2912 r = generic_array_bisect_plus_one(f,
2913 le64toh(d->data.entry_offset),
2914 le64toh(d->data.entry_array_offset),
2915 le64toh(d->data.n_entries),
2916 p,
2917 test_object_offset,
2918 DIRECTION_DOWN,
2919 NULL, NULL,
2920 &i);
2921
2922 if (r <= 0)
cec736d2
LP
2923 return r;
2924
aa598ba5
LP
2925 r = bump_array_index(&i, direction, n);
2926 if (r <= 0)
2927 return r;
de190aef 2928 }
cec736d2 2929
989793d3
LP
2930 for (;;) {
2931 r = generic_array_get_plus_one(f,
2932 le64toh(d->data.entry_offset),
2933 le64toh(d->data.entry_array_offset),
2934 i,
2935 ret, &ofs);
2936 if (r > 0)
2937 break;
2938 if (r != -EBADMSG)
2939 return r;
2940
2941 log_debug_errno(r, "Data entry item %" PRIu64 " is bad, skipping over it.", i);
2942
2943 r = bump_array_index(&i, direction, n);
2944 if (r <= 0)
2945 return r;
2946 }
ded5034e
LP
2947
2948 /* Ensure our array is properly ordered. */
baaa35ad
ZJS
2949 if (p > 0 && check_properly_ordered(ofs, p, direction))
2950 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
2951 "%s data entry array not properly ordered at entry %" PRIu64,
2952 f->path, i);
ded5034e 2953
f4474e00
LP
2954 if (ret_offset)
2955 *ret_offset = ofs;
ded5034e
LP
2956
2957 return 1;
de190aef 2958}
cec736d2 2959
cbdca852
LP
2960int journal_file_move_to_entry_by_offset_for_data(
2961 JournalFile *f,
2962 uint64_t data_offset,
2963 uint64_t p,
2964 direction_t direction,
f4474e00 2965 Object **ret, uint64_t *ret_offset) {
cbdca852
LP
2966
2967 int r;
2968 Object *d;
2969
2970 assert(f);
2971
2972 r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
2973 if (r < 0)
2974 return r;
2975
f4474e00
LP
2976 return generic_array_bisect_plus_one(
2977 f,
2978 le64toh(d->data.entry_offset),
2979 le64toh(d->data.entry_array_offset),
2980 le64toh(d->data.n_entries),
2981 p,
2982 test_object_offset,
2983 direction,
2984 ret, ret_offset, NULL);
cbdca852
LP
2985}
2986
2987int journal_file_move_to_entry_by_monotonic_for_data(
2988 JournalFile *f,
2989 uint64_t data_offset,
2990 sd_id128_t boot_id,
2991 uint64_t monotonic,
2992 direction_t direction,
f4474e00 2993 Object **ret, uint64_t *ret_offset) {
cbdca852 2994
cbdca852
LP
2995 Object *o, *d;
2996 int r;
2997 uint64_t b, z;
2998
2999 assert(f);
3000
3001 /* First, seek by time */
47838ab3 3002 r = find_data_object_by_boot_id(f, boot_id, &o, &b);
cbdca852
LP
3003 if (r < 0)
3004 return r;
3005 if (r == 0)
3006 return -ENOENT;
3007
3008 r = generic_array_bisect_plus_one(f,
3009 le64toh(o->data.entry_offset),
3010 le64toh(o->data.entry_array_offset),
3011 le64toh(o->data.n_entries),
3012 monotonic,
3013 test_object_monotonic,
3014 direction,
3015 NULL, &z, NULL);
3016 if (r <= 0)
3017 return r;
3018
3019 /* And now, continue seeking until we find an entry that
3020 * exists in both bisection arrays */
3021
3022 for (;;) {
3023 Object *qo;
3024 uint64_t p, q;
3025
3026 r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
3027 if (r < 0)
3028 return r;
3029
3030 r = generic_array_bisect_plus_one(f,
3031 le64toh(d->data.entry_offset),
3032 le64toh(d->data.entry_array_offset),
3033 le64toh(d->data.n_entries),
3034 z,
3035 test_object_offset,
3036 direction,
3037 NULL, &p, NULL);
3038 if (r <= 0)
3039 return r;
3040
3041 r = journal_file_move_to_object(f, OBJECT_DATA, b, &o);
3042 if (r < 0)
3043 return r;
3044
3045 r = generic_array_bisect_plus_one(f,
3046 le64toh(o->data.entry_offset),
3047 le64toh(o->data.entry_array_offset),
3048 le64toh(o->data.n_entries),
3049 p,
3050 test_object_offset,
3051 direction,
3052 &qo, &q, NULL);
3053
3054 if (r <= 0)
3055 return r;
3056
3057 if (p == q) {
3058 if (ret)
3059 *ret = qo;
f4474e00
LP
3060 if (ret_offset)
3061 *ret_offset = q;
cbdca852
LP
3062
3063 return 1;
3064 }
3065
3066 z = q;
3067 }
cbdca852
LP
3068}
3069
de190aef
LP
3070int journal_file_move_to_entry_by_seqnum_for_data(
3071 JournalFile *f,
3072 uint64_t data_offset,
3073 uint64_t seqnum,
3074 direction_t direction,
f4474e00 3075 Object **ret, uint64_t *ret_offset) {
cec736d2 3076
de190aef
LP
3077 Object *d;
3078 int r;
cec736d2 3079
91a31dde
LP
3080 assert(f);
3081
de190aef 3082 r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
91a31dde 3083 if (r < 0)
de190aef 3084 return r;
cec736d2 3085
f4474e00
LP
3086 return generic_array_bisect_plus_one(
3087 f,
3088 le64toh(d->data.entry_offset),
3089 le64toh(d->data.entry_array_offset),
3090 le64toh(d->data.n_entries),
3091 seqnum,
3092 test_object_seqnum,
3093 direction,
3094 ret, ret_offset, NULL);
de190aef 3095}
cec736d2 3096
de190aef
LP
3097int journal_file_move_to_entry_by_realtime_for_data(
3098 JournalFile *f,
3099 uint64_t data_offset,
3100 uint64_t realtime,
3101 direction_t direction,
f4474e00 3102 Object **ret, uint64_t *ret_offset) {
de190aef
LP
3103
3104 Object *d;
3105 int r;
3106
91a31dde
LP
3107 assert(f);
3108
de190aef 3109 r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
91a31dde 3110 if (r < 0)
de190aef
LP
3111 return r;
3112
f4474e00
LP
3113 return generic_array_bisect_plus_one(
3114 f,
3115 le64toh(d->data.entry_offset),
3116 le64toh(d->data.entry_array_offset),
3117 le64toh(d->data.n_entries),
3118 realtime,
3119 test_object_realtime,
3120 direction,
3121 ret, ret_offset, NULL);
cec736d2
LP
3122}
3123
0284adc6 3124void journal_file_dump(JournalFile *f) {
7560fffc 3125 Object *o;
7560fffc 3126 int r;
0284adc6 3127 uint64_t p;
7560fffc
LP
3128
3129 assert(f);
c88cc6af 3130 assert(f->header);
7560fffc 3131
0284adc6 3132 journal_file_print_header(f);
7560fffc 3133
893e0f8f 3134 p = le64toh(READ_NOW(f->header->header_size));
0284adc6 3135 while (p != 0) {
d05089d8 3136 r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &o);
0284adc6
LP
3137 if (r < 0)
3138 goto fail;
7560fffc 3139
0284adc6 3140 switch (o->object.type) {
d98cc1f2 3141
0284adc6
LP
3142 case OBJECT_UNUSED:
3143 printf("Type: OBJECT_UNUSED\n");
3144 break;
d98cc1f2 3145
0284adc6
LP
3146 case OBJECT_DATA:
3147 printf("Type: OBJECT_DATA\n");
3148 break;
7560fffc 3149
3c1668da
LP
3150 case OBJECT_FIELD:
3151 printf("Type: OBJECT_FIELD\n");
3152 break;
3153
0284adc6 3154 case OBJECT_ENTRY:
507f22bd
ZJS
3155 printf("Type: OBJECT_ENTRY seqnum=%"PRIu64" monotonic=%"PRIu64" realtime=%"PRIu64"\n",
3156 le64toh(o->entry.seqnum),
3157 le64toh(o->entry.monotonic),
3158 le64toh(o->entry.realtime));
0284adc6 3159 break;
7560fffc 3160
0284adc6
LP
3161 case OBJECT_FIELD_HASH_TABLE:
3162 printf("Type: OBJECT_FIELD_HASH_TABLE\n");
3163 break;
7560fffc 3164
0284adc6
LP
3165 case OBJECT_DATA_HASH_TABLE:
3166 printf("Type: OBJECT_DATA_HASH_TABLE\n");
3167 break;
7560fffc 3168
0284adc6
LP
3169 case OBJECT_ENTRY_ARRAY:
3170 printf("Type: OBJECT_ENTRY_ARRAY\n");
3171 break;
7560fffc 3172
0284adc6 3173 case OBJECT_TAG:
507f22bd
ZJS
3174 printf("Type: OBJECT_TAG seqnum=%"PRIu64" epoch=%"PRIu64"\n",
3175 le64toh(o->tag.seqnum),
3176 le64toh(o->tag.epoch));
0284adc6 3177 break;
3c1668da
LP
3178
3179 default:
8facc349 3180 printf("Type: unknown (%i)\n", o->object.type);
3c1668da 3181 break;
0284adc6 3182 }
7560fffc 3183
d89c8fdf
ZJS
3184 if (o->object.flags & OBJECT_COMPRESSION_MASK)
3185 printf("Flags: %s\n",
3186 object_compressed_to_string(o->object.flags & OBJECT_COMPRESSION_MASK));
7560fffc 3187
0284adc6
LP
3188 if (p == le64toh(f->header->tail_object_offset))
3189 p = 0;
3190 else
71139898 3191 p += ALIGN64(le64toh(o->object.size));
0284adc6 3192 }
7560fffc 3193
0284adc6
LP
3194 return;
3195fail:
3196 log_error("File corrupt");
7560fffc
LP
3197}
3198
718fe4b1
ZJS
3199static const char* format_timestamp_safe(char *buf, size_t l, usec_t t) {
3200 const char *x;
3201
3202 x = format_timestamp(buf, l, t);
3203 if (x)
3204 return x;
3205 return " --- ";
3206}
3207
0284adc6 3208void journal_file_print_header(JournalFile *f) {
5905d7cf 3209 char a[SD_ID128_STRING_MAX], b[SD_ID128_STRING_MAX], c[SD_ID128_STRING_MAX], d[SD_ID128_STRING_MAX];
ed375beb 3210 char x[FORMAT_TIMESTAMP_MAX], y[FORMAT_TIMESTAMP_MAX], z[FORMAT_TIMESTAMP_MAX];
a1a03e30
LP
3211 struct stat st;
3212 char bytes[FORMAT_BYTES_MAX];
7560fffc
LP
3213
3214 assert(f);
c88cc6af 3215 assert(f->header);
7560fffc 3216
2c54acb1 3217 printf("File path: %s\n"
0284adc6
LP
3218 "File ID: %s\n"
3219 "Machine ID: %s\n"
3220 "Boot ID: %s\n"
2c54acb1 3221 "Sequential number ID: %s\n"
0284adc6 3222 "State: %s\n"
2c54acb1 3223 "Compatible flags:%s%s\n"
8653185a 3224 "Incompatible flags:%s%s%s%s%s\n"
507f22bd
ZJS
3225 "Header size: %"PRIu64"\n"
3226 "Arena size: %"PRIu64"\n"
2c54acb1
TN
3227 "Data hash table size: %"PRIu64"\n"
3228 "Field hash table size: %"PRIu64"\n"
3229 "Rotate suggested: %s\n"
3230 "Head sequential number: %"PRIu64" (%"PRIx64")\n"
3231 "Tail sequential number: %"PRIu64" (%"PRIx64")\n"
3232 "Head realtime timestamp: %s (%"PRIx64")\n"
3233 "Tail realtime timestamp: %s (%"PRIx64")\n"
3234 "Tail monotonic timestamp: %s (%"PRIx64")\n"
507f22bd 3235 "Objects: %"PRIu64"\n"
2c54acb1 3236 "Entry objects: %"PRIu64"\n",
0284adc6
LP
3237 f->path,
3238 sd_id128_to_string(f->header->file_id, a),
3239 sd_id128_to_string(f->header->machine_id, b),
3240 sd_id128_to_string(f->header->boot_id, c),
2765b7bb 3241 sd_id128_to_string(f->header->seqnum_id, d),
3223f44f
LP
3242 f->header->state == STATE_OFFLINE ? "OFFLINE" :
3243 f->header->state == STATE_ONLINE ? "ONLINE" :
3244 f->header->state == STATE_ARCHIVED ? "ARCHIVED" : "UNKNOWN",
8088cbd3 3245 JOURNAL_HEADER_SEALED(f->header) ? " SEALED" : "",
d89c8fdf
ZJS
3246 (le32toh(f->header->compatible_flags) & ~HEADER_COMPATIBLE_ANY) ? " ???" : "",
3247 JOURNAL_HEADER_COMPRESSED_XZ(f->header) ? " COMPRESSED-XZ" : "",
3248 JOURNAL_HEADER_COMPRESSED_LZ4(f->header) ? " COMPRESSED-LZ4" : "",
8653185a 3249 JOURNAL_HEADER_COMPRESSED_ZSTD(f->header) ? " COMPRESSED-ZSTD" : "",
4ce534f4 3250 JOURNAL_HEADER_KEYED_HASH(f->header) ? " KEYED-HASH" : "",
d89c8fdf 3251 (le32toh(f->header->incompatible_flags) & ~HEADER_INCOMPATIBLE_ANY) ? " ???" : "",
507f22bd
ZJS
3252 le64toh(f->header->header_size),
3253 le64toh(f->header->arena_size),
3254 le64toh(f->header->data_hash_table_size) / sizeof(HashItem),
3255 le64toh(f->header->field_hash_table_size) / sizeof(HashItem),
fb0951b0 3256 yes_no(journal_file_rotate_suggested(f, 0)),
0808b92f
LP
3257 le64toh(f->header->head_entry_seqnum), le64toh(f->header->head_entry_seqnum),
3258 le64toh(f->header->tail_entry_seqnum), le64toh(f->header->tail_entry_seqnum),
3259 format_timestamp_safe(x, sizeof(x), le64toh(f->header->head_entry_realtime)), le64toh(f->header->head_entry_realtime),
3260 format_timestamp_safe(y, sizeof(y), le64toh(f->header->tail_entry_realtime)), le64toh(f->header->tail_entry_realtime),
3261 format_timespan(z, sizeof(z), le64toh(f->header->tail_entry_monotonic), USEC_PER_MSEC), le64toh(f->header->tail_entry_monotonic),
507f22bd
ZJS
3262 le64toh(f->header->n_objects),
3263 le64toh(f->header->n_entries));
7560fffc 3264
0284adc6 3265 if (JOURNAL_HEADER_CONTAINS(f->header, n_data))
2c54acb1
TN
3266 printf("Data objects: %"PRIu64"\n"
3267 "Data hash table fill: %.1f%%\n",
507f22bd 3268 le64toh(f->header->n_data),
0284adc6 3269 100.0 * (double) le64toh(f->header->n_data) / ((double) (le64toh(f->header->data_hash_table_size) / sizeof(HashItem))));
7560fffc 3270
0284adc6 3271 if (JOURNAL_HEADER_CONTAINS(f->header, n_fields))
2c54acb1
TN
3272 printf("Field objects: %"PRIu64"\n"
3273 "Field hash table fill: %.1f%%\n",
507f22bd 3274 le64toh(f->header->n_fields),
0284adc6 3275 100.0 * (double) le64toh(f->header->n_fields) / ((double) (le64toh(f->header->field_hash_table_size) / sizeof(HashItem))));
3223f44f
LP
3276
3277 if (JOURNAL_HEADER_CONTAINS(f->header, n_tags))
2c54acb1 3278 printf("Tag objects: %"PRIu64"\n",
507f22bd 3279 le64toh(f->header->n_tags));
3223f44f 3280 if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays))
2c54acb1 3281 printf("Entry array objects: %"PRIu64"\n",
507f22bd 3282 le64toh(f->header->n_entry_arrays));
a1a03e30 3283
0dbe57ee
LP
3284 if (JOURNAL_HEADER_CONTAINS(f->header, field_hash_chain_depth))
3285 printf("Deepest field hash chain: %" PRIu64"\n",
3286 f->header->field_hash_chain_depth);
3287
3288 if (JOURNAL_HEADER_CONTAINS(f->header, data_hash_chain_depth))
3289 printf("Deepest data hash chain: %" PRIu64"\n",
3290 f->header->data_hash_chain_depth);
3291
a1a03e30 3292 if (fstat(f->fd, &st) >= 0)
59f448cf 3293 printf("Disk usage: %s\n", format_bytes(bytes, sizeof(bytes), (uint64_t) st.st_blocks * 512ULL));
7560fffc
LP
3294}
3295
fc68c929
LP
3296static int journal_file_warn_btrfs(JournalFile *f) {
3297 unsigned attrs;
3298 int r;
3299
3300 assert(f);
3301
3302 /* Before we write anything, check if the COW logic is turned
3303 * off on btrfs. Given our write pattern that is quite
3304 * unfriendly to COW file systems this should greatly improve
3305 * performance on COW file systems, such as btrfs, at the
3306 * expense of data integrity features (which shouldn't be too
3307 * bad, given that we do our own checksumming). */
3308
3309 r = btrfs_is_filesystem(f->fd);
3310 if (r < 0)
3311 return log_warning_errno(r, "Failed to determine if journal is on btrfs: %m");
3312 if (!r)
3313 return 0;
3314
3315 r = read_attr_fd(f->fd, &attrs);
3316 if (r < 0)
3317 return log_warning_errno(r, "Failed to read file attributes: %m");
3318
3319 if (attrs & FS_NOCOW_FL) {
3320 log_debug("Detected btrfs file system with copy-on-write disabled, all is good.");
3321 return 0;
3322 }
3323
3324 log_notice("Creating journal file %s on a btrfs file system, and copy-on-write is enabled. "
3325 "This is likely to slow down journal access substantially, please consider turning "
3326 "off the copy-on-write file attribute on the journal directory, using chattr +C.", f->path);
3327
3328 return 1;
3329}
3330
0284adc6 3331int journal_file_open(
5d1ce257 3332 int fd,
0284adc6
LP
3333 const char *fname,
3334 int flags,
3335 mode_t mode,
3336 bool compress,
57850536 3337 uint64_t compress_threshold_bytes,
baed47c3 3338 bool seal,
0284adc6
LP
3339 JournalMetrics *metrics,
3340 MMapCache *mmap_cache,
b58c888f 3341 Set *deferred_closes,
0284adc6
LP
3342 JournalFile *template,
3343 JournalFile **ret) {
7560fffc 3344
fa6ac760 3345 bool newly_created = false;
0284adc6 3346 JournalFile *f;
fa6ac760 3347 void *h;
0284adc6 3348 int r;
7560fffc 3349
0559d3a5 3350 assert(ret);
5d1ce257 3351 assert(fd >= 0 || fname);
7560fffc 3352
ec2ce0c5 3353 if (!IN_SET((flags & O_ACCMODE), O_RDONLY, O_RDWR))
0284adc6 3354 return -EINVAL;
7560fffc 3355
6eda13d3
LP
3356 if (fname && (flags & O_CREAT) && !endswith(fname, ".journal"))
3357 return -EINVAL;
7560fffc 3358
971b52c4 3359 f = new(JournalFile, 1);
0284adc6
LP
3360 if (!f)
3361 return -ENOMEM;
7560fffc 3362
971b52c4
LP
3363 *f = (JournalFile) {
3364 .fd = fd,
3365 .mode = mode,
3366
3367 .flags = flags,
3368 .prot = prot_from_flags(flags),
3369 .writable = (flags & O_ACCMODE) != O_RDONLY,
7560fffc 3370
8653185a
LP
3371#if HAVE_ZSTD
3372 .compress_zstd = compress,
3373#elif HAVE_LZ4
971b52c4 3374 .compress_lz4 = compress,
349cc4a5 3375#elif HAVE_XZ
971b52c4 3376 .compress_xz = compress,
48b61739 3377#endif
971b52c4
LP
3378 .compress_threshold_bytes = compress_threshold_bytes == (uint64_t) -1 ?
3379 DEFAULT_COMPRESS_THRESHOLD :
3380 MAX(MIN_COMPRESS_THRESHOLD, compress_threshold_bytes),
349cc4a5 3381#if HAVE_GCRYPT
971b52c4 3382 .seal = seal,
49a32d43 3383#endif
971b52c4 3384 };
7560fffc 3385
4ce534f4
LP
3386 /* We turn on keyed hashes by default, but provide an environment variable to turn them off, if
3387 * people really want that */
3388 r = getenv_bool("SYSTEMD_JOURNAL_KEYED_HASH");
3389 if (r < 0) {
3390 if (r != -ENXIO)
3391 log_debug_errno(r, "Failed to parse $SYSTEMD_JOURNAL_KEYED_HASH environment variable, ignoring.");
3392 f->keyed_hash = true;
3393 } else
3394 f->keyed_hash = r;
3395
170a434c 3396 if (DEBUG_LOGGING) {
4ce534f4 3397 static int last_seal = -1, last_compress = -1, last_keyed_hash = -1;
170a434c
ZJS
3398 static uint64_t last_bytes = UINT64_MAX;
3399 char bytes[FORMAT_BYTES_MAX];
3400
3401 if (last_seal != f->seal ||
4ce534f4 3402 last_keyed_hash != f->keyed_hash ||
170a434c
ZJS
3403 last_compress != JOURNAL_FILE_COMPRESS(f) ||
3404 last_bytes != f->compress_threshold_bytes) {
3405
4ce534f4
LP
3406 log_debug("Journal effective settings seal=%s keyed_hash=%s compress=%s compress_threshold_bytes=%s",
3407 yes_no(f->seal), yes_no(f->keyed_hash), yes_no(JOURNAL_FILE_COMPRESS(f)),
170a434c
ZJS
3408 format_bytes(bytes, sizeof bytes, f->compress_threshold_bytes));
3409 last_seal = f->seal;
4ce534f4 3410 last_keyed_hash = f->keyed_hash;
170a434c
ZJS
3411 last_compress = JOURNAL_FILE_COMPRESS(f);
3412 last_bytes = f->compress_threshold_bytes;
3413 }
3414 }
57850536 3415
0284adc6
LP
3416 if (mmap_cache)
3417 f->mmap = mmap_cache_ref(mmap_cache);
3418 else {
84168d80 3419 f->mmap = mmap_cache_new();
0284adc6
LP
3420 if (!f->mmap) {
3421 r = -ENOMEM;
3422 goto fail;
3423 }
3424 }
7560fffc 3425
7645c77b 3426 if (fname) {
5d1ce257 3427 f->path = strdup(fname);
7645c77b
ZJS
3428 if (!f->path) {
3429 r = -ENOMEM;
3430 goto fail;
3431 }
3432 } else {
817b1c5b
LP
3433 assert(fd >= 0);
3434
7645c77b
ZJS
3435 /* If we don't know the path, fill in something explanatory and vaguely useful */
3436 if (asprintf(&f->path, "/proc/self/%i", fd) < 0) {
3437 r = -ENOMEM;
3438 goto fail;
3439 }
0284adc6 3440 }
7560fffc 3441
4743015d 3442 f->chain_cache = ordered_hashmap_new(&uint64_hash_ops);
a4bcff5b
LP
3443 if (!f->chain_cache) {
3444 r = -ENOMEM;
3445 goto fail;
3446 }
3447
0284adc6 3448 if (f->fd < 0) {
817b1c5b
LP
3449 /* We pass O_NONBLOCK here, so that in case somebody pointed us to some character device node or FIFO
3450 * or so, we likely fail quickly than block for long. For regular files O_NONBLOCK has no effect, hence
3451 * it doesn't hurt in that case. */
3452
3453 f->fd = open(f->path, f->flags|O_CLOEXEC|O_NONBLOCK, f->mode);
5d1ce257
LP
3454 if (f->fd < 0) {
3455 r = -errno;
3456 goto fail;
3457 }
3458
3459 /* fds we opened here by us should also be closed by us. */
3460 f->close_fd = true;
817b1c5b
LP
3461
3462 r = fd_nonblock(f->fd, false);
3463 if (r < 0)
3464 goto fail;
7560fffc 3465 }
7560fffc 3466
be7cdd8e
VC
3467 f->cache_fd = mmap_cache_add_fd(f->mmap, f->fd);
3468 if (!f->cache_fd) {
3469 r = -ENOMEM;
3470 goto fail;
3471 }
3472
2678031a
LP
3473 r = journal_file_fstat(f);
3474 if (r < 0)
0284adc6 3475 goto fail;
7560fffc 3476
0284adc6 3477 if (f->last_stat.st_size == 0 && f->writable) {
11689d2a 3478
fc68c929 3479 (void) journal_file_warn_btrfs(f);
11689d2a 3480
4c2e1b39
LP
3481 /* Let's attach the creation time to the journal file, so that the vacuuming code knows the age of this
3482 * file even if the file might end up corrupted one day... Ideally we'd just use the creation time many
3483 * file systems maintain for each file, but the API to query this is very new, hence let's emulate this
3484 * via extended attributes. If extended attributes are not supported we'll just skip this, and rely
3485 * solely on mtime/atime/ctime of the file. */
3486 (void) fd_setcrtime(f->fd, 0);
7560fffc 3487
349cc4a5 3488#if HAVE_GCRYPT
0284adc6 3489 /* Try to load the FSPRG state, and if we can't, then
baed47c3 3490 * just don't do sealing */
49a32d43
LP
3491 if (f->seal) {
3492 r = journal_file_fss_load(f);
3493 if (r < 0)
3494 f->seal = false;
3495 }
feb12d3e 3496#endif
7560fffc 3497
0284adc6
LP
3498 r = journal_file_init_header(f, template);
3499 if (r < 0)
3500 goto fail;
7560fffc 3501
2678031a
LP
3502 r = journal_file_fstat(f);
3503 if (r < 0)
0284adc6 3504 goto fail;
fb0951b0
LP
3505
3506 newly_created = true;
0284adc6 3507 }
7560fffc 3508
0284adc6 3509 if (f->last_stat.st_size < (off_t) HEADER_SIZE_MIN) {
cfb571f3 3510 r = -ENODATA;
0284adc6
LP
3511 goto fail;
3512 }
7560fffc 3513
b42549ad 3514 r = mmap_cache_get(f->mmap, f->cache_fd, f->prot, CONTEXT_HEADER, true, 0, PAGE_ALIGN(sizeof(Header)), &f->last_stat, &h, NULL);
5087825e
LP
3515 if (r == -EINVAL) {
3516 /* Some file systems (jffs2 or p9fs) don't support mmap() properly (or only read-only
3517 * mmap()), and return EINVAL in that case. Let's propagate that as a more recognizable error
3518 * code. */
3519 r = -EAFNOSUPPORT;
3520 goto fail;
3521 }
977eaa1e 3522 if (r < 0)
0284adc6 3523 goto fail;
7560fffc 3524
fa6ac760
LP
3525 f->header = h;
3526
0284adc6 3527 if (!newly_created) {
f9168190 3528 set_clear_with_destructor(deferred_closes, journal_file_close);
b58c888f 3529
0284adc6
LP
3530 r = journal_file_verify_header(f);
3531 if (r < 0)
3532 goto fail;
3533 }
7560fffc 3534
349cc4a5 3535#if HAVE_GCRYPT
0284adc6 3536 if (!newly_created && f->writable) {
baed47c3 3537 r = journal_file_fss_load(f);
0284adc6
LP
3538 if (r < 0)
3539 goto fail;
3540 }
feb12d3e 3541#endif
cec736d2
LP
3542
3543 if (f->writable) {
4a92baf3
LP
3544 if (metrics) {
3545 journal_default_metrics(metrics, f->fd);
3546 f->metrics = *metrics;
3547 } else if (template)
3548 f->metrics = template->metrics;
3549
cec736d2
LP
3550 r = journal_file_refresh_header(f);
3551 if (r < 0)
3552 goto fail;
3553 }
3554
349cc4a5 3555#if HAVE_GCRYPT
baed47c3 3556 r = journal_file_hmac_setup(f);
14d10188
LP
3557 if (r < 0)
3558 goto fail;
feb12d3e 3559#endif
14d10188 3560
cec736d2 3561 if (newly_created) {
de190aef 3562 r = journal_file_setup_field_hash_table(f);
cec736d2
LP
3563 if (r < 0)
3564 goto fail;
3565
de190aef 3566 r = journal_file_setup_data_hash_table(f);
cec736d2
LP
3567 if (r < 0)
3568 goto fail;
7560fffc 3569
349cc4a5 3570#if HAVE_GCRYPT
7560fffc
LP
3571 r = journal_file_append_first_tag(f);
3572 if (r < 0)
3573 goto fail;
feb12d3e 3574#endif
cec736d2
LP
3575 }
3576
be7cdd8e 3577 if (mmap_cache_got_sigbus(f->mmap, f->cache_fd)) {
fa6ac760
LP
3578 r = -EIO;
3579 goto fail;
3580 }
3581
7a24f3bf 3582 if (template && template->post_change_timer) {
e167d7fd
LP
3583 r = journal_file_enable_post_change_timer(
3584 f,
3585 sd_event_source_get_event(template->post_change_timer),
3586 template->post_change_timer_period);
7a24f3bf 3587
7a24f3bf
VC
3588 if (r < 0)
3589 goto fail;
3590 }
3591
f8e2f4d6 3592 /* The file is opened now successfully, thus we take possession of any passed in fd. */
5d1ce257
LP
3593 f->close_fd = true;
3594
0559d3a5 3595 *ret = f;
cec736d2
LP
3596 return 0;
3597
3598fail:
be7cdd8e 3599 if (f->cache_fd && mmap_cache_got_sigbus(f->mmap, f->cache_fd))
fa6ac760
LP
3600 r = -EIO;
3601
69a3a6fd 3602 (void) journal_file_close(f);
cec736d2
LP
3603
3604 return r;
3605}
0ac38b70 3606
7a4d21ad 3607int journal_file_archive(JournalFile *f) {
57535f47 3608 _cleanup_free_ char *p = NULL;
0ac38b70
LP
3609
3610 assert(f);
0ac38b70 3611
7a4d21ad 3612 if (!f->writable)
0ac38b70
LP
3613 return -EINVAL;
3614
5d1ce257 3615 /* Is this a journal file that was passed to us as fd? If so, we synthesized a path name for it, and we refuse
13e785f7 3616 * rotation, since we don't know the actual path, and couldn't rename the file hence. */
7a4d21ad 3617 if (path_startswith(f->path, "/proc/self/fd"))
5d1ce257
LP
3618 return -EINVAL;
3619
7a4d21ad 3620 if (!endswith(f->path, ".journal"))
0ac38b70
LP
3621 return -EINVAL;
3622
7a4d21ad
LP
3623 if (asprintf(&p, "%.*s@" SD_ID128_FORMAT_STR "-%016"PRIx64"-%016"PRIx64".journal",
3624 (int) strlen(f->path) - 8, f->path,
3625 SD_ID128_FORMAT_VAL(f->header->seqnum_id),
3626 le64toh(f->header->head_entry_seqnum),
3627 le64toh(f->header->head_entry_realtime)) < 0)
0ac38b70
LP
3628 return -ENOMEM;
3629
7a4d21ad
LP
3630 /* Try to rename the file to the archived version. If the file already was deleted, we'll get ENOENT, let's
3631 * ignore that case. */
3632 if (rename(f->path, p) < 0 && errno != ENOENT)
0ac38b70
LP
3633 return -errno;
3634
1fcefd88 3635 /* Sync the rename to disk */
7a4d21ad
LP
3636 (void) fsync_directory_of_file(f->fd);
3637
3638 /* Set as archive so offlining commits w/state=STATE_ARCHIVED. Previously we would set old_file->header->state
3639 * to STATE_ARCHIVED directly here, but journal_file_set_offline() short-circuits when state != STATE_ONLINE,
3640 * which would result in the rotated journal never getting fsync() called before closing. Now we simply queue
3641 * the archive state by setting an archive bit, leaving the state as STATE_ONLINE so proper offlining
3642 * occurs. */
3643 f->archive = true;
3644
3645 /* Currently, btrfs is not very good with out write patterns and fragments heavily. Let's defrag our journal
3646 * files when we archive them */
3647 f->defrag_on_close = true;
3648
3649 return 0;
3650}
3651
3652JournalFile* journal_initiate_close(
3653 JournalFile *f,
3654 Set *deferred_closes) {
3655
3656 int r;
3657
3658 assert(f);
3659
3660 if (deferred_closes) {
0ac38b70 3661
7a4d21ad
LP
3662 r = set_put(deferred_closes, f);
3663 if (r < 0)
3664 log_debug_errno(r, "Failed to add file to deferred close set, closing immediately.");
3665 else {
3666 (void) journal_file_set_offline(f, false);
3667 return NULL;
3668 }
3669 }
3670
3671 return journal_file_close(f);
3672}
3673
3674int journal_file_rotate(
3675 JournalFile **f,
3676 bool compress,
3677 uint64_t compress_threshold_bytes,
3678 bool seal,
3679 Set *deferred_closes) {
3680
3681 JournalFile *new_file = NULL;
3682 int r;
3683
3684 assert(f);
3685 assert(*f);
3686
3687 r = journal_file_archive(*f);
3688 if (r < 0)
3689 return r;
3690
3691 r = journal_file_open(
3692 -1,
3693 (*f)->path,
3694 (*f)->flags,
3695 (*f)->mode,
3696 compress,
3697 compress_threshold_bytes,
3698 seal,
3699 NULL, /* metrics */
3700 (*f)->mmap,
3701 deferred_closes,
3702 *f, /* template */
3703 &new_file);
3704
3705 journal_initiate_close(*f, deferred_closes);
0ac38b70 3706 *f = new_file;
7a4d21ad 3707
0ac38b70
LP
3708 return r;
3709}
3710
68127658
LP
3711int journal_file_dispose(int dir_fd, const char *fname) {
3712 _cleanup_free_ char *p = NULL;
3713 _cleanup_close_ int fd = -1;
3714
3715 assert(fname);
3716
3717 /* Renames a journal file to *.journal~, i.e. to mark it as corruped or otherwise uncleanly shutdown. Note that
3718 * this is done without looking into the file or changing any of its contents. The idea is that this is called
3719 * whenever something is suspicious and we want to move the file away and make clear that it is not accessed
3720 * for writing anymore. */
3721
3722 if (!endswith(fname, ".journal"))
3723 return -EINVAL;
3724
3725 if (asprintf(&p, "%.*s@%016" PRIx64 "-%016" PRIx64 ".journal~",
3726 (int) strlen(fname) - 8, fname,
3727 now(CLOCK_REALTIME),
3728 random_u64()) < 0)
3729 return -ENOMEM;
3730
3731 if (renameat(dir_fd, fname, dir_fd, p) < 0)
3732 return -errno;
3733
3734 /* btrfs doesn't cope well with our write pattern and fragments heavily. Let's defrag all files we rotate */
3735 fd = openat(dir_fd, p, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
3736 if (fd < 0)
3737 log_debug_errno(errno, "Failed to open file for defragmentation/FS_NOCOW_FL, ignoring: %m");
3738 else {
3739 (void) chattr_fd(fd, 0, FS_NOCOW_FL, NULL);
3740 (void) btrfs_defrag_fd(fd);
3741 }
3742
3743 return 0;
3744}
3745
9447a7f1
LP
3746int journal_file_open_reliably(
3747 const char *fname,
3748 int flags,
3749 mode_t mode,
7560fffc 3750 bool compress,
57850536 3751 uint64_t compress_threshold_bytes,
baed47c3 3752 bool seal,
4a92baf3 3753 JournalMetrics *metrics,
27370278 3754 MMapCache *mmap_cache,
b58c888f 3755 Set *deferred_closes,
9447a7f1
LP
3756 JournalFile *template,
3757 JournalFile **ret) {
3758
68127658 3759 int r;
9447a7f1 3760
57850536
AG
3761 r = journal_file_open(-1, fname, flags, mode, compress, compress_threshold_bytes, seal, metrics, mmap_cache,
3762 deferred_closes, template, ret);
288359db 3763 if (!IN_SET(r,
b288cdeb
ZJS
3764 -EBADMSG, /* Corrupted */
3765 -ENODATA, /* Truncated */
3766 -EHOSTDOWN, /* Other machine */
3767 -EPROTONOSUPPORT, /* Incompatible feature */
3768 -EBUSY, /* Unclean shutdown */
3769 -ESHUTDOWN, /* Already archived */
288359db 3770 -EIO, /* IO error, including SIGBUS on mmap */
ae739cc1
LP
3771 -EIDRM, /* File has been deleted */
3772 -ETXTBSY)) /* File is from the future */
9447a7f1
LP
3773 return r;
3774
3775 if ((flags & O_ACCMODE) == O_RDONLY)
3776 return r;
3777
3778 if (!(flags & O_CREAT))
3779 return r;
3780
7560fffc
LP
3781 if (!endswith(fname, ".journal"))
3782 return r;
3783
5c70eab4 3784 /* The file is corrupted. Rotate it away and try it again (but only once) */
65089b82 3785 log_warning_errno(r, "File %s corrupted or uncleanly shut down, renaming and replacing.", fname);
9447a7f1 3786
68127658
LP
3787 r = journal_file_dispose(AT_FDCWD, fname);
3788 if (r < 0)
3789 return r;
3790
57850536
AG
3791 return journal_file_open(-1, fname, flags, mode, compress, compress_threshold_bytes, seal, metrics, mmap_cache,
3792 deferred_closes, template, ret);
9447a7f1
LP
3793}
3794
5a271b08 3795int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint64_t p) {
cf244689
LP
3796 uint64_t i, n;
3797 uint64_t q, xor_hash = 0;
3798 int r;
3799 EntryItem *items;
3800 dual_timestamp ts;
d180c349 3801 const sd_id128_t *boot_id;
cf244689
LP
3802
3803 assert(from);
3804 assert(to);
3805 assert(o);
3806 assert(p);
3807
3808 if (!to->writable)
3809 return -EPERM;
3810
3811 ts.monotonic = le64toh(o->entry.monotonic);
3812 ts.realtime = le64toh(o->entry.realtime);
d180c349 3813 boot_id = &o->entry.boot_id;
cf244689 3814
cf244689 3815 n = journal_file_entry_n_items(o);
4faa7004 3816 /* alloca() can't take 0, hence let's allocate at least one */
cf409d15 3817 items = newa(EntryItem, MAX(1u, n));
cf244689
LP
3818
3819 for (i = 0; i < n; i++) {
4fd052ae
FC
3820 uint64_t l, h;
3821 le64_t le_hash;
cf244689
LP
3822 size_t t;
3823 void *data;
3824 Object *u;
3825
3826 q = le64toh(o->entry.items[i].object_offset);
3827 le_hash = o->entry.items[i].hash;
3828
3829 r = journal_file_move_to_object(from, OBJECT_DATA, q, &o);
3830 if (r < 0)
3831 return r;
3832
3833 if (le_hash != o->data.hash)
3834 return -EBADMSG;
3835
893e0f8f
LP
3836 l = le64toh(READ_NOW(o->object.size));
3837 if (l < offsetof(Object, data.payload))
3838 return -EBADMSG;
3839
3840 l -= offsetof(Object, data.payload);
cf244689
LP
3841 t = (size_t) l;
3842
3843 /* We hit the limit on 32bit machines */
3844 if ((uint64_t) t != l)
3845 return -E2BIG;
3846
d89c8fdf 3847 if (o->object.flags & OBJECT_COMPRESSION_MASK) {
d80b051c 3848#if HAVE_COMPRESSION
a7f7d1bd 3849 size_t rsize = 0;
cf244689 3850
d89c8fdf
ZJS
3851 r = decompress_blob(o->object.flags & OBJECT_COMPRESSION_MASK,
3852 o->data.payload, l, &from->compress_buffer, &from->compress_buffer_size, &rsize, 0);
3853 if (r < 0)
3854 return r;
cf244689
LP
3855
3856 data = from->compress_buffer;
3857 l = rsize;
3b1a55e1
ZJS
3858#else
3859 return -EPROTONOSUPPORT;
3860#endif
cf244689
LP
3861 } else
3862 data = o->data.payload;
3863
3864 r = journal_file_append_data(to, data, l, &u, &h);
3865 if (r < 0)
3866 return r;
3867
4ce534f4
LP
3868 if (JOURNAL_HEADER_KEYED_HASH(to->header))
3869 xor_hash ^= jenkins_hash64(data, l);
3870 else
3871 xor_hash ^= le64toh(u->data.hash);
3872
cf244689
LP
3873 items[i].object_offset = htole64(h);
3874 items[i].hash = u->data.hash;
3875
3876 r = journal_file_move_to_object(from, OBJECT_ENTRY, p, &o);
3877 if (r < 0)
3878 return r;
3879 }
3880
d180c349
ZJS
3881 r = journal_file_append_entry_internal(to, &ts, boot_id, xor_hash, items, n,
3882 NULL, NULL, NULL);
fa6ac760 3883
be7cdd8e 3884 if (mmap_cache_got_sigbus(to->mmap, to->cache_fd))
fa6ac760
LP
3885 return -EIO;
3886
3887 return r;
cf244689 3888}
babfc091 3889
8580d1f7
LP
3890void journal_reset_metrics(JournalMetrics *m) {
3891 assert(m);
3892
3893 /* Set everything to "pick automatic values". */
3894
3895 *m = (JournalMetrics) {
3896 .min_use = (uint64_t) -1,
3897 .max_use = (uint64_t) -1,
3898 .min_size = (uint64_t) -1,
3899 .max_size = (uint64_t) -1,
3900 .keep_free = (uint64_t) -1,
3901 .n_max_files = (uint64_t) -1,
3902 };
3903}
3904
babfc091 3905void journal_default_metrics(JournalMetrics *m, int fd) {
8580d1f7 3906 char a[FORMAT_BYTES_MAX], b[FORMAT_BYTES_MAX], c[FORMAT_BYTES_MAX], d[FORMAT_BYTES_MAX], e[FORMAT_BYTES_MAX];
babfc091 3907 struct statvfs ss;
6aae0b1a 3908 uint64_t fs_size = 0;
babfc091
LP
3909
3910 assert(m);
3911 assert(fd >= 0);
3912
3913 if (fstatvfs(fd, &ss) >= 0)
3914 fs_size = ss.f_frsize * ss.f_blocks;
6aae0b1a 3915 else
8fc58f1a 3916 log_debug_errno(errno, "Failed to determine disk size: %m");
babfc091
LP
3917
3918 if (m->max_use == (uint64_t) -1) {
3919
6aae0b1a
ZJS
3920 if (fs_size > 0)
3921 m->max_use = CLAMP(PAGE_ALIGN(fs_size / 10), /* 10% of file system size */
3922 MAX_USE_LOWER, MAX_USE_UPPER);
3923 else
3924 m->max_use = MAX_USE_LOWER;
babfc091
LP
3925 } else {
3926 m->max_use = PAGE_ALIGN(m->max_use);
3927
8580d1f7 3928 if (m->max_use != 0 && m->max_use < JOURNAL_FILE_SIZE_MIN*2)
babfc091
LP
3929 m->max_use = JOURNAL_FILE_SIZE_MIN*2;
3930 }
3931
6aae0b1a
ZJS
3932 if (m->min_use == (uint64_t) -1) {
3933 if (fs_size > 0)
3934 m->min_use = CLAMP(PAGE_ALIGN(fs_size / 50), /* 2% of file system size */
3935 MIN_USE_LOW, MIN_USE_HIGH);
3936 else
3937 m->min_use = MIN_USE_LOW;
3938 }
8580d1f7
LP
3939
3940 if (m->min_use > m->max_use)
3941 m->min_use = m->max_use;
3942
6aae0b1a
ZJS
3943 if (m->max_size == (uint64_t) -1)
3944 m->max_size = MIN(PAGE_ALIGN(m->max_use / 8), /* 8 chunks */
3945 MAX_SIZE_UPPER);
3946 else
babfc091
LP
3947 m->max_size = PAGE_ALIGN(m->max_size);
3948
8580d1f7
LP
3949 if (m->max_size != 0) {
3950 if (m->max_size < JOURNAL_FILE_SIZE_MIN)
3951 m->max_size = JOURNAL_FILE_SIZE_MIN;
babfc091 3952
8580d1f7
LP
3953 if (m->max_use != 0 && m->max_size*2 > m->max_use)
3954 m->max_use = m->max_size*2;
3955 }
babfc091
LP
3956
3957 if (m->min_size == (uint64_t) -1)
3958 m->min_size = JOURNAL_FILE_SIZE_MIN;
6aae0b1a
ZJS
3959 else
3960 m->min_size = CLAMP(PAGE_ALIGN(m->min_size),
3961 JOURNAL_FILE_SIZE_MIN,
3962 m->max_size ?: UINT64_MAX);
babfc091
LP
3963
3964 if (m->keep_free == (uint64_t) -1) {
6aae0b1a
ZJS
3965 if (fs_size > 0)
3966 m->keep_free = MIN(PAGE_ALIGN(fs_size / 20), /* 5% of file system size */
3967 KEEP_FREE_UPPER);
3968 else
babfc091
LP
3969 m->keep_free = DEFAULT_KEEP_FREE;
3970 }
3971
8580d1f7
LP
3972 if (m->n_max_files == (uint64_t) -1)
3973 m->n_max_files = DEFAULT_N_MAX_FILES;
3974
3975 log_debug("Fixed min_use=%s max_use=%s max_size=%s min_size=%s keep_free=%s n_max_files=%" PRIu64,
3976 format_bytes(a, sizeof(a), m->min_use),
3977 format_bytes(b, sizeof(b), m->max_use),
3978 format_bytes(c, sizeof(c), m->max_size),
3979 format_bytes(d, sizeof(d), m->min_size),
3980 format_bytes(e, sizeof(e), m->keep_free),
3981 m->n_max_files);
babfc091 3982}
08984293
LP
3983
3984int journal_file_get_cutoff_realtime_usec(JournalFile *f, usec_t *from, usec_t *to) {
08984293 3985 assert(f);
c88cc6af 3986 assert(f->header);
08984293
LP
3987 assert(from || to);
3988
3989 if (from) {
162566a4
LP
3990 if (f->header->head_entry_realtime == 0)
3991 return -ENOENT;
08984293 3992
162566a4 3993 *from = le64toh(f->header->head_entry_realtime);
08984293
LP
3994 }
3995
3996 if (to) {
162566a4
LP
3997 if (f->header->tail_entry_realtime == 0)
3998 return -ENOENT;
08984293 3999
162566a4 4000 *to = le64toh(f->header->tail_entry_realtime);
08984293
LP
4001 }
4002
4003 return 1;
4004}
4005
4006int journal_file_get_cutoff_monotonic_usec(JournalFile *f, sd_id128_t boot_id, usec_t *from, usec_t *to) {
08984293
LP
4007 Object *o;
4008 uint64_t p;
4009 int r;
4010
4011 assert(f);
4012 assert(from || to);
4013
47838ab3 4014 r = find_data_object_by_boot_id(f, boot_id, &o, &p);
08984293
LP
4015 if (r <= 0)
4016 return r;
4017
4018 if (le64toh(o->data.n_entries) <= 0)
4019 return 0;
4020
4021 if (from) {
4022 r = journal_file_move_to_object(f, OBJECT_ENTRY, le64toh(o->data.entry_offset), &o);
4023 if (r < 0)
4024 return r;
4025
4026 *from = le64toh(o->entry.monotonic);
4027 }
4028
4029 if (to) {
4030 r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
4031 if (r < 0)
4032 return r;
4033
4034 r = generic_array_get_plus_one(f,
4035 le64toh(o->data.entry_offset),
4036 le64toh(o->data.entry_array_offset),
4037 le64toh(o->data.n_entries)-1,
4038 &o, NULL);
4039 if (r <= 0)
4040 return r;
4041
4042 *to = le64toh(o->entry.monotonic);
4043 }
4044
4045 return 1;
4046}
dca6219e 4047
fb0951b0 4048bool journal_file_rotate_suggested(JournalFile *f, usec_t max_file_usec) {
dca6219e 4049 assert(f);
c88cc6af 4050 assert(f->header);
dca6219e
LP
4051
4052 /* If we gained new header fields we gained new features,
4053 * hence suggest a rotation */
361f9cbc
LP
4054 if (le64toh(f->header->header_size) < sizeof(Header)) {
4055 log_debug("%s uses an outdated header, suggesting rotation.", f->path);
dca6219e 4056 return true;
361f9cbc 4057 }
dca6219e 4058
0dbe57ee
LP
4059 /* Let's check if the hash tables grew over a certain fill level (75%, borrowing this value from
4060 * Java's hash table implementation), and if so suggest a rotation. To calculate the fill level we
4061 * need the n_data field, which only exists in newer versions. */
dca6219e
LP
4062
4063 if (JOURNAL_HEADER_CONTAINS(f->header, n_data))
361f9cbc 4064 if (le64toh(f->header->n_data) * 4ULL > (le64toh(f->header->data_hash_table_size) / sizeof(HashItem)) * 3ULL) {
507f22bd 4065 log_debug("Data hash table of %s has a fill level at %.1f (%"PRIu64" of %"PRIu64" items, %llu file size, %"PRIu64" bytes per hash table item), suggesting rotation.",
361f9cbc
LP
4066 f->path,
4067 100.0 * (double) le64toh(f->header->n_data) / ((double) (le64toh(f->header->data_hash_table_size) / sizeof(HashItem))),
507f22bd
ZJS
4068 le64toh(f->header->n_data),
4069 le64toh(f->header->data_hash_table_size) / sizeof(HashItem),
4070 (unsigned long long) f->last_stat.st_size,
4071 f->last_stat.st_size / le64toh(f->header->n_data));
dca6219e 4072 return true;
361f9cbc 4073 }
dca6219e
LP
4074
4075 if (JOURNAL_HEADER_CONTAINS(f->header, n_fields))
361f9cbc 4076 if (le64toh(f->header->n_fields) * 4ULL > (le64toh(f->header->field_hash_table_size) / sizeof(HashItem)) * 3ULL) {
507f22bd 4077 log_debug("Field hash table of %s has a fill level at %.1f (%"PRIu64" of %"PRIu64" items), suggesting rotation.",
361f9cbc
LP
4078 f->path,
4079 100.0 * (double) le64toh(f->header->n_fields) / ((double) (le64toh(f->header->field_hash_table_size) / sizeof(HashItem))),
507f22bd
ZJS
4080 le64toh(f->header->n_fields),
4081 le64toh(f->header->field_hash_table_size) / sizeof(HashItem));
dca6219e 4082 return true;
361f9cbc 4083 }
dca6219e 4084
0dbe57ee
LP
4085 /* If there are too many hash collisions somebody is most likely playing games with us. Hence, if our
4086 * longest chain is longer than some threshold, let's suggest rotation. */
4087 if (JOURNAL_HEADER_CONTAINS(f->header, data_hash_chain_depth) &&
4088 le64toh(f->header->data_hash_chain_depth) > HASH_CHAIN_DEPTH_MAX) {
4089 log_debug("Data hash table of %s has deepest hash chain of length %" PRIu64 ", suggesting rotation.",
4090 f->path, le64toh(f->header->data_hash_chain_depth));
4091 return true;
4092 }
4093
4094 if (JOURNAL_HEADER_CONTAINS(f->header, field_hash_chain_depth) &&
4095 le64toh(f->header->field_hash_chain_depth) > HASH_CHAIN_DEPTH_MAX) {
4096 log_debug("Field hash table of %s has deepest hash chain of length at %" PRIu64 ", suggesting rotation.",
4097 f->path, le64toh(f->header->field_hash_chain_depth));
4098 return true;
4099 }
4100
0598fd4a
LP
4101 /* Are the data objects properly indexed by field objects? */
4102 if (JOURNAL_HEADER_CONTAINS(f->header, n_data) &&
4103 JOURNAL_HEADER_CONTAINS(f->header, n_fields) &&
4104 le64toh(f->header->n_data) > 0 &&
4105 le64toh(f->header->n_fields) == 0)
4106 return true;
4107
fb0951b0
LP
4108 if (max_file_usec > 0) {
4109 usec_t t, h;
4110
4111 h = le64toh(f->header->head_entry_realtime);
4112 t = now(CLOCK_REALTIME);
4113
4114 if (h > 0 && t > h + max_file_usec)
4115 return true;
4116 }
4117
dca6219e
LP
4118 return false;
4119}