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