journal-file.c revision 0240c603691e006165d8687d6a2c70859755b11f
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt/***
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt This file is part of systemd.
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
7bd8e95d44977833d0de3fc4e893eb3bc84351d6Patrik Flykt Copyright 2011 Lennart Poettering
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt systemd is free software; you can redistribute it and/or modify it
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt under the terms of the GNU Lesser General Public License as published by
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt the Free Software Foundation; either version 2.1 of the License, or
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt (at your option) any later version.
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt systemd is distributed in the hope that it will be useful, but
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt WITHOUT ANY WARRANTY; without even the implied warranty of
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt Lesser General Public License for more details.
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt You should have received a copy of the GNU Lesser General Public License
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt along with systemd; If not, see <http://www.gnu.org/licenses/>.
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt***/
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt#include <sys/mman.h>
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt#include <errno.h>
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt#include <sys/uio.h>
76253e73f9c9c24fec755e485516f3b55d0707b4Dan Williams#include <unistd.h>
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt#include <sys/statvfs.h>
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt#include <fcntl.h>
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt#include <stddef.h>
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt#include <linux/fs.h>
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
3df3e884ae1237ef0d4d23b0e80f4ffda95ac135Ronny Chevalier#include "btrfs-util.h"
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt#include "journal-def.h"
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt#include "journal-file.h"
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt#include "journal-authenticate.h"
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt#include "lookup3.h"
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt#include "compress.h"
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt#include "random-util.h"
764aad6258eec3bd4ae62ea341ea507bd69ce628Tom Gundersen
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt#define DEFAULT_DATA_HASH_TABLE_SIZE (2047ULL*sizeof(HashItem))
76253e73f9c9c24fec755e485516f3b55d0707b4Dan Williams#define DEFAULT_FIELD_HASH_TABLE_SIZE (333ULL*sizeof(HashItem))
76253e73f9c9c24fec755e485516f3b55d0707b4Dan Williams
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt#define COMPRESSION_SIZE_THRESHOLD (512ULL)
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt/* This is the minimum journal file size */
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt#define JOURNAL_FILE_SIZE_MIN (4ULL*1024ULL*1024ULL) /* 4 MiB */
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt/* These are the lower and upper bounds if we deduce the max_use value
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt * from the file system size */
76253e73f9c9c24fec755e485516f3b55d0707b4Dan Williams#define DEFAULT_MAX_USE_LOWER (1ULL*1024ULL*1024ULL) /* 1 MiB */
76253e73f9c9c24fec755e485516f3b55d0707b4Dan Williams#define DEFAULT_MAX_USE_UPPER (4ULL*1024ULL*1024ULL*1024ULL) /* 4 GiB */
76253e73f9c9c24fec755e485516f3b55d0707b4Dan Williams
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt/* This is the default minimal use limit, how much we'll use even if keep_free suggests otherwise. */
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt#define DEFAULT_MIN_USE (1ULL*1024ULL*1024ULL) /* 1 MiB */
346e13a25dc6f76d3bc9d8decd40dc4782b02d2aPatrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt/* This is the upper bound if we deduce max_size from max_use */
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt#define DEFAULT_MAX_SIZE_UPPER (128ULL*1024ULL*1024ULL) /* 128 MiB */
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt/* This is the upper bound if we deduce the keep_free value from the
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt * file system size */
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt#define DEFAULT_KEEP_FREE_UPPER (4ULL*1024ULL*1024ULL*1024ULL) /* 4 GiB */
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt/* This is the keep_free value when we can't determine the system
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt * size */
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt#define DEFAULT_KEEP_FREE (1024ULL*1024ULL) /* 1 MB */
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt/* This is the default maximum number of journal files to keep around. */
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt#define DEFAULT_N_MAX_FILES (100)
764aad6258eec3bd4ae62ea341ea507bd69ce628Tom Gundersen
66eac1201a9c1596f5901f8dbbf24bda7e350878Dan Williams/* n_data was the first entry we added after the initial file format design */
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt#define HEADER_SIZE_MIN ALIGN64(offsetof(Header, n_data))
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt/* How many entries to keep in the entry array chain cache at max */
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt#define CHAIN_CACHE_MAX 20
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt/* How much to increase the journal file size at once each time we allocate something new. */
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt#define FILE_SIZE_INCREASE (8ULL*1024ULL*1024ULL) /* 8MB */
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt/* Reread fstat() of the file for detecting deletions at least this often */
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt#define LAST_STAT_REFRESH_USEC (5*USEC_PER_SEC)
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt/* The mmap context to use for the header we pick as one above the last defined typed */
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt#define CONTEXT_HEADER _OBJECT_TYPE_MAX
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flyktstatic int journal_file_set_online(JournalFile *f) {
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt assert(f);
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt if (!f->writable)
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt return -EPERM;
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt if (!(f->fd >= 0 && f->header))
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt return -EINVAL;
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt if (mmap_cache_got_sigbus(f->mmap, f->fd))
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt return -EIO;
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt switch(f->header->state) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt case STATE_ONLINE:
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return 0;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt case STATE_OFFLINE:
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt f->header->state = STATE_ONLINE;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt fsync(f->fd);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return 0;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt default:
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return -EINVAL;
3f0c075f8ef3344da5a6bda524540201f9204e61Patrik Flykt }
3f0c075f8ef3344da5a6bda524540201f9204e61Patrik Flykt}
3f0c075f8ef3344da5a6bda524540201f9204e61Patrik Flykt
3f0c075f8ef3344da5a6bda524540201f9204e61Patrik Flyktint journal_file_set_offline(JournalFile *f) {
3f0c075f8ef3344da5a6bda524540201f9204e61Patrik Flykt assert(f);
3f0c075f8ef3344da5a6bda524540201f9204e61Patrik Flykt
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt if (!f->writable)
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt return -EPERM;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (!(f->fd >= 0 && f->header))
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt return -EINVAL;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (f->header->state != STATE_ONLINE)
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt return 0;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt fsync(f->fd);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (mmap_cache_got_sigbus(f->mmap, f->fd))
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt return -EIO;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt f->header->state = STATE_OFFLINE;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (mmap_cache_got_sigbus(f->mmap, f->fd))
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt return -EIO;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt fsync(f->fd);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt return 0;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt}
76253e73f9c9c24fec755e485516f3b55d0707b4Dan Williams
76253e73f9c9c24fec755e485516f3b55d0707b4Dan WilliamsJournalFile* journal_file_close(JournalFile *f) {
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt assert(f);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
76253e73f9c9c24fec755e485516f3b55d0707b4Dan Williams#ifdef HAVE_GCRYPT
76253e73f9c9c24fec755e485516f3b55d0707b4Dan Williams /* Write the final tag */
76253e73f9c9c24fec755e485516f3b55d0707b4Dan Williams if (f->seal && f->writable)
76253e73f9c9c24fec755e485516f3b55d0707b4Dan Williams journal_file_append_tag(f);
76253e73f9c9c24fec755e485516f3b55d0707b4Dan Williams#endif
76253e73f9c9c24fec755e485516f3b55d0707b4Dan Williams
76253e73f9c9c24fec755e485516f3b55d0707b4Dan Williams journal_file_set_offline(f);
76253e73f9c9c24fec755e485516f3b55d0707b4Dan Williams
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (f->mmap && f->fd >= 0)
76253e73f9c9c24fec755e485516f3b55d0707b4Dan Williams mmap_cache_close_fd(f->mmap, f->fd);
76253e73f9c9c24fec755e485516f3b55d0707b4Dan Williams
76253e73f9c9c24fec755e485516f3b55d0707b4Dan Williams if (f->fd >= 0 && f->defrag_on_close) {
76253e73f9c9c24fec755e485516f3b55d0707b4Dan Williams
76253e73f9c9c24fec755e485516f3b55d0707b4Dan Williams /* Be friendly to btrfs: turn COW back on again now,
76253e73f9c9c24fec755e485516f3b55d0707b4Dan Williams * and defragment the file. We won't write to the file
76253e73f9c9c24fec755e485516f3b55d0707b4Dan Williams * ever again, hence remove all fragmentation, and
76253e73f9c9c24fec755e485516f3b55d0707b4Dan Williams * reenable all the good bits COW usually provides
76253e73f9c9c24fec755e485516f3b55d0707b4Dan Williams * (such as data checksumming). */
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt (void) chattr_fd(f->fd, 0, FS_NOCOW_FL);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt (void) btrfs_defrag_fd(f->fd);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt }
cc22955cfefb4bd6e7a135f1ec95fb5a07ba9ce3Thomas Haller
cc22955cfefb4bd6e7a135f1ec95fb5a07ba9ce3Thomas Haller safe_close(f->fd);
cc22955cfefb4bd6e7a135f1ec95fb5a07ba9ce3Thomas Haller free(f->path);
cc22955cfefb4bd6e7a135f1ec95fb5a07ba9ce3Thomas Haller
cc22955cfefb4bd6e7a135f1ec95fb5a07ba9ce3Thomas Haller if (f->mmap)
cc22955cfefb4bd6e7a135f1ec95fb5a07ba9ce3Thomas Haller mmap_cache_unref(f->mmap);
cc22955cfefb4bd6e7a135f1ec95fb5a07ba9ce3Thomas Haller
ebe207d4acf38165adbc45298662982eecdb9e9fTom Gundersen ordered_hashmap_free_free(f->chain_cache);
66eac1201a9c1596f5901f8dbbf24bda7e350878Dan Williams
66eac1201a9c1596f5901f8dbbf24bda7e350878Dan Williams#if defined(HAVE_XZ) || defined(HAVE_LZ4)
66eac1201a9c1596f5901f8dbbf24bda7e350878Dan Williams free(f->compress_buffer);
66eac1201a9c1596f5901f8dbbf24bda7e350878Dan Williams#endif
66eac1201a9c1596f5901f8dbbf24bda7e350878Dan Williams
66eac1201a9c1596f5901f8dbbf24bda7e350878Dan Williams#ifdef HAVE_GCRYPT
fe4b2156256c5bdf52341576571ce9f095d9f085Tom Gundersen if (f->fss_file)
fe4b2156256c5bdf52341576571ce9f095d9f085Tom Gundersen munmap(f->fss_file, PAGE_ALIGN(f->fss_file_size));
764aad6258eec3bd4ae62ea341ea507bd69ce628Tom Gundersen else
fe4b2156256c5bdf52341576571ce9f095d9f085Tom Gundersen free(f->fsprg_state);
fe4b2156256c5bdf52341576571ce9f095d9f085Tom Gundersen
fe4b2156256c5bdf52341576571ce9f095d9f085Tom Gundersen free(f->fsprg_seed);
764aad6258eec3bd4ae62ea341ea507bd69ce628Tom Gundersen
fe4b2156256c5bdf52341576571ce9f095d9f085Tom Gundersen if (f->hmac)
fe4b2156256c5bdf52341576571ce9f095d9f085Tom Gundersen gcry_md_close(f->hmac);
fe4b2156256c5bdf52341576571ce9f095d9f085Tom Gundersen#endif
764aad6258eec3bd4ae62ea341ea507bd69ce628Tom Gundersen
fe4b2156256c5bdf52341576571ce9f095d9f085Tom Gundersen free(f);
fe4b2156256c5bdf52341576571ce9f095d9f085Tom Gundersen return NULL;
fe4b2156256c5bdf52341576571ce9f095d9f085Tom Gundersen}
764aad6258eec3bd4ae62ea341ea507bd69ce628Tom Gundersen
fe4b2156256c5bdf52341576571ce9f095d9f085Tom Gundersenstatic int journal_file_init_header(JournalFile *f, JournalFile *template) {
fe4b2156256c5bdf52341576571ce9f095d9f085Tom Gundersen Header h = {};
fe4b2156256c5bdf52341576571ce9f095d9f085Tom Gundersen ssize_t k;
fe4b2156256c5bdf52341576571ce9f095d9f085Tom Gundersen int r;
fe4b2156256c5bdf52341576571ce9f095d9f085Tom Gundersen
fe4b2156256c5bdf52341576571ce9f095d9f085Tom Gundersen assert(f);
fe4b2156256c5bdf52341576571ce9f095d9f085Tom Gundersen
764aad6258eec3bd4ae62ea341ea507bd69ce628Tom Gundersen memcpy(h.signature, HEADER_SIGNATURE, 8);
ebe207d4acf38165adbc45298662982eecdb9e9fTom Gundersen h.header_size = htole64(ALIGN64(sizeof(h)));
764aad6258eec3bd4ae62ea341ea507bd69ce628Tom Gundersen
66eac1201a9c1596f5901f8dbbf24bda7e350878Dan Williams h.incompatible_flags |= htole32(
66eac1201a9c1596f5901f8dbbf24bda7e350878Dan Williams f->compress_xz * HEADER_INCOMPATIBLE_COMPRESSED_XZ |
66eac1201a9c1596f5901f8dbbf24bda7e350878Dan Williams f->compress_lz4 * HEADER_INCOMPATIBLE_COMPRESSED_LZ4);
66eac1201a9c1596f5901f8dbbf24bda7e350878Dan Williams
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt h.compatible_flags = htole32(
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt f->seal * HEADER_COMPATIBLE_SEALED);
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt r = sd_id128_randomize(&h.file_id);
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt if (r < 0)
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt return r;
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt if (template) {
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt h.seqnum_id = template->header->seqnum_id;
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt h.tail_entry_seqnum = template->header->tail_entry_seqnum;
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt } else
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt h.seqnum_id = h.file_id;
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt k = pwrite(f->fd, &h, sizeof(h), 0);
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt if (k < 0)
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt return -errno;
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt if (k != sizeof(h))
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt return -EIO;
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt return 0;
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt}
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flyktstatic int journal_file_refresh_header(JournalFile *f) {
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt sd_id128_t boot_id;
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt int r;
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt assert(f);
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt r = sd_id128_get_machine(&f->header->machine_id);
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt if (r < 0)
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt return r;
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt r = sd_id128_get_boot(&boot_id);
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt if (r < 0)
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt return r;
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt if (sd_id128_equal(boot_id, f->header->boot_id))
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt f->tail_entry_monotonic_valid = true;
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt f->header->boot_id = boot_id;
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt r = journal_file_set_online(f);
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt /* Sync the online state to disk */
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt fsync(f->fd);
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt return r;
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt}
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt
ea3b3a75abb3f8b853f7da454b9b8e258a120eeaPatrik Flyktstatic int journal_file_verify_header(JournalFile *f) {
ea3b3a75abb3f8b853f7da454b9b8e258a120eeaPatrik Flykt uint32_t flags;
ea3b3a75abb3f8b853f7da454b9b8e258a120eeaPatrik Flykt
ea3b3a75abb3f8b853f7da454b9b8e258a120eeaPatrik Flykt assert(f);
ea3b3a75abb3f8b853f7da454b9b8e258a120eeaPatrik Flykt
ea3b3a75abb3f8b853f7da454b9b8e258a120eeaPatrik Flykt if (memcmp(f->header->signature, HEADER_SIGNATURE, 8))
ea3b3a75abb3f8b853f7da454b9b8e258a120eeaPatrik Flykt return -EBADMSG;
ea3b3a75abb3f8b853f7da454b9b8e258a120eeaPatrik Flykt
ea3b3a75abb3f8b853f7da454b9b8e258a120eeaPatrik Flykt /* In both read and write mode we refuse to open files with
ea3b3a75abb3f8b853f7da454b9b8e258a120eeaPatrik Flykt * incompatible flags we don't know */
ea3b3a75abb3f8b853f7da454b9b8e258a120eeaPatrik Flykt flags = le32toh(f->header->incompatible_flags);
ea3b3a75abb3f8b853f7da454b9b8e258a120eeaPatrik Flykt if (flags & ~HEADER_INCOMPATIBLE_SUPPORTED) {
3f0c075f8ef3344da5a6bda524540201f9204e61Patrik Flykt if (flags & ~HEADER_INCOMPATIBLE_ANY)
3f0c075f8ef3344da5a6bda524540201f9204e61Patrik Flykt log_debug("Journal file %s has unknown incompatible flags %"PRIx32,
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt f->path, flags & ~HEADER_INCOMPATIBLE_ANY);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt flags = (flags & HEADER_INCOMPATIBLE_ANY) & ~HEADER_INCOMPATIBLE_SUPPORTED;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (flags)
c806ffb9592fa9a2b13a1f9f9be4c77cd5b211aaZbigniew Jędrzejewski-Szmek log_debug("Journal file %s uses incompatible flags %"PRIx32
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt " disabled at compilation time.", f->path, flags);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt return -EPROTONOSUPPORT;
4e3e6679e8f73b83d38e4b20d8b025e12991d1cbPatrik Flykt }
4e3e6679e8f73b83d38e4b20d8b025e12991d1cbPatrik Flykt
4e3e6679e8f73b83d38e4b20d8b025e12991d1cbPatrik Flykt /* When open for writing we refuse to open files with
4e3e6679e8f73b83d38e4b20d8b025e12991d1cbPatrik Flykt * compatible flags, too */
4e3e6679e8f73b83d38e4b20d8b025e12991d1cbPatrik Flykt flags = le32toh(f->header->compatible_flags);
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt if (f->writable && (flags & ~HEADER_COMPATIBLE_SUPPORTED)) {
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt if (flags & ~HEADER_COMPATIBLE_ANY)
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt log_debug("Journal file %s has unknown compatible flags %"PRIx32,
c806ffb9592fa9a2b13a1f9f9be4c77cd5b211aaZbigniew Jędrzejewski-Szmek f->path, flags & ~HEADER_COMPATIBLE_ANY);
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt flags = (flags & HEADER_COMPATIBLE_ANY) & ~HEADER_COMPATIBLE_SUPPORTED;
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt if (flags)
346e13a25dc6f76d3bc9d8decd40dc4782b02d2aPatrik Flykt log_debug("Journal file %s uses compatible flags %"PRIx32
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt " disabled at compilation time.", f->path, flags);
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt return -EPROTONOSUPPORT;
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt }
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt if (f->header->state >= _STATE_MAX)
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt return -EBADMSG;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt /* The first addition was n_data, so check that we are at least this large */
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt if (le64toh(f->header->header_size) < HEADER_SIZE_MIN)
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt return -EBADMSG;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt if (JOURNAL_HEADER_SEALED(f->header) && !JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays))
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt return -EBADMSG;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if ((le64toh(f->header->header_size) + le64toh(f->header->arena_size)) > (uint64_t) f->last_stat.st_size)
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt return -ENODATA;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
3f0c075f8ef3344da5a6bda524540201f9204e61Patrik Flykt if (le64toh(f->header->tail_object_offset) > (le64toh(f->header->header_size) + le64toh(f->header->arena_size)))
3f0c075f8ef3344da5a6bda524540201f9204e61Patrik Flykt return -ENODATA;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
3f0c075f8ef3344da5a6bda524540201f9204e61Patrik Flykt if (!VALID64(le64toh(f->header->data_hash_table_offset)) ||
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt !VALID64(le64toh(f->header->field_hash_table_offset)) ||
3f0c075f8ef3344da5a6bda524540201f9204e61Patrik Flykt !VALID64(le64toh(f->header->tail_object_offset)) ||
3f0c075f8ef3344da5a6bda524540201f9204e61Patrik Flykt !VALID64(le64toh(f->header->entry_array_offset)))
3f0c075f8ef3344da5a6bda524540201f9204e61Patrik Flykt return -ENODATA;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (f->writable) {
346e13a25dc6f76d3bc9d8decd40dc4782b02d2aPatrik Flykt uint8_t state;
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt sd_id128_t machine_id;
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt int r;
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt r = sd_id128_get_machine(&machine_id);
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt if (r < 0)
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt return r;
346e13a25dc6f76d3bc9d8decd40dc4782b02d2aPatrik Flykt
346e13a25dc6f76d3bc9d8decd40dc4782b02d2aPatrik Flykt if (!sd_id128_equal(machine_id, f->header->machine_id))
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt return -EHOSTDOWN;
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt state = f->header->state;
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt if (state == STATE_ONLINE) {
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt log_debug("Journal file %s is already online. Assuming unclean closing.", f->path);
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt return -EBUSY;
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt } else if (state == STATE_ARCHIVED)
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt return -ESHUTDOWN;
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt else if (state != STATE_OFFLINE) {
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt log_debug("Journal file %s has unknown state %i.", f->path, state);
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt return -EBUSY;
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt }
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt }
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt f->compress_xz = JOURNAL_HEADER_COMPRESSED_XZ(f->header);
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt f->compress_lz4 = JOURNAL_HEADER_COMPRESSED_LZ4(f->header);
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt f->seal = JOURNAL_HEADER_SEALED(f->header);
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt
ed6ee21953dac9c78383da00bc4514ece6b75ab5Patrik Flykt return 0;
ed6ee21953dac9c78383da00bc4514ece6b75ab5Patrik Flykt}
926695f1b5f9395eeb416cc2f478a9cf75fdbeb4Thomas Hindoe Paaboel Andersen
926695f1b5f9395eeb416cc2f478a9cf75fdbeb4Thomas Hindoe Paaboel Andersenstatic int journal_file_fstat(JournalFile *f) {
ed6ee21953dac9c78383da00bc4514ece6b75ab5Patrik Flykt assert(f);
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt assert(f->fd >= 0);
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt if (fstat(f->fd, &f->last_stat) < 0)
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt return -errno;
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt f->last_stat_usec = now(CLOCK_MONOTONIC);
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt /* Refuse appending to files that are already deleted */
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt if (f->last_stat.st_nlink <= 0)
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt return -EIDRM;
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt return 0;
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt}
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flyktstatic int journal_file_allocate(JournalFile *f, uint64_t offset, uint64_t size) {
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt uint64_t old_size, new_size;
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt int r;
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt assert(f);
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt /* We assume that this file is not sparse, and we know that
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt * for sure, since we always call posix_fallocate()
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt * ourselves */
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt if (mmap_cache_got_sigbus(f->mmap, f->fd))
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt return -EIO;
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt old_size =
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt le64toh(f->header->header_size) +
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt le64toh(f->header->arena_size);
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt new_size = PAGE_ALIGN(offset + size);
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt if (new_size < le64toh(f->header->header_size))
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt new_size = le64toh(f->header->header_size);
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt if (new_size <= old_size) {
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt /* We already pre-allocated enough space, but before
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt * we write to it, let's check with fstat() if the
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt * file got deleted, in order make sure we don't throw
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt * away the data immediately. Don't check fstat() for
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt * all writes though, but only once ever 10s. */
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt if (f->last_stat_usec + LAST_STAT_REFRESH_USEC > now(CLOCK_MONOTONIC))
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt return 0;
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt
cc22955cfefb4bd6e7a135f1ec95fb5a07ba9ce3Thomas Haller return journal_file_fstat(f);
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt }
66eac1201a9c1596f5901f8dbbf24bda7e350878Dan Williams
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt /* Allocate more space. */
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt if (f->metrics.max_size > 0 && new_size > f->metrics.max_size)
346e13a25dc6f76d3bc9d8decd40dc4782b02d2aPatrik Flykt return -E2BIG;
346e13a25dc6f76d3bc9d8decd40dc4782b02d2aPatrik Flykt
346e13a25dc6f76d3bc9d8decd40dc4782b02d2aPatrik Flykt if (new_size > f->metrics.min_size && f->metrics.keep_free > 0) {
346e13a25dc6f76d3bc9d8decd40dc4782b02d2aPatrik Flykt struct statvfs svfs;
346e13a25dc6f76d3bc9d8decd40dc4782b02d2aPatrik Flykt
346e13a25dc6f76d3bc9d8decd40dc4782b02d2aPatrik Flykt if (fstatvfs(f->fd, &svfs) >= 0) {
346e13a25dc6f76d3bc9d8decd40dc4782b02d2aPatrik Flykt uint64_t available;
346e13a25dc6f76d3bc9d8decd40dc4782b02d2aPatrik Flykt
346e13a25dc6f76d3bc9d8decd40dc4782b02d2aPatrik Flykt available = LESS_BY((uint64_t) svfs.f_bfree * (uint64_t) svfs.f_bsize, f->metrics.keep_free);
346e13a25dc6f76d3bc9d8decd40dc4782b02d2aPatrik Flykt
346e13a25dc6f76d3bc9d8decd40dc4782b02d2aPatrik Flykt if (new_size - old_size > available)
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt return -E2BIG;
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt }
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt }
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt /* Increase by larger blocks at once */
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt new_size = ((new_size+FILE_SIZE_INCREASE-1) / FILE_SIZE_INCREASE) * FILE_SIZE_INCREASE;
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt if (f->metrics.max_size > 0 && new_size > f->metrics.max_size)
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt new_size = f->metrics.max_size;
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt /* Note that the glibc fallocate() fallback is very
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt inefficient, hence we try to minimize the allocation area
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt as we can. */
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt r = posix_fallocate(f->fd, old_size, new_size - old_size);
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt if (r != 0)
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt return -r;
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt f->header->arena_size = htole64(new_size - le64toh(f->header->header_size));
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt return journal_file_fstat(f);
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt}
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flyktstatic unsigned type_to_context(ObjectType type) {
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt /* One context for each type, plus one catch-all for the rest */
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt assert_cc(_OBJECT_TYPE_MAX <= MMAP_CACHE_MAX_CONTEXTS);
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt assert_cc(CONTEXT_HEADER < MMAP_CACHE_MAX_CONTEXTS);
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt return type > OBJECT_UNUSED && type < _OBJECT_TYPE_MAX ? type : 0;
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt}
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flyktstatic int journal_file_move_to(JournalFile *f, ObjectType type, bool keep_always, uint64_t offset, uint64_t size, void **ret) {
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt int r;
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt assert(f);
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt assert(ret);
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt if (size <= 0)
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt return -EINVAL;
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt /* Avoid SIGBUS on invalid accesses */
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt if (offset + size > (uint64_t) f->last_stat.st_size) {
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt /* Hmm, out of range? Let's refresh the fstat() data
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt * first, before we trust that check. */
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt r = journal_file_fstat(f);
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt if (r < 0)
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt return r;
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt if (offset + size > (uint64_t) f->last_stat.st_size)
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt return -EADDRNOTAVAIL;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt }
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt return mmap_cache_get(f->mmap, f->fd, f->prot, type_to_context(type), keep_always, offset, size, &f->last_stat, ret);
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt}
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flyktstatic uint64_t minimum_header_size(Object *o) {
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt static const uint64_t table[] = {
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt [OBJECT_DATA] = sizeof(DataObject),
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt [OBJECT_FIELD] = sizeof(FieldObject),
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt [OBJECT_ENTRY] = sizeof(EntryObject),
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt [OBJECT_DATA_HASH_TABLE] = sizeof(HashTableObject),
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt [OBJECT_FIELD_HASH_TABLE] = sizeof(HashTableObject),
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt [OBJECT_ENTRY_ARRAY] = sizeof(EntryArrayObject),
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt [OBJECT_TAG] = sizeof(TagObject),
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt };
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt if (o->object.type >= ELEMENTSOF(table) || table[o->object.type] <= 0)
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt return sizeof(ObjectHeader);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt return table[o->object.type];
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt}
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flyktint journal_file_move_to_object(JournalFile *f, ObjectType type, uint64_t offset, Object **ret) {
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt int r;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt void *t;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt Object *o;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt uint64_t s;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt assert(f);
4b4923e65423e60d755841b5b264730e8f3deab3Tom Gundersen assert(ret);
5e91345094a9e983e7abb2313334e7808bcd2cc2Tom Gundersen
513a6fa8679510ea1b55967bdb482dd5f8a39f21Ronny Chevalier /* Objects may only be located at multiple of 64 bit */
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt if (!VALID64(offset))
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt return -EFAULT;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt r = journal_file_move_to(f, type, false, offset, sizeof(ObjectHeader), &t);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt if (r < 0)
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt return r;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt o = (Object*) t;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt s = le64toh(o->object.size);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt if (s < sizeof(ObjectHeader))
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt return -EBADMSG;
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt if (o->object.type <= OBJECT_UNUSED)
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt return -EBADMSG;
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt if (s < minimum_header_size(o))
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt return -EBADMSG;
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt if (type > OBJECT_UNUSED && o->object.type != type)
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt return -EBADMSG;
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt if (s > sizeof(ObjectHeader)) {
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt r = journal_file_move_to(f, type, false, offset, s, &t);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt if (r < 0)
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt return r;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt o = (Object*) t;
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt }
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt *ret = o;
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt return 0;
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt}
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flyktstatic uint64_t journal_file_entry_seqnum(JournalFile *f, uint64_t *seqnum) {
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt uint64_t r;
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt assert(f);
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt r = le64toh(f->header->tail_entry_seqnum) + 1;
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt if (seqnum) {
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt /* If an external seqnum counter was passed, we update
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt * both the local and the external one, and set it to
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt * the maximum of both */
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt if (*seqnum + 1 > r)
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt r = *seqnum + 1;
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt *seqnum = r;
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt }
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt f->header->tail_entry_seqnum = htole64(r);
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt if (f->header->head_entry_seqnum == 0)
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt f->header->head_entry_seqnum = htole64(r);
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt return r;
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt}
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flyktint journal_file_append_object(JournalFile *f, ObjectType type, uint64_t size, Object **ret, uint64_t *offset) {
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt int r;
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt uint64_t p;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt Object *tail, *o;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt void *t;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt assert(f);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt assert(type > OBJECT_UNUSED && type < _OBJECT_TYPE_MAX);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt assert(size >= sizeof(ObjectHeader));
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt assert(offset);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt assert(ret);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
fa94c34b083b5b4019975624453e53d0cbad2a5dTom Gundersen r = journal_file_set_online(f);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt if (r < 0)
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt return r;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
346e13a25dc6f76d3bc9d8decd40dc4782b02d2aPatrik Flykt p = le64toh(f->header->tail_object_offset);
346e13a25dc6f76d3bc9d8decd40dc4782b02d2aPatrik Flykt if (p == 0)
346e13a25dc6f76d3bc9d8decd40dc4782b02d2aPatrik Flykt p = le64toh(f->header->header_size);
346e13a25dc6f76d3bc9d8decd40dc4782b02d2aPatrik Flykt else {
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &tail);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt if (r < 0)
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt return r;
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt p += ALIGN64(le64toh(tail->object.size));
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt }
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt r = journal_file_allocate(f, p, size);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt if (r < 0)
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt return r;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt r = journal_file_move_to(f, type, false, p, size, &t);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt if (r < 0)
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt return r;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt o = (Object*) t;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt zero(o->object);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt o->object.type = type;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt o->object.size = htole64(size);
fa94c34b083b5b4019975624453e53d0cbad2a5dTom Gundersen
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt f->header->tail_object_offset = htole64(p);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt f->header->n_objects = htole64(le64toh(f->header->n_objects) + 1);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt *ret = o;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt *offset = p;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt return 0;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt}
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flyktstatic int journal_file_setup_data_hash_table(JournalFile *f) {
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt uint64_t s, p;
356779df90a2ecab5da2cb310ad0f8ebc9ca9f46Lennart Poettering Object *o;
9021bb9f935c93b516b10c88db2a212a9e3a8140Tom Gundersen int r;
9021bb9f935c93b516b10c88db2a212a9e3a8140Tom Gundersen
9021bb9f935c93b516b10c88db2a212a9e3a8140Tom Gundersen assert(f);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt /* We estimate that we need 1 hash table entry per 768 bytes
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt of journal file and we want to make sure we never get
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt beyond 75% fill level. Calculate the hash table size for
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt the maximum file size based on these metrics. */
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt s = (f->metrics.max_size * 4 / 768 / 3) * sizeof(HashItem);
fa94c34b083b5b4019975624453e53d0cbad2a5dTom Gundersen if (s < DEFAULT_DATA_HASH_TABLE_SIZE)
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt s = DEFAULT_DATA_HASH_TABLE_SIZE;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt log_debug("Reserving %"PRIu64" entries in hash table.", s / sizeof(HashItem));
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt r = journal_file_append_object(f,
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt OBJECT_DATA_HASH_TABLE,
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt offsetof(Object, hash_table.items) + s,
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt &o, &p);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt if (r < 0)
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt return r;
9021bb9f935c93b516b10c88db2a212a9e3a8140Tom Gundersen
356779df90a2ecab5da2cb310ad0f8ebc9ca9f46Lennart Poettering memzero(o->hash_table.items, s);
9021bb9f935c93b516b10c88db2a212a9e3a8140Tom Gundersen
9021bb9f935c93b516b10c88db2a212a9e3a8140Tom Gundersen f->header->data_hash_table_offset = htole64(p + offsetof(Object, hash_table.items));
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt f->header->data_hash_table_size = htole64(s);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt return 0;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt}
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flyktstatic int journal_file_setup_field_hash_table(JournalFile *f) {
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt uint64_t s, p;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt Object *o;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt int r;
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt
cfb5b3805759e63dc5e0cae6e92e1df885b5c5b6Tom Gundersen assert(f);
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt /* We use a fixed size hash table for the fields as this
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt * number should grow very slowly only */
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt s = DEFAULT_FIELD_HASH_TABLE_SIZE;
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt r = journal_file_append_object(f,
cfb5b3805759e63dc5e0cae6e92e1df885b5c5b6Tom Gundersen OBJECT_FIELD_HASH_TABLE,
cfb5b3805759e63dc5e0cae6e92e1df885b5c5b6Tom Gundersen offsetof(Object, hash_table.items) + s,
cfb5b3805759e63dc5e0cae6e92e1df885b5c5b6Tom Gundersen &o, &p);
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt if (r < 0)
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt return r;
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt memzero(o->hash_table.items, s);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt f->header->field_hash_table_offset = htole64(p + offsetof(Object, hash_table.items));
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt f->header->field_hash_table_size = htole64(s);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
44481a8b537839cd9ffead4d261491641f5b5260Zbigniew Jędrzejewski-Szmek return 0;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt}
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flyktint journal_file_map_data_hash_table(JournalFile *f) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt uint64_t s, p;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt void *t;
44481a8b537839cd9ffead4d261491641f5b5260Zbigniew Jędrzejewski-Szmek int r;
44481a8b537839cd9ffead4d261491641f5b5260Zbigniew Jędrzejewski-Szmek
44481a8b537839cd9ffead4d261491641f5b5260Zbigniew Jędrzejewski-Szmek assert(f);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (f->data_hash_table)
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return 0;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt p = le64toh(f->header->data_hash_table_offset);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt s = le64toh(f->header->data_hash_table_size);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt r = journal_file_move_to(f,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt OBJECT_DATA_HASH_TABLE,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt true,
66eac1201a9c1596f5901f8dbbf24bda7e350878Dan Williams p, s,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt &t);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (r < 0)
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return r;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt f->data_hash_table = t;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return 0;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt}
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flyktint journal_file_map_field_hash_table(JournalFile *f) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt uint64_t s, p;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt void *t;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt int r;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt assert(f);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (f->field_hash_table)
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return 0;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt p = le64toh(f->header->field_hash_table_offset);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt s = le64toh(f->header->field_hash_table_size);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt r = journal_file_move_to(f,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt OBJECT_FIELD_HASH_TABLE,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt true,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt p, s,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt &t);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (r < 0)
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return r;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt f->field_hash_table = t;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return 0;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt}
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flyktstatic int journal_file_link_field(
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt JournalFile *f,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt Object *o,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt uint64_t offset,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt uint64_t hash) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt uint64_t p, h, m;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt int r;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt assert(f);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt assert(o);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt assert(offset > 0);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (o->object.type != OBJECT_FIELD)
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return -EINVAL;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt m = le64toh(f->header->field_hash_table_size) / sizeof(HashItem);
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt if (m <= 0)
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt return -EBADMSG;
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt /* This might alter the window we are looking at */
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt o->field.next_hash_offset = o->field.head_data_offset = 0;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt h = hash % m;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt p = le64toh(f->field_hash_table[h].tail_hash_offset);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (p == 0)
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt f->field_hash_table[h].head_hash_offset = htole64(offset);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt else {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt r = journal_file_move_to_object(f, OBJECT_FIELD, p, &o);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (r < 0)
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return r;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt o->field.next_hash_offset = htole64(offset);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt }
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt f->field_hash_table[h].tail_hash_offset = htole64(offset);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (JOURNAL_HEADER_CONTAINS(f->header, n_fields))
ed6ee21953dac9c78383da00bc4514ece6b75ab5Patrik Flykt f->header->n_fields = htole64(le64toh(f->header->n_fields) + 1);
ed6ee21953dac9c78383da00bc4514ece6b75ab5Patrik Flykt
ed6ee21953dac9c78383da00bc4514ece6b75ab5Patrik Flykt return 0;
ed6ee21953dac9c78383da00bc4514ece6b75ab5Patrik Flykt}
ed6ee21953dac9c78383da00bc4514ece6b75ab5Patrik Flykt
ed6ee21953dac9c78383da00bc4514ece6b75ab5Patrik Flyktstatic int journal_file_link_data(
ed6ee21953dac9c78383da00bc4514ece6b75ab5Patrik Flykt JournalFile *f,
7bd8e95d44977833d0de3fc4e893eb3bc84351d6Patrik Flykt Object *o,
7bd8e95d44977833d0de3fc4e893eb3bc84351d6Patrik Flykt uint64_t offset,
7bd8e95d44977833d0de3fc4e893eb3bc84351d6Patrik Flykt uint64_t hash) {
7bd8e95d44977833d0de3fc4e893eb3bc84351d6Patrik Flykt
7bd8e95d44977833d0de3fc4e893eb3bc84351d6Patrik Flykt uint64_t p, h, m;
7bd8e95d44977833d0de3fc4e893eb3bc84351d6Patrik Flykt int r;
7bd8e95d44977833d0de3fc4e893eb3bc84351d6Patrik Flykt
5da1b97f3c3d15521f2dcfbc18eccd6580122ebcPatrik Flykt assert(f);
5da1b97f3c3d15521f2dcfbc18eccd6580122ebcPatrik Flykt assert(o);
5da1b97f3c3d15521f2dcfbc18eccd6580122ebcPatrik Flykt assert(offset > 0);
5da1b97f3c3d15521f2dcfbc18eccd6580122ebcPatrik Flykt
5da1b97f3c3d15521f2dcfbc18eccd6580122ebcPatrik Flykt if (o->object.type != OBJECT_DATA)
5da1b97f3c3d15521f2dcfbc18eccd6580122ebcPatrik Flykt return -EINVAL;
5da1b97f3c3d15521f2dcfbc18eccd6580122ebcPatrik Flykt
5da1b97f3c3d15521f2dcfbc18eccd6580122ebcPatrik Flykt m = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
6599680e2d33597f0f11a99e1c3c957b42418568Patrik Flykt if (m <= 0)
6599680e2d33597f0f11a99e1c3c957b42418568Patrik Flykt return -EBADMSG;
6599680e2d33597f0f11a99e1c3c957b42418568Patrik Flykt
6599680e2d33597f0f11a99e1c3c957b42418568Patrik Flykt /* This might alter the window we are looking at */
6599680e2d33597f0f11a99e1c3c957b42418568Patrik Flykt o->data.next_hash_offset = o->data.next_field_offset = 0;
6599680e2d33597f0f11a99e1c3c957b42418568Patrik Flykt o->data.entry_offset = o->data.entry_array_offset = 0;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt o->data.n_entries = 0;
6599680e2d33597f0f11a99e1c3c957b42418568Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt h = hash % m;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt p = le64toh(f->data_hash_table[h].tail_hash_offset);
c47e8936a43ce546e8a74fa569e9fbfae6c64be7Patrik Flykt if (p == 0)
c47e8936a43ce546e8a74fa569e9fbfae6c64be7Patrik Flykt /* Only entry in the hash table is easy */
c47e8936a43ce546e8a74fa569e9fbfae6c64be7Patrik Flykt f->data_hash_table[h].head_hash_offset = htole64(offset);
c47e8936a43ce546e8a74fa569e9fbfae6c64be7Patrik Flykt else {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt /* Move back to the previous data object, to patch in
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt * pointer */
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (r < 0)
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt return r;
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt o->data.next_hash_offset = htole64(offset);
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt }
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt f->data_hash_table[h].tail_hash_offset = htole64(offset);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (JOURNAL_HEADER_CONTAINS(f->header, n_data))
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt f->header->n_data = htole64(le64toh(f->header->n_data) + 1);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt return 0;
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt}
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flyktint journal_file_find_field_object_with_hash(
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt JournalFile *f,
ed6ee21953dac9c78383da00bc4514ece6b75ab5Patrik Flykt const void *field, uint64_t size, uint64_t hash,
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt Object **ret, uint64_t *offset) {
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt
ed6ee21953dac9c78383da00bc4514ece6b75ab5Patrik Flykt uint64_t p, osize, h, m;
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt int r;
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt assert(f);
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt assert(field && size > 0);
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt /* If the field hash table is empty, we can't find anything */
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt if (le64toh(f->header->field_hash_table_size) <= 0)
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt return 0;
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt
ed6ee21953dac9c78383da00bc4514ece6b75ab5Patrik Flykt /* Map the field hash table, if it isn't mapped yet. */
ed6ee21953dac9c78383da00bc4514ece6b75ab5Patrik Flykt r = journal_file_map_field_hash_table(f);
ed6ee21953dac9c78383da00bc4514ece6b75ab5Patrik Flykt if (r < 0)
ed6ee21953dac9c78383da00bc4514ece6b75ab5Patrik Flykt return r;
ed6ee21953dac9c78383da00bc4514ece6b75ab5Patrik Flykt
ed6ee21953dac9c78383da00bc4514ece6b75ab5Patrik Flykt osize = offsetof(Object, field.payload) + size;
ed6ee21953dac9c78383da00bc4514ece6b75ab5Patrik Flykt
ed6ee21953dac9c78383da00bc4514ece6b75ab5Patrik Flykt m = le64toh(f->header->field_hash_table_size) / sizeof(HashItem);
ed6ee21953dac9c78383da00bc4514ece6b75ab5Patrik Flykt if (m <= 0)
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt return -EBADMSG;
5364f729ba9616cd9fdab8d5413fbc25a1af3a57Lennart Poettering
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt h = hash % m;
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt p = le64toh(f->field_hash_table[h].head_hash_offset);
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt
9d89d1ae71cb298218e35a69d6b70e2c94de5271Patrik Flykt while (p > 0) {
9d89d1ae71cb298218e35a69d6b70e2c94de5271Patrik Flykt Object *o;
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt r = journal_file_move_to_object(f, OBJECT_FIELD, p, &o);
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt if (r < 0)
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt return r;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (le64toh(o->field.hash) == hash &&
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt le64toh(o->object.size) == osize &&
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt memcmp(o->field.payload, field, size) == 0) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (ret)
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt *ret = o;
ed6ee21953dac9c78383da00bc4514ece6b75ab5Patrik Flykt if (offset)
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt *offset = p;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return 1;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt }
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt p = le64toh(o->field.next_hash_offset);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt }
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return 0;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt}
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flyktint journal_file_find_field_object(
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt JournalFile *f,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt const void *field, uint64_t size,
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt Object **ret, uint64_t *offset) {
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt uint64_t hash;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt assert(f);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt assert(field && size > 0);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt hash = hash64(field, size);
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt return journal_file_find_field_object_with_hash(f,
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt field, size, hash,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt ret, offset);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt}
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flyktint journal_file_find_data_object_with_hash(
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt JournalFile *f,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt const void *data, uint64_t size, uint64_t hash,
3f0c075f8ef3344da5a6bda524540201f9204e61Patrik Flykt Object **ret, uint64_t *offset) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt uint64_t p, osize, h, m;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt int r;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt assert(f);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt assert(data || size == 0);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt /* If there's no data hash table, then there's no entry. */
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (le64toh(f->header->data_hash_table_size) <= 0)
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return 0;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt /* Map the data hash table, if it isn't mapped yet. */
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt r = journal_file_map_data_hash_table(f);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (r < 0)
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return r;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt osize = offsetof(Object, data.payload) + size;
6ec60d20724d2a32e20d25ef75d2af178c242bc2Ronny Chevalier
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt m = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (m <= 0)
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return -EBADMSG;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt h = hash % m;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt p = le64toh(f->data_hash_table[h].head_hash_offset);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt while (p > 0) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt Object *o;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (r < 0)
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return r;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (le64toh(o->data.hash) != hash)
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt goto next;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (o->object.flags & OBJECT_COMPRESSION_MASK) {
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt#if defined(HAVE_XZ) || defined(HAVE_LZ4)
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt uint64_t l;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt size_t rsize = 0;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt l = le64toh(o->object.size);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (l <= offsetof(Object, data.payload))
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return -EBADMSG;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt l -= offsetof(Object, data.payload);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt r = decompress_blob(o->object.flags & OBJECT_COMPRESSION_MASK,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt o->data.payload, l, &f->compress_buffer, &f->compress_buffer_size, &rsize, 0);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt if (r < 0)
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return r;
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt if (rsize == size &&
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt memcmp(f->compress_buffer, data, size) == 0) {
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt if (ret)
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt *ret = o;
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt if (offset)
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt *offset = p;
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt return 1;
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt }
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt#else
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return -EPROTONOSUPPORT;
ed6ee21953dac9c78383da00bc4514ece6b75ab5Patrik Flykt#endif
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt } else if (le64toh(o->object.size) == osize &&
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt memcmp(o->data.payload, data, size) == 0) {
ed6ee21953dac9c78383da00bc4514ece6b75ab5Patrik Flykt
ed6ee21953dac9c78383da00bc4514ece6b75ab5Patrik Flykt if (ret)
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt *ret = o;
ed6ee21953dac9c78383da00bc4514ece6b75ab5Patrik Flykt
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt if (offset)
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt *offset = p;
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt return 1;
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt }
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt next:
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt p = le64toh(o->data.next_hash_offset);
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt }
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt return 0;
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt}
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flyktint journal_file_find_data_object(
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt JournalFile *f,
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt const void *data, uint64_t size,
3f0c075f8ef3344da5a6bda524540201f9204e61Patrik Flykt Object **ret, uint64_t *offset) {
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt uint64_t hash;
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt assert(f);
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt assert(data || size == 0);
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt hash = hash64(data, size);
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt return journal_file_find_data_object_with_hash(f,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt data, size, hash,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt ret, offset);
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt}
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flyktstatic int journal_file_append_field(
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt JournalFile *f,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt const void *field, uint64_t size,
631bbe71298ec892f77f44f94feb612646fe6853Patrik Flykt Object **ret, uint64_t *offset) {
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt uint64_t hash, p;
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt uint64_t osize;
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt Object *o;
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt int r;
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt assert(f);
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt assert(field && size > 0);
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt hash = hash64(field, size);
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt r = journal_file_find_field_object_with_hash(f, field, size, hash, &o, &p);
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt if (r < 0)
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt return r;
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt else if (r > 0) {
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt if (ret)
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt *ret = o;
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt if (offset)
38a03f06a7393d2721c23f23f0589d2f6d0904afLennart Poettering *offset = p;
38a03f06a7393d2721c23f23f0589d2f6d0904afLennart Poettering
38a03f06a7393d2721c23f23f0589d2f6d0904afLennart Poettering return 0;
346e13a25dc6f76d3bc9d8decd40dc4782b02d2aPatrik Flykt }
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt osize = offsetof(Object, field.payload) + size;
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt r = journal_file_append_object(f, OBJECT_FIELD, osize, &o, &p);
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt if (r < 0)
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt return r;
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt o->field.hash = htole64(hash);
9021bb9f935c93b516b10c88db2a212a9e3a8140Tom Gundersen memcpy(o->field.payload, field, size);
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt r = journal_file_link_field(f, o, p, hash);
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt if (r < 0)
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt return r;
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt /* The linking might have altered the window, so let's
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt * refresh our pointer */
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt r = journal_file_move_to_object(f, OBJECT_FIELD, p, &o);
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt if (r < 0)
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt return r;
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt#ifdef HAVE_GCRYPT
7246333cb803b03440d3bd0bdaa233564d09b5aePatrik Flykt r = journal_file_hmac_put_object(f, OBJECT_FIELD, o, p);
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt if (r < 0)
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt return r;
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt#endif
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt if (ret)
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt *ret = o;
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt if (offset)
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt *offset = p;
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt return 0;
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt}
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flyktstatic int journal_file_append_data(
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt JournalFile *f,
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt const void *data, uint64_t size,
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt Object **ret, uint64_t *offset) {
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt uint64_t hash, p;
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt uint64_t osize;
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt Object *o;
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt int r, compression = 0;
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt const void *eq;
fa94c34b083b5b4019975624453e53d0cbad2a5dTom Gundersen
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt assert(f);
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt assert(data || size == 0);
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt hash = hash64(data, size);
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt r = journal_file_find_data_object_with_hash(f, data, size, hash, &o, &p);
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt if (r < 0)
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt return r;
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt if (r > 0) {
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt
356779df90a2ecab5da2cb310ad0f8ebc9ca9f46Lennart Poettering if (ret)
9021bb9f935c93b516b10c88db2a212a9e3a8140Tom Gundersen *ret = o;
9021bb9f935c93b516b10c88db2a212a9e3a8140Tom Gundersen
9021bb9f935c93b516b10c88db2a212a9e3a8140Tom Gundersen if (offset)
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt *offset = p;
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt return 0;
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt }
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt osize = offsetof(Object, data.payload) + size;
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt r = journal_file_append_object(f, OBJECT_DATA, osize, &o, &p);
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt if (r < 0)
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt return r;
fa94c34b083b5b4019975624453e53d0cbad2a5dTom Gundersen
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt o->data.hash = htole64(hash);
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt#if defined(HAVE_XZ) || defined(HAVE_LZ4)
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt if (JOURNAL_FILE_COMPRESS(f) && size >= COMPRESSION_SIZE_THRESHOLD) {
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt size_t rsize = 0;
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt compression = compress_blob(data, size, o->data.payload, &rsize);
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt if (compression >= 0) {
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt o->object.size = htole64(offsetof(Object, data.payload) + rsize);
356779df90a2ecab5da2cb310ad0f8ebc9ca9f46Lennart Poettering o->object.flags |= compression;
9021bb9f935c93b516b10c88db2a212a9e3a8140Tom Gundersen
9021bb9f935c93b516b10c88db2a212a9e3a8140Tom Gundersen log_debug("Compressed data object %"PRIu64" -> %zu using %s",
9021bb9f935c93b516b10c88db2a212a9e3a8140Tom Gundersen size, rsize, object_compressed_to_string(compression));
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt } else
3dc34fcc97b41f8b7b019027225b121dfbb9871dPatrik Flykt /* Compression didn't work, we don't really care why, let's continue without compression */
a34b57c0d43b8bf819ccd4f62c314b41b625454dPatrik Flykt compression = 0;
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt }
a9aff3615b430f86bd0a824214d95f634efaf894Patrik Flykt#endif
c3e2adeaba8e043caed0ef139eeaea016bd152d0Patrik Flykt
346e13a25dc6f76d3bc9d8decd40dc4782b02d2aPatrik Flykt if (compression == 0 && size > 0)
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt memcpy(o->data.payload, data, size);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
fa94c34b083b5b4019975624453e53d0cbad2a5dTom Gundersen r = journal_file_link_data(f, o, p, hash);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt if (r < 0)
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt return r;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt /* The linking might have altered the window, so let's
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt * refresh our pointer */
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt if (r < 0)
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt return r;
d1b0afe3653b4316a6361d204169620726d468a0Patrik Flykt
356779df90a2ecab5da2cb310ad0f8ebc9ca9f46Lennart Poettering if (!data)
9021bb9f935c93b516b10c88db2a212a9e3a8140Tom Gundersen eq = NULL;
9021bb9f935c93b516b10c88db2a212a9e3a8140Tom Gundersen else
9021bb9f935c93b516b10c88db2a212a9e3a8140Tom Gundersen eq = memchr(data, '=', size);
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt if (eq && eq > data) {
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt Object *fo = NULL;
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt uint64_t fp;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt /* Create field object ... */
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt r = journal_file_append_field(f, data, (uint8_t*) eq - (uint8_t*) data, &fo, &fp);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (r < 0)
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt return r;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt /* ... and link it in. */
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt o->data.next_field_offset = fo->field.head_data_offset;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt fo->field.head_data_offset = le64toh(p);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt }
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt#ifdef HAVE_GCRYPT
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt r = journal_file_hmac_put_object(f, OBJECT_DATA, o, p);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (r < 0)
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt return r;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt#endif
c806ffb9592fa9a2b13a1f9f9be4c77cd5b211aaZbigniew Jędrzejewski-Szmek
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt if (ret)
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt *ret = o;
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt if (offset)
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt *offset = p;
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt return 0;
cc22955cfefb4bd6e7a135f1ec95fb5a07ba9ce3Thomas Haller}
cc22955cfefb4bd6e7a135f1ec95fb5a07ba9ce3Thomas Haller
cc22955cfefb4bd6e7a135f1ec95fb5a07ba9ce3Thomas Halleruint64_t journal_file_entry_n_items(Object *o) {
cc22955cfefb4bd6e7a135f1ec95fb5a07ba9ce3Thomas Haller assert(o);
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt if (o->object.type != OBJECT_ENTRY)
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt return 0;
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt return (le64toh(o->object.size) - offsetof(Object, entry.items)) / sizeof(EntryItem);
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt}
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flyktuint64_t journal_file_entry_array_n_items(Object *o) {
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt assert(o);
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt if (o->object.type != OBJECT_ENTRY_ARRAY)
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt return 0;
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt return (le64toh(o->object.size) - offsetof(Object, entry_array.items)) / sizeof(uint64_t);
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt}
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flyktuint64_t journal_file_hash_table_n_items(Object *o) {
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt assert(o);
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt if (o->object.type != OBJECT_DATA_HASH_TABLE &&
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt o->object.type != OBJECT_FIELD_HASH_TABLE)
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt return 0;
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt return (le64toh(o->object.size) - offsetof(Object, hash_table.items)) / sizeof(HashItem);
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt}
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flyktstatic int link_entry_into_array(JournalFile *f,
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt le64_t *first,
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt le64_t *idx,
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt uint64_t p) {
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt int r;
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt uint64_t n = 0, ap = 0, q, i, a, hidx;
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt Object *o;
bbfa43ca37df0718287c25a8e39ee7477ebf33f6Patrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt assert(f);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt assert(first);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt assert(idx);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt assert(p > 0);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt a = le64toh(*first);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt i = hidx = le64toh(*idx);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt while (a > 0) {
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (r < 0)
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt return r;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt n = journal_file_entry_array_n_items(o);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (i < n) {
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt o->entry_array.items[i] = htole64(p);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt *idx = htole64(hidx + 1);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt return 0;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt }
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt i -= n;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt ap = a;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt a = le64toh(o->entry_array.next_entry_array_offset);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt }
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (hidx > n)
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt n = (hidx+1) * 2;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt else
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt n = n * 2;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (n < 4)
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt n = 4;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt r = journal_file_append_object(f, OBJECT_ENTRY_ARRAY,
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt offsetof(Object, entry_array.items) + n * sizeof(uint64_t),
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt &o, &q);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (r < 0)
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt return r;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt#ifdef HAVE_GCRYPT
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt r = journal_file_hmac_put_object(f, OBJECT_ENTRY_ARRAY, o, q);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (r < 0)
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt return r;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt#endif
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt o->entry_array.items[i] = htole64(p);
f0c4b1c3fd827b429ba36aa45fd39e0a023cbf2cTom Gundersen
c806ffb9592fa9a2b13a1f9f9be4c77cd5b211aaZbigniew Jędrzejewski-Szmek if (ap == 0)
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt *first = htole64(q);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt else {
62e3d1aed512d68cab1fc9b509e813a1fa2b3790Lennart Poettering r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, ap, &o);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (r < 0)
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt return r;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt o->entry_array.next_entry_array_offset = htole64(q);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt }
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays))
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt f->header->n_entry_arrays = htole64(le64toh(f->header->n_entry_arrays) + 1);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt *idx = htole64(hidx + 1);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt return 0;
3f0c075f8ef3344da5a6bda524540201f9204e61Patrik Flykt}
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flyktstatic int link_entry_into_array_plus_one(JournalFile *f,
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt le64_t *extra,
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt le64_t *first,
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt le64_t *idx,
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt uint64_t p) {
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt int r;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt assert(f);
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt assert(extra);
f12abb48fc510b8b349c05e35ba048134debaf25Patrik Flykt assert(first);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt assert(idx);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt assert(p > 0);
c806ffb9592fa9a2b13a1f9f9be4c77cd5b211aaZbigniew Jędrzejewski-Szmek
c806ffb9592fa9a2b13a1f9f9be4c77cd5b211aaZbigniew Jędrzejewski-Szmek if (*idx == 0)
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt *extra = htole64(p);
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt else {
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt le64_t i;
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt i = htole64(le64toh(*idx) - 1);
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt r = link_entry_into_array(f, first, &i, p);
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt if (r < 0)
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt return r;
da6fe470e17fa02f3adedc779585caf8669252bdPatrik Flykt }
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt *idx = htole64(le64toh(*idx) + 1);
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt return 0;
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt}
139b011ab81ccea1d51f09e0261a1c390115c6ffPatrik Flykt
static int journal_file_link_entry_item(JournalFile *f, Object *o, uint64_t offset, uint64_t i) {
uint64_t p;
int r;
assert(f);
assert(o);
assert(offset > 0);
p = le64toh(o->entry.items[i].object_offset);
if (p == 0)
return -EINVAL;
r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
if (r < 0)
return r;
return link_entry_into_array_plus_one(f,
&o->data.entry_offset,
&o->data.entry_array_offset,
&o->data.n_entries,
offset);
}
static int journal_file_link_entry(JournalFile *f, Object *o, uint64_t offset) {
uint64_t n, i;
int r;
assert(f);
assert(o);
assert(offset > 0);
if (o->object.type != OBJECT_ENTRY)
return -EINVAL;
__sync_synchronize();
/* Link up the entry itself */
r = link_entry_into_array(f,
&f->header->entry_array_offset,
&f->header->n_entries,
offset);
if (r < 0)
return r;
/* log_debug("=> %s seqnr=%"PRIu64" n_entries=%"PRIu64, f->path, o->entry.seqnum, f->header->n_entries); */
if (f->header->head_entry_realtime == 0)
f->header->head_entry_realtime = o->entry.realtime;
f->header->tail_entry_realtime = o->entry.realtime;
f->header->tail_entry_monotonic = o->entry.monotonic;
f->tail_entry_monotonic_valid = true;
/* Link up the items */
n = journal_file_entry_n_items(o);
for (i = 0; i < n; i++) {
r = journal_file_link_entry_item(f, o, offset, i);
if (r < 0)
return r;
}
return 0;
}
static int journal_file_append_entry_internal(
JournalFile *f,
const dual_timestamp *ts,
uint64_t xor_hash,
const EntryItem items[], unsigned n_items,
uint64_t *seqnum,
Object **ret, uint64_t *offset) {
uint64_t np;
uint64_t osize;
Object *o;
int r;
assert(f);
assert(items || n_items == 0);
assert(ts);
osize = offsetof(Object, entry.items) + (n_items * sizeof(EntryItem));
r = journal_file_append_object(f, OBJECT_ENTRY, osize, &o, &np);
if (r < 0)
return r;
o->entry.seqnum = htole64(journal_file_entry_seqnum(f, seqnum));
memcpy(o->entry.items, items, n_items * sizeof(EntryItem));
o->entry.realtime = htole64(ts->realtime);
o->entry.monotonic = htole64(ts->monotonic);
o->entry.xor_hash = htole64(xor_hash);
o->entry.boot_id = f->header->boot_id;
#ifdef HAVE_GCRYPT
r = journal_file_hmac_put_object(f, OBJECT_ENTRY, o, np);
if (r < 0)
return r;
#endif
r = journal_file_link_entry(f, o, np);
if (r < 0)
return r;
if (ret)
*ret = o;
if (offset)
*offset = np;
return 0;
}
void journal_file_post_change(JournalFile *f) {
assert(f);
/* inotify() does not receive IN_MODIFY events from file
* accesses done via mmap(). After each access we hence
* trigger IN_MODIFY by truncating the journal file to its
* current size which triggers IN_MODIFY. */
__sync_synchronize();
if (ftruncate(f->fd, f->last_stat.st_size) < 0)
log_error_errno(errno, "Failed to truncate file to its own size: %m");
}
static int entry_item_cmp(const void *_a, const void *_b) {
const EntryItem *a = _a, *b = _b;
if (le64toh(a->object_offset) < le64toh(b->object_offset))
return -1;
if (le64toh(a->object_offset) > le64toh(b->object_offset))
return 1;
return 0;
}
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) {
unsigned i;
EntryItem *items;
int r;
uint64_t xor_hash = 0;
struct dual_timestamp _ts;
assert(f);
assert(iovec || n_iovec == 0);
if (!ts) {
dual_timestamp_get(&_ts);
ts = &_ts;
}
if (f->tail_entry_monotonic_valid &&
ts->monotonic < le64toh(f->header->tail_entry_monotonic))
return -EINVAL;
#ifdef HAVE_GCRYPT
r = journal_file_maybe_append_tag(f, ts->realtime);
if (r < 0)
return r;
#endif
/* alloca() can't take 0, hence let's allocate at least one */
items = alloca(sizeof(EntryItem) * MAX(1u, n_iovec));
for (i = 0; i < n_iovec; i++) {
uint64_t p;
Object *o;
r = journal_file_append_data(f, iovec[i].iov_base, iovec[i].iov_len, &o, &p);
if (r < 0)
return r;
xor_hash ^= le64toh(o->data.hash);
items[i].object_offset = htole64(p);
items[i].hash = o->data.hash;
}
/* Order by the position on disk, in order to improve seek
* times for rotating media. */
qsort_safe(items, n_iovec, sizeof(EntryItem), entry_item_cmp);
r = journal_file_append_entry_internal(f, ts, xor_hash, items, n_iovec, seqnum, ret, offset);
/* If the memory mapping triggered a SIGBUS then we return an
* IO error and ignore the error code passed down to us, since
* it is very likely just an effect of a nullified replacement
* mapping page */
if (mmap_cache_got_sigbus(f->mmap, f->fd))
r = -EIO;
journal_file_post_change(f);
return r;
}
typedef struct ChainCacheItem {
uint64_t first; /* the array at the beginning of the chain */
uint64_t array; /* the cached array */
uint64_t begin; /* the first item in the cached array */
uint64_t total; /* the total number of items in all arrays before this one in the chain */
uint64_t last_index; /* the last index we looked at, to optimize locality when bisecting */
} ChainCacheItem;
static void chain_cache_put(
OrderedHashmap *h,
ChainCacheItem *ci,
uint64_t first,
uint64_t array,
uint64_t begin,
uint64_t total,
uint64_t last_index) {
if (!ci) {
/* If the chain item to cache for this chain is the
* first one it's not worth caching anything */
if (array == first)
return;
if (ordered_hashmap_size(h) >= CHAIN_CACHE_MAX) {
ci = ordered_hashmap_steal_first(h);
assert(ci);
} else {
ci = new(ChainCacheItem, 1);
if (!ci)
return;
}
ci->first = first;
if (ordered_hashmap_put(h, &ci->first, ci) < 0) {
free(ci);
return;
}
} else
assert(ci->first == first);
ci->array = array;
ci->begin = begin;
ci->total = total;
ci->last_index = last_index;
}
static int generic_array_get(
JournalFile *f,
uint64_t first,
uint64_t i,
Object **ret, uint64_t *offset) {
Object *o;
uint64_t p = 0, a, t = 0;
int r;
ChainCacheItem *ci;
assert(f);
a = first;
/* Try the chain cache first */
ci = ordered_hashmap_get(f->chain_cache, &first);
if (ci && i > ci->total) {
a = ci->array;
i -= ci->total;
t = ci->total;
}
while (a > 0) {
uint64_t k;
r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
if (r < 0)
return r;
k = journal_file_entry_array_n_items(o);
if (i < k) {
p = le64toh(o->entry_array.items[i]);
goto found;
}
i -= k;
t += k;
a = le64toh(o->entry_array.next_entry_array_offset);
}
return 0;
found:
/* Let's cache this item for the next invocation */
chain_cache_put(f->chain_cache, ci, first, a, le64toh(o->entry_array.items[0]), t, i);
r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
if (r < 0)
return r;
if (ret)
*ret = o;
if (offset)
*offset = p;
return 1;
}
static int generic_array_get_plus_one(
JournalFile *f,
uint64_t extra,
uint64_t first,
uint64_t i,
Object **ret, uint64_t *offset) {
Object *o;
assert(f);
if (i == 0) {
int r;
r = journal_file_move_to_object(f, OBJECT_ENTRY, extra, &o);
if (r < 0)
return r;
if (ret)
*ret = o;
if (offset)
*offset = extra;
return 1;
}
return generic_array_get(f, first, i-1, ret, offset);
}
enum {
TEST_FOUND,
TEST_LEFT,
TEST_RIGHT
};
static int generic_array_bisect(
JournalFile *f,
uint64_t first,
uint64_t n,
uint64_t needle,
int (*test_object)(JournalFile *f, uint64_t p, uint64_t needle),
direction_t direction,
Object **ret,
uint64_t *offset,
uint64_t *idx) {
uint64_t a, p, t = 0, i = 0, last_p = 0, last_index = (uint64_t) -1;
bool subtract_one = false;
Object *o, *array = NULL;
int r;
ChainCacheItem *ci;
assert(f);
assert(test_object);
/* Start with the first array in the chain */
a = first;
ci = ordered_hashmap_get(f->chain_cache, &first);
if (ci && n > ci->total) {
/* Ah, we have iterated this bisection array chain
* previously! Let's see if we can skip ahead in the
* chain, as far as the last time. But we can't jump
* backwards in the chain, so let's check that
* first. */
r = test_object(f, ci->begin, needle);
if (r < 0)
return r;
if (r == TEST_LEFT) {
/* OK, what we are looking for is right of the
* begin of this EntryArray, so let's jump
* straight to previously cached array in the
* chain */
a = ci->array;
n -= ci->total;
t = ci->total;
last_index = ci->last_index;
}
}
while (a > 0) {
uint64_t left, right, k, lp;
r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &array);
if (r < 0)
return r;
k = journal_file_entry_array_n_items(array);
right = MIN(k, n);
if (right <= 0)
return 0;
i = right - 1;
lp = p = le64toh(array->entry_array.items[i]);
if (p <= 0)
return -EBADMSG;
r = test_object(f, p, needle);
if (r < 0)
return r;
if (r == TEST_FOUND)
r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT;
if (r == TEST_RIGHT) {
left = 0;
right -= 1;
if (last_index != (uint64_t) -1) {
assert(last_index <= right);
/* If we cached the last index we
* looked at, let's try to not to jump
* too wildly around and see if we can
* limit the range to look at early to
* the immediate neighbors of the last
* index we looked at. */
if (last_index > 0) {
uint64_t x = last_index - 1;
p = le64toh(array->entry_array.items[x]);
if (p <= 0)
return -EBADMSG;
r = test_object(f, p, needle);
if (r < 0)
return r;
if (r == TEST_FOUND)
r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT;
if (r == TEST_RIGHT)
right = x;
else
left = x + 1;
}
if (last_index < right) {
uint64_t y = last_index + 1;
p = le64toh(array->entry_array.items[y]);
if (p <= 0)
return -EBADMSG;
r = test_object(f, p, needle);
if (r < 0)
return r;
if (r == TEST_FOUND)
r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT;
if (r == TEST_RIGHT)
right = y;
else
left = y + 1;
}
}
for (;;) {
if (left == right) {
if (direction == DIRECTION_UP)
subtract_one = true;
i = left;
goto found;
}
assert(left < right);
i = (left + right) / 2;
p = le64toh(array->entry_array.items[i]);
if (p <= 0)
return -EBADMSG;
r = test_object(f, p, needle);
if (r < 0)
return r;
if (r == TEST_FOUND)
r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT;
if (r == TEST_RIGHT)
right = i;
else
left = i + 1;
}
}
if (k >= n) {
if (direction == DIRECTION_UP) {
i = n;
subtract_one = true;
goto found;
}
return 0;
}
last_p = lp;
n -= k;
t += k;
last_index = (uint64_t) -1;
a = le64toh(array->entry_array.next_entry_array_offset);
}
return 0;
found:
if (subtract_one && t == 0 && i == 0)
return 0;
/* Let's cache this item for the next invocation */
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);
if (subtract_one && i == 0)
p = last_p;
else if (subtract_one)
p = le64toh(array->entry_array.items[i-1]);
else
p = le64toh(array->entry_array.items[i]);
r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
if (r < 0)
return r;
if (ret)
*ret = o;
if (offset)
*offset = p;
if (idx)
*idx = t + i + (subtract_one ? -1 : 0);
return 1;
}
static int generic_array_bisect_plus_one(
JournalFile *f,
uint64_t extra,
uint64_t first,
uint64_t n,
uint64_t needle,
int (*test_object)(JournalFile *f, uint64_t p, uint64_t needle),
direction_t direction,
Object **ret,
uint64_t *offset,
uint64_t *idx) {
int r;
bool step_back = false;
Object *o;
assert(f);
assert(test_object);
if (n <= 0)
return 0;
/* This bisects the array in object 'first', but first checks
* an extra */
r = test_object(f, extra, needle);
if (r < 0)
return r;
if (r == TEST_FOUND)
r = direction == DIRECTION_DOWN ? TEST_RIGHT : TEST_LEFT;
/* if we are looking with DIRECTION_UP then we need to first
see if in the actual array there is a matching entry, and
return the last one of that. But if there isn't any we need
to return this one. Hence remember this, and return it
below. */
if (r == TEST_LEFT)
step_back = direction == DIRECTION_UP;
if (r == TEST_RIGHT) {
if (direction == DIRECTION_DOWN)
goto found;
else
return 0;
}
r = generic_array_bisect(f, first, n-1, needle, test_object, direction, ret, offset, idx);
if (r == 0 && step_back)
goto found;
if (r > 0 && idx)
(*idx) ++;
return r;
found:
r = journal_file_move_to_object(f, OBJECT_ENTRY, extra, &o);
if (r < 0)
return r;
if (ret)
*ret = o;
if (offset)
*offset = extra;
if (idx)
*idx = 0;
return 1;
}
_pure_ static int test_object_offset(JournalFile *f, uint64_t p, uint64_t needle) {
assert(f);
assert(p > 0);
if (p == needle)
return TEST_FOUND;
else if (p < needle)
return TEST_LEFT;
else
return TEST_RIGHT;
}
static int test_object_seqnum(JournalFile *f, uint64_t p, uint64_t needle) {
Object *o;
int r;
assert(f);
assert(p > 0);
r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
if (r < 0)
return r;
if (le64toh(o->entry.seqnum) == needle)
return TEST_FOUND;
else if (le64toh(o->entry.seqnum) < needle)
return TEST_LEFT;
else
return TEST_RIGHT;
}
int journal_file_move_to_entry_by_seqnum(
JournalFile *f,
uint64_t seqnum,
direction_t direction,
Object **ret,
uint64_t *offset) {
return generic_array_bisect(f,
le64toh(f->header->entry_array_offset),
le64toh(f->header->n_entries),
seqnum,
test_object_seqnum,
direction,
ret, offset, NULL);
}
static int test_object_realtime(JournalFile *f, uint64_t p, uint64_t needle) {
Object *o;
int r;
assert(f);
assert(p > 0);
r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
if (r < 0)
return r;
if (le64toh(o->entry.realtime) == needle)
return TEST_FOUND;
else if (le64toh(o->entry.realtime) < needle)
return TEST_LEFT;
else
return TEST_RIGHT;
}
int journal_file_move_to_entry_by_realtime(
JournalFile *f,
uint64_t realtime,
direction_t direction,
Object **ret,
uint64_t *offset) {
return generic_array_bisect(f,
le64toh(f->header->entry_array_offset),
le64toh(f->header->n_entries),
realtime,
test_object_realtime,
direction,
ret, offset, NULL);
}
static int test_object_monotonic(JournalFile *f, uint64_t p, uint64_t needle) {
Object *o;
int r;
assert(f);
assert(p > 0);
r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o);
if (r < 0)
return r;
if (le64toh(o->entry.monotonic) == needle)
return TEST_FOUND;
else if (le64toh(o->entry.monotonic) < needle)
return TEST_LEFT;
else
return TEST_RIGHT;
}
static int find_data_object_by_boot_id(
JournalFile *f,
sd_id128_t boot_id,
Object **o,
uint64_t *b) {
char t[sizeof("_BOOT_ID=")-1 + 32 + 1] = "_BOOT_ID=";
sd_id128_to_string(boot_id, t + 9);
return journal_file_find_data_object(f, t, sizeof(t) - 1, o, b);
}
int journal_file_move_to_entry_by_monotonic(
JournalFile *f,
sd_id128_t boot_id,
uint64_t monotonic,
direction_t direction,
Object **ret,
uint64_t *offset) {
Object *o;
int r;
assert(f);
r = find_data_object_by_boot_id(f, boot_id, &o, NULL);
if (r < 0)
return r;
if (r == 0)
return -ENOENT;
return generic_array_bisect_plus_one(f,
le64toh(o->data.entry_offset),
le64toh(o->data.entry_array_offset),
le64toh(o->data.n_entries),
monotonic,
test_object_monotonic,
direction,
ret, offset, NULL);
}
void journal_file_reset_location(JournalFile *f) {
f->location_type = LOCATION_HEAD;
f->current_offset = 0;
f->current_seqnum = 0;
f->current_realtime = 0;
f->current_monotonic = 0;
zero(f->current_boot_id);
f->current_xor_hash = 0;
}
void journal_file_save_location(JournalFile *f, Object *o, uint64_t offset) {
f->location_type = LOCATION_SEEK;
f->current_offset = offset;
f->current_seqnum = le64toh(o->entry.seqnum);
f->current_realtime = le64toh(o->entry.realtime);
f->current_monotonic = le64toh(o->entry.monotonic);
f->current_boot_id = o->entry.boot_id;
f->current_xor_hash = le64toh(o->entry.xor_hash);
}
int journal_file_compare_locations(JournalFile *af, JournalFile *bf) {
assert(af);
assert(bf);
assert(af->location_type == LOCATION_SEEK);
assert(bf->location_type == LOCATION_SEEK);
/* If contents and timestamps match, these entries are
* identical, even if the seqnum does not match */
if (sd_id128_equal(af->current_boot_id, bf->current_boot_id) &&
af->current_monotonic == bf->current_monotonic &&
af->current_realtime == bf->current_realtime &&
af->current_xor_hash == bf->current_xor_hash)
return 0;
if (sd_id128_equal(af->header->seqnum_id, bf->header->seqnum_id)) {
/* If this is from the same seqnum source, compare
* seqnums */
if (af->current_seqnum < bf->current_seqnum)
return -1;
if (af->current_seqnum > bf->current_seqnum)
return 1;
/* Wow! This is weird, different data but the same
* seqnums? Something is borked, but let's make the
* best of it and compare by time. */
}
if (sd_id128_equal(af->current_boot_id, bf->current_boot_id)) {
/* If the boot id matches, compare monotonic time */
if (af->current_monotonic < bf->current_monotonic)
return -1;
if (af->current_monotonic > bf->current_monotonic)
return 1;
}
/* Otherwise, compare UTC time */
if (af->current_realtime < bf->current_realtime)
return -1;
if (af->current_realtime > bf->current_realtime)
return 1;
/* Finally, compare by contents */
if (af->current_xor_hash < bf->current_xor_hash)
return -1;
if (af->current_xor_hash > bf->current_xor_hash)
return 1;
return 0;
}
int journal_file_next_entry(
JournalFile *f,
uint64_t p,
direction_t direction,
Object **ret, uint64_t *offset) {
uint64_t i, n, ofs;
int r;
assert(f);
n = le64toh(f->header->n_entries);
if (n <= 0)
return 0;
if (p == 0)
i = direction == DIRECTION_DOWN ? 0 : n - 1;
else {
r = generic_array_bisect(f,
le64toh(f->header->entry_array_offset),
le64toh(f->header->n_entries),
p,
test_object_offset,
DIRECTION_DOWN,
NULL, NULL,
&i);
if (r <= 0)
return r;
if (direction == DIRECTION_DOWN) {
if (i >= n - 1)
return 0;
i++;
} else {
if (i <= 0)
return 0;
i--;
}
}
/* And jump to it */
r = generic_array_get(f,
le64toh(f->header->entry_array_offset),
i,
ret, &ofs);
if (r <= 0)
return r;
if (p > 0 &&
(direction == DIRECTION_DOWN ? ofs <= p : ofs >= p)) {
log_debug("%s: entry array corrupted at entry %"PRIu64,
f->path, i);
return -EBADMSG;
}
if (offset)
*offset = ofs;
return 1;
}
int journal_file_next_entry_for_data(
JournalFile *f,
Object *o, uint64_t p,
uint64_t data_offset,
direction_t direction,
Object **ret, uint64_t *offset) {
uint64_t n, i;
int r;
Object *d;
assert(f);
assert(p > 0 || !o);
r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
if (r < 0)
return r;
n = le64toh(d->data.n_entries);
if (n <= 0)
return n;
if (!o)
i = direction == DIRECTION_DOWN ? 0 : n - 1;
else {
if (o->object.type != OBJECT_ENTRY)
return -EINVAL;
r = generic_array_bisect_plus_one(f,
le64toh(d->data.entry_offset),
le64toh(d->data.entry_array_offset),
le64toh(d->data.n_entries),
p,
test_object_offset,
DIRECTION_DOWN,
NULL, NULL,
&i);
if (r <= 0)
return r;
if (direction == DIRECTION_DOWN) {
if (i >= n - 1)
return 0;
i++;
} else {
if (i <= 0)
return 0;
i--;
}
}
return generic_array_get_plus_one(f,
le64toh(d->data.entry_offset),
le64toh(d->data.entry_array_offset),
i,
ret, offset);
}
int journal_file_move_to_entry_by_offset_for_data(
JournalFile *f,
uint64_t data_offset,
uint64_t p,
direction_t direction,
Object **ret, uint64_t *offset) {
int r;
Object *d;
assert(f);
r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
if (r < 0)
return r;
return generic_array_bisect_plus_one(f,
le64toh(d->data.entry_offset),
le64toh(d->data.entry_array_offset),
le64toh(d->data.n_entries),
p,
test_object_offset,
direction,
ret, offset, NULL);
}
int journal_file_move_to_entry_by_monotonic_for_data(
JournalFile *f,
uint64_t data_offset,
sd_id128_t boot_id,
uint64_t monotonic,
direction_t direction,
Object **ret, uint64_t *offset) {
Object *o, *d;
int r;
uint64_t b, z;
assert(f);
/* First, seek by time */
r = find_data_object_by_boot_id(f, boot_id, &o, &b);
if (r < 0)
return r;
if (r == 0)
return -ENOENT;
r = generic_array_bisect_plus_one(f,
le64toh(o->data.entry_offset),
le64toh(o->data.entry_array_offset),
le64toh(o->data.n_entries),
monotonic,
test_object_monotonic,
direction,
NULL, &z, NULL);
if (r <= 0)
return r;
/* And now, continue seeking until we find an entry that
* exists in both bisection arrays */
for (;;) {
Object *qo;
uint64_t p, q;
r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
if (r < 0)
return r;
r = generic_array_bisect_plus_one(f,
le64toh(d->data.entry_offset),
le64toh(d->data.entry_array_offset),
le64toh(d->data.n_entries),
z,
test_object_offset,
direction,
NULL, &p, NULL);
if (r <= 0)
return r;
r = journal_file_move_to_object(f, OBJECT_DATA, b, &o);
if (r < 0)
return r;
r = generic_array_bisect_plus_one(f,
le64toh(o->data.entry_offset),
le64toh(o->data.entry_array_offset),
le64toh(o->data.n_entries),
p,
test_object_offset,
direction,
&qo, &q, NULL);
if (r <= 0)
return r;
if (p == q) {
if (ret)
*ret = qo;
if (offset)
*offset = q;
return 1;
}
z = q;
}
}
int journal_file_move_to_entry_by_seqnum_for_data(
JournalFile *f,
uint64_t data_offset,
uint64_t seqnum,
direction_t direction,
Object **ret, uint64_t *offset) {
Object *d;
int r;
assert(f);
r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
if (r < 0)
return r;
return generic_array_bisect_plus_one(f,
le64toh(d->data.entry_offset),
le64toh(d->data.entry_array_offset),
le64toh(d->data.n_entries),
seqnum,
test_object_seqnum,
direction,
ret, offset, NULL);
}
int journal_file_move_to_entry_by_realtime_for_data(
JournalFile *f,
uint64_t data_offset,
uint64_t realtime,
direction_t direction,
Object **ret, uint64_t *offset) {
Object *d;
int r;
assert(f);
r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d);
if (r < 0)
return r;
return generic_array_bisect_plus_one(f,
le64toh(d->data.entry_offset),
le64toh(d->data.entry_array_offset),
le64toh(d->data.n_entries),
realtime,
test_object_realtime,
direction,
ret, offset, NULL);
}
void journal_file_dump(JournalFile *f) {
Object *o;
int r;
uint64_t p;
assert(f);
journal_file_print_header(f);
p = le64toh(f->header->header_size);
while (p != 0) {
r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &o);
if (r < 0)
goto fail;
switch (o->object.type) {
case OBJECT_UNUSED:
printf("Type: OBJECT_UNUSED\n");
break;
case OBJECT_DATA:
printf("Type: OBJECT_DATA\n");
break;
case OBJECT_FIELD:
printf("Type: OBJECT_FIELD\n");
break;
case OBJECT_ENTRY:
printf("Type: OBJECT_ENTRY seqnum=%"PRIu64" monotonic=%"PRIu64" realtime=%"PRIu64"\n",
le64toh(o->entry.seqnum),
le64toh(o->entry.monotonic),
le64toh(o->entry.realtime));
break;
case OBJECT_FIELD_HASH_TABLE:
printf("Type: OBJECT_FIELD_HASH_TABLE\n");
break;
case OBJECT_DATA_HASH_TABLE:
printf("Type: OBJECT_DATA_HASH_TABLE\n");
break;
case OBJECT_ENTRY_ARRAY:
printf("Type: OBJECT_ENTRY_ARRAY\n");
break;
case OBJECT_TAG:
printf("Type: OBJECT_TAG seqnum=%"PRIu64" epoch=%"PRIu64"\n",
le64toh(o->tag.seqnum),
le64toh(o->tag.epoch));
break;
default:
printf("Type: unknown (%i)\n", o->object.type);
break;
}
if (o->object.flags & OBJECT_COMPRESSION_MASK)
printf("Flags: %s\n",
object_compressed_to_string(o->object.flags & OBJECT_COMPRESSION_MASK));
if (p == le64toh(f->header->tail_object_offset))
p = 0;
else
p = p + ALIGN64(le64toh(o->object.size));
}
return;
fail:
log_error("File corrupt");
}
static const char* format_timestamp_safe(char *buf, size_t l, usec_t t) {
const char *x;
x = format_timestamp(buf, l, t);
if (x)
return x;
return " --- ";
}
void journal_file_print_header(JournalFile *f) {
char a[33], b[33], c[33], d[33];
char x[FORMAT_TIMESTAMP_MAX], y[FORMAT_TIMESTAMP_MAX], z[FORMAT_TIMESTAMP_MAX];
struct stat st;
char bytes[FORMAT_BYTES_MAX];
assert(f);
printf("File Path: %s\n"
"File ID: %s\n"
"Machine ID: %s\n"
"Boot ID: %s\n"
"Sequential Number ID: %s\n"
"State: %s\n"
"Compatible Flags:%s%s\n"
"Incompatible Flags:%s%s%s\n"
"Header size: %"PRIu64"\n"
"Arena size: %"PRIu64"\n"
"Data Hash Table Size: %"PRIu64"\n"
"Field Hash Table Size: %"PRIu64"\n"
"Rotate Suggested: %s\n"
"Head Sequential Number: %"PRIu64"\n"
"Tail Sequential Number: %"PRIu64"\n"
"Head Realtime Timestamp: %s\n"
"Tail Realtime Timestamp: %s\n"
"Tail Monotonic Timestamp: %s\n"
"Objects: %"PRIu64"\n"
"Entry Objects: %"PRIu64"\n",
f->path,
sd_id128_to_string(f->header->file_id, a),
sd_id128_to_string(f->header->machine_id, b),
sd_id128_to_string(f->header->boot_id, c),
sd_id128_to_string(f->header->seqnum_id, d),
f->header->state == STATE_OFFLINE ? "OFFLINE" :
f->header->state == STATE_ONLINE ? "ONLINE" :
f->header->state == STATE_ARCHIVED ? "ARCHIVED" : "UNKNOWN",
JOURNAL_HEADER_SEALED(f->header) ? " SEALED" : "",
(le32toh(f->header->compatible_flags) & ~HEADER_COMPATIBLE_ANY) ? " ???" : "",
JOURNAL_HEADER_COMPRESSED_XZ(f->header) ? " COMPRESSED-XZ" : "",
JOURNAL_HEADER_COMPRESSED_LZ4(f->header) ? " COMPRESSED-LZ4" : "",
(le32toh(f->header->incompatible_flags) & ~HEADER_INCOMPATIBLE_ANY) ? " ???" : "",
le64toh(f->header->header_size),
le64toh(f->header->arena_size),
le64toh(f->header->data_hash_table_size) / sizeof(HashItem),
le64toh(f->header->field_hash_table_size) / sizeof(HashItem),
yes_no(journal_file_rotate_suggested(f, 0)),
le64toh(f->header->head_entry_seqnum),
le64toh(f->header->tail_entry_seqnum),
format_timestamp_safe(x, sizeof(x), le64toh(f->header->head_entry_realtime)),
format_timestamp_safe(y, sizeof(y), le64toh(f->header->tail_entry_realtime)),
format_timespan(z, sizeof(z), le64toh(f->header->tail_entry_monotonic), USEC_PER_MSEC),
le64toh(f->header->n_objects),
le64toh(f->header->n_entries));
if (JOURNAL_HEADER_CONTAINS(f->header, n_data))
printf("Data Objects: %"PRIu64"\n"
"Data Hash Table Fill: %.1f%%\n",
le64toh(f->header->n_data),
100.0 * (double) le64toh(f->header->n_data) / ((double) (le64toh(f->header->data_hash_table_size) / sizeof(HashItem))));
if (JOURNAL_HEADER_CONTAINS(f->header, n_fields))
printf("Field Objects: %"PRIu64"\n"
"Field Hash Table Fill: %.1f%%\n",
le64toh(f->header->n_fields),
100.0 * (double) le64toh(f->header->n_fields) / ((double) (le64toh(f->header->field_hash_table_size) / sizeof(HashItem))));
if (JOURNAL_HEADER_CONTAINS(f->header, n_tags))
printf("Tag Objects: %"PRIu64"\n",
le64toh(f->header->n_tags));
if (JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays))
printf("Entry Array Objects: %"PRIu64"\n",
le64toh(f->header->n_entry_arrays));
if (fstat(f->fd, &st) >= 0)
printf("Disk usage: %s\n", format_bytes(bytes, sizeof(bytes), (uint64_t) st.st_blocks * 512ULL));
}
static int journal_file_warn_btrfs(JournalFile *f) {
unsigned attrs;
int r;
assert(f);
/* Before we write anything, check if the COW logic is turned
* off on btrfs. Given our write pattern that is quite
* unfriendly to COW file systems this should greatly improve
* performance on COW file systems, such as btrfs, at the
* expense of data integrity features (which shouldn't be too
* bad, given that we do our own checksumming). */
r = btrfs_is_filesystem(f->fd);
if (r < 0)
return log_warning_errno(r, "Failed to determine if journal is on btrfs: %m");
if (!r)
return 0;
r = read_attr_fd(f->fd, &attrs);
if (r < 0)
return log_warning_errno(r, "Failed to read file attributes: %m");
if (attrs & FS_NOCOW_FL) {
log_debug("Detected btrfs file system with copy-on-write disabled, all is good.");
return 0;
}
log_notice("Creating journal file %s on a btrfs file system, and copy-on-write is enabled. "
"This is likely to slow down journal access substantially, please consider turning "
"off the copy-on-write file attribute on the journal directory, using chattr +C.", f->path);
return 1;
}
int journal_file_open(
const char *fname,
int flags,
mode_t mode,
bool compress,
bool seal,
JournalMetrics *metrics,
MMapCache *mmap_cache,
JournalFile *template,
JournalFile **ret) {
bool newly_created = false;
JournalFile *f;
void *h;
int r;
assert(fname);
assert(ret);
if ((flags & O_ACCMODE) != O_RDONLY &&
(flags & O_ACCMODE) != O_RDWR)
return -EINVAL;
if (!endswith(fname, ".journal") &&
!endswith(fname, ".journal~"))
return -EINVAL;
f = new0(JournalFile, 1);
if (!f)
return -ENOMEM;
f->fd = -1;
f->mode = mode;
f->flags = flags;
f->prot = prot_from_flags(flags);
f->writable = (flags & O_ACCMODE) != O_RDONLY;
#if defined(HAVE_LZ4)
f->compress_lz4 = compress;
#elif defined(HAVE_XZ)
f->compress_xz = compress;
#endif
#ifdef HAVE_GCRYPT
f->seal = seal;
#endif
if (mmap_cache)
f->mmap = mmap_cache_ref(mmap_cache);
else {
f->mmap = mmap_cache_new();
if (!f->mmap) {
r = -ENOMEM;
goto fail;
}
}
f->path = strdup(fname);
if (!f->path) {
r = -ENOMEM;
goto fail;
}
f->chain_cache = ordered_hashmap_new(&uint64_hash_ops);
if (!f->chain_cache) {
r = -ENOMEM;
goto fail;
}
f->fd = open(f->path, f->flags|O_CLOEXEC, f->mode);
if (f->fd < 0) {
r = -errno;
goto fail;
}
r = journal_file_fstat(f);
if (r < 0)
goto fail;
if (f->last_stat.st_size == 0 && f->writable) {
(void) journal_file_warn_btrfs(f);
/* Let's attach the creation time to the journal file,
* so that the vacuuming code knows the age of this
* file even if the file might end up corrupted one
* day... Ideally we'd just use the creation time many
* file systems maintain for each file, but there is
* currently no usable API to query this, hence let's
* emulate this via extended attributes. If extended
* attributes are not supported we'll just skip this,
* and rely solely on mtime/atime/ctime of the file. */
fd_setcrtime(f->fd, 0);
#ifdef HAVE_GCRYPT
/* Try to load the FSPRG state, and if we can't, then
* just don't do sealing */
if (f->seal) {
r = journal_file_fss_load(f);
if (r < 0)
f->seal = false;
}
#endif
r = journal_file_init_header(f, template);
if (r < 0)
goto fail;
r = journal_file_fstat(f);
if (r < 0)
goto fail;
newly_created = true;
}
if (f->last_stat.st_size < (off_t) HEADER_SIZE_MIN) {
r = -EIO;
goto fail;
}
r = mmap_cache_get(f->mmap, f->fd, f->prot, CONTEXT_HEADER, true, 0, PAGE_ALIGN(sizeof(Header)), &f->last_stat, &h);
if (r < 0)
goto fail;
f->header = h;
if (!newly_created) {
r = journal_file_verify_header(f);
if (r < 0)
goto fail;
}
#ifdef HAVE_GCRYPT
if (!newly_created && f->writable) {
r = journal_file_fss_load(f);
if (r < 0)
goto fail;
}
#endif
if (f->writable) {
if (metrics) {
journal_default_metrics(metrics, f->fd);
f->metrics = *metrics;
} else if (template)
f->metrics = template->metrics;
r = journal_file_refresh_header(f);
if (r < 0)
goto fail;
}
#ifdef HAVE_GCRYPT
r = journal_file_hmac_setup(f);
if (r < 0)
goto fail;
#endif
if (newly_created) {
r = journal_file_setup_field_hash_table(f);
if (r < 0)
goto fail;
r = journal_file_setup_data_hash_table(f);
if (r < 0)
goto fail;
#ifdef HAVE_GCRYPT
r = journal_file_append_first_tag(f);
if (r < 0)
goto fail;
#endif
}
if (mmap_cache_got_sigbus(f->mmap, f->fd)) {
r = -EIO;
goto fail;
}
*ret = f;
return 0;
fail:
if (f->fd >= 0 && mmap_cache_got_sigbus(f->mmap, f->fd))
r = -EIO;
journal_file_close(f);
return r;
}
int journal_file_rotate(JournalFile **f, bool compress, bool seal) {
_cleanup_free_ char *p = NULL;
size_t l;
JournalFile *old_file, *new_file = NULL;
int r;
assert(f);
assert(*f);
old_file = *f;
if (!old_file->writable)
return -EINVAL;
if (!endswith(old_file->path, ".journal"))
return -EINVAL;
l = strlen(old_file->path);
r = asprintf(&p, "%.*s@" SD_ID128_FORMAT_STR "-%016"PRIx64"-%016"PRIx64".journal",
(int) l - 8, old_file->path,
SD_ID128_FORMAT_VAL(old_file->header->seqnum_id),
le64toh((*f)->header->head_entry_seqnum),
le64toh((*f)->header->head_entry_realtime));
if (r < 0)
return -ENOMEM;
/* Try to rename the file to the archived version. If the file
* already was deleted, we'll get ENOENT, let's ignore that
* case. */
r = rename(old_file->path, p);
if (r < 0 && errno != ENOENT)
return -errno;
old_file->header->state = STATE_ARCHIVED;
/* Currently, btrfs is not very good with out write patterns
* and fragments heavily. Let's defrag our journal files when
* we archive them */
old_file->defrag_on_close = true;
r = journal_file_open(old_file->path, old_file->flags, old_file->mode, compress, seal, NULL, old_file->mmap, old_file, &new_file);
journal_file_close(old_file);
*f = new_file;
return r;
}
int journal_file_open_reliably(
const char *fname,
int flags,
mode_t mode,
bool compress,
bool seal,
JournalMetrics *metrics,
MMapCache *mmap_cache,
JournalFile *template,
JournalFile **ret) {
int r;
size_t l;
_cleanup_free_ char *p = NULL;
r = journal_file_open(fname, flags, mode, compress, seal, metrics, mmap_cache, template, ret);
if (!IN_SET(r,
-EBADMSG, /* corrupted */
-ENODATA, /* truncated */
-EHOSTDOWN, /* other machine */
-EPROTONOSUPPORT, /* incompatible feature */
-EBUSY, /* unclean shutdown */
-ESHUTDOWN, /* already archived */
-EIO, /* IO error, including SIGBUS on mmap */
-EIDRM /* File has been deleted */))
return r;
if ((flags & O_ACCMODE) == O_RDONLY)
return r;
if (!(flags & O_CREAT))
return r;
if (!endswith(fname, ".journal"))
return r;
/* The file is corrupted. Rotate it away and try it again (but only once) */
l = strlen(fname);
if (asprintf(&p, "%.*s@%016"PRIx64 "-%016"PRIx64 ".journal~",
(int) l - 8, fname,
now(CLOCK_REALTIME),
random_u64()) < 0)
return -ENOMEM;
if (rename(fname, p) < 0)
return -errno;
/* btrfs doesn't cope well with our write pattern and
* fragments heavily. Let's defrag all files we rotate */
(void) chattr_path(p, false, FS_NOCOW_FL);
(void) btrfs_defrag(p);
log_warning_errno(r, "File %s corrupted or uncleanly shut down, renaming and replacing.", fname);
return journal_file_open(fname, flags, mode, compress, seal, metrics, mmap_cache, template, ret);
}
int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint64_t p, uint64_t *seqnum, Object **ret, uint64_t *offset) {
uint64_t i, n;
uint64_t q, xor_hash = 0;
int r;
EntryItem *items;
dual_timestamp ts;
assert(from);
assert(to);
assert(o);
assert(p);
if (!to->writable)
return -EPERM;
ts.monotonic = le64toh(o->entry.monotonic);
ts.realtime = le64toh(o->entry.realtime);
n = journal_file_entry_n_items(o);
/* alloca() can't take 0, hence let's allocate at least one */
items = alloca(sizeof(EntryItem) * MAX(1u, n));
for (i = 0; i < n; i++) {
uint64_t l, h;
le64_t le_hash;
size_t t;
void *data;
Object *u;
q = le64toh(o->entry.items[i].object_offset);
le_hash = o->entry.items[i].hash;
r = journal_file_move_to_object(from, OBJECT_DATA, q, &o);
if (r < 0)
return r;
if (le_hash != o->data.hash)
return -EBADMSG;
l = le64toh(o->object.size) - offsetof(Object, data.payload);
t = (size_t) l;
/* We hit the limit on 32bit machines */
if ((uint64_t) t != l)
return -E2BIG;
if (o->object.flags & OBJECT_COMPRESSION_MASK) {
#if defined(HAVE_XZ) || defined(HAVE_LZ4)
size_t rsize = 0;
r = decompress_blob(o->object.flags & OBJECT_COMPRESSION_MASK,
o->data.payload, l, &from->compress_buffer, &from->compress_buffer_size, &rsize, 0);
if (r < 0)
return r;
data = from->compress_buffer;
l = rsize;
#else
return -EPROTONOSUPPORT;
#endif
} else
data = o->data.payload;
r = journal_file_append_data(to, data, l, &u, &h);
if (r < 0)
return r;
xor_hash ^= le64toh(u->data.hash);
items[i].object_offset = htole64(h);
items[i].hash = u->data.hash;
r = journal_file_move_to_object(from, OBJECT_ENTRY, p, &o);
if (r < 0)
return r;
}
r = journal_file_append_entry_internal(to, &ts, xor_hash, items, n, seqnum, ret, offset);
if (mmap_cache_got_sigbus(to->mmap, to->fd))
return -EIO;
return r;
}
void journal_reset_metrics(JournalMetrics *m) {
assert(m);
/* Set everything to "pick automatic values". */
*m = (JournalMetrics) {
.min_use = (uint64_t) -1,
.max_use = (uint64_t) -1,
.min_size = (uint64_t) -1,
.max_size = (uint64_t) -1,
.keep_free = (uint64_t) -1,
.n_max_files = (uint64_t) -1,
};
}
void journal_default_metrics(JournalMetrics *m, int fd) {
char a[FORMAT_BYTES_MAX], b[FORMAT_BYTES_MAX], c[FORMAT_BYTES_MAX], d[FORMAT_BYTES_MAX], e[FORMAT_BYTES_MAX];
struct statvfs ss;
uint64_t fs_size;
assert(m);
assert(fd >= 0);
if (fstatvfs(fd, &ss) >= 0)
fs_size = ss.f_frsize * ss.f_blocks;
else {
log_debug_errno(errno, "Failed to detremine disk size: %m");
fs_size = 0;
}
if (m->max_use == (uint64_t) -1) {
if (fs_size > 0) {
m->max_use = PAGE_ALIGN(fs_size / 10); /* 10% of file system size */
if (m->max_use > DEFAULT_MAX_USE_UPPER)
m->max_use = DEFAULT_MAX_USE_UPPER;
if (m->max_use < DEFAULT_MAX_USE_LOWER)
m->max_use = DEFAULT_MAX_USE_LOWER;
} else
m->max_use = DEFAULT_MAX_USE_LOWER;
} else {
m->max_use = PAGE_ALIGN(m->max_use);
if (m->max_use != 0 && m->max_use < JOURNAL_FILE_SIZE_MIN*2)
m->max_use = JOURNAL_FILE_SIZE_MIN*2;
}
if (m->min_use == (uint64_t) -1)
m->min_use = DEFAULT_MIN_USE;
if (m->min_use > m->max_use)
m->min_use = m->max_use;
if (m->max_size == (uint64_t) -1) {
m->max_size = PAGE_ALIGN(m->max_use / 8); /* 8 chunks */
if (m->max_size > DEFAULT_MAX_SIZE_UPPER)
m->max_size = DEFAULT_MAX_SIZE_UPPER;
} else
m->max_size = PAGE_ALIGN(m->max_size);
if (m->max_size != 0) {
if (m->max_size < JOURNAL_FILE_SIZE_MIN)
m->max_size = JOURNAL_FILE_SIZE_MIN;
if (m->max_use != 0 && m->max_size*2 > m->max_use)
m->max_use = m->max_size*2;
}
if (m->min_size == (uint64_t) -1)
m->min_size = JOURNAL_FILE_SIZE_MIN;
else {
m->min_size = PAGE_ALIGN(m->min_size);
if (m->min_size < JOURNAL_FILE_SIZE_MIN)
m->min_size = JOURNAL_FILE_SIZE_MIN;
if (m->max_size != 0 && m->min_size > m->max_size)
m->max_size = m->min_size;
}
if (m->keep_free == (uint64_t) -1) {
if (fs_size > 0) {
m->keep_free = PAGE_ALIGN(fs_size * 3 / 20); /* 15% of file system size */
if (m->keep_free > DEFAULT_KEEP_FREE_UPPER)
m->keep_free = DEFAULT_KEEP_FREE_UPPER;
} else
m->keep_free = DEFAULT_KEEP_FREE;
}
if (m->n_max_files == (uint64_t) -1)
m->n_max_files = DEFAULT_N_MAX_FILES;
log_debug("Fixed min_use=%s max_use=%s max_size=%s min_size=%s keep_free=%s n_max_files=%" PRIu64,
format_bytes(a, sizeof(a), m->min_use),
format_bytes(b, sizeof(b), m->max_use),
format_bytes(c, sizeof(c), m->max_size),
format_bytes(d, sizeof(d), m->min_size),
format_bytes(e, sizeof(e), m->keep_free),
m->n_max_files);
}
int journal_file_get_cutoff_realtime_usec(JournalFile *f, usec_t *from, usec_t *to) {
assert(f);
assert(from || to);
if (from) {
if (f->header->head_entry_realtime == 0)
return -ENOENT;
*from = le64toh(f->header->head_entry_realtime);
}
if (to) {
if (f->header->tail_entry_realtime == 0)
return -ENOENT;
*to = le64toh(f->header->tail_entry_realtime);
}
return 1;
}
int journal_file_get_cutoff_monotonic_usec(JournalFile *f, sd_id128_t boot_id, usec_t *from, usec_t *to) {
Object *o;
uint64_t p;
int r;
assert(f);
assert(from || to);
r = find_data_object_by_boot_id(f, boot_id, &o, &p);
if (r <= 0)
return r;
if (le64toh(o->data.n_entries) <= 0)
return 0;
if (from) {
r = journal_file_move_to_object(f, OBJECT_ENTRY, le64toh(o->data.entry_offset), &o);
if (r < 0)
return r;
*from = le64toh(o->entry.monotonic);
}
if (to) {
r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
if (r < 0)
return r;
r = generic_array_get_plus_one(f,
le64toh(o->data.entry_offset),
le64toh(o->data.entry_array_offset),
le64toh(o->data.n_entries)-1,
&o, NULL);
if (r <= 0)
return r;
*to = le64toh(o->entry.monotonic);
}
return 1;
}
bool journal_file_rotate_suggested(JournalFile *f, usec_t max_file_usec) {
assert(f);
/* If we gained new header fields we gained new features,
* hence suggest a rotation */
if (le64toh(f->header->header_size) < sizeof(Header)) {
log_debug("%s uses an outdated header, suggesting rotation.", f->path);
return true;
}
/* Let's check if the hash tables grew over a certain fill
* level (75%, borrowing this value from Java's hash table
* implementation), and if so suggest a rotation. To calculate
* the fill level we need the n_data field, which only exists
* in newer versions. */
if (JOURNAL_HEADER_CONTAINS(f->header, n_data))
if (le64toh(f->header->n_data) * 4ULL > (le64toh(f->header->data_hash_table_size) / sizeof(HashItem)) * 3ULL) {
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.",
f->path,
100.0 * (double) le64toh(f->header->n_data) / ((double) (le64toh(f->header->data_hash_table_size) / sizeof(HashItem))),
le64toh(f->header->n_data),
le64toh(f->header->data_hash_table_size) / sizeof(HashItem),
(unsigned long long) f->last_stat.st_size,
f->last_stat.st_size / le64toh(f->header->n_data));
return true;
}
if (JOURNAL_HEADER_CONTAINS(f->header, n_fields))
if (le64toh(f->header->n_fields) * 4ULL > (le64toh(f->header->field_hash_table_size) / sizeof(HashItem)) * 3ULL) {
log_debug("Field hash table of %s has a fill level at %.1f (%"PRIu64" of %"PRIu64" items), suggesting rotation.",
f->path,
100.0 * (double) le64toh(f->header->n_fields) / ((double) (le64toh(f->header->field_hash_table_size) / sizeof(HashItem))),
le64toh(f->header->n_fields),
le64toh(f->header->field_hash_table_size) / sizeof(HashItem));
return true;
}
/* Are the data objects properly indexed by field objects? */
if (JOURNAL_HEADER_CONTAINS(f->header, n_data) &&
JOURNAL_HEADER_CONTAINS(f->header, n_fields) &&
le64toh(f->header->n_data) > 0 &&
le64toh(f->header->n_fields) == 0)
return true;
if (max_file_usec > 0) {
usec_t t, h;
h = le64toh(f->header->head_entry_realtime);
t = now(CLOCK_REALTIME);
if (h > 0 && t > h + max_file_usec)
return true;
}
return false;
}