journal-file.c revision 0071d9f1db6dc9a1035cf5afb81455b67ed462f6
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering This file is part of systemd.
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering Copyright 2011 Lennart Poettering
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering systemd is free software; you can redistribute it and/or modify it
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering under the terms of the GNU General Public License as published by
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering the Free Software Foundation; either version 2 of the License, or
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering (at your option) any later version.
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering systemd is distributed in the hope that it will be useful, but
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering General Public License for more details.
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering You should have received a copy of the GNU General Public License
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
31885cd5e38ec9807a6a7ab32660cf8c2fcf48f7Zbigniew Jędrzejewski-Szmek#define DEFAULT_DATA_HASH_TABLE_SIZE (2047ULL*16ULL)
b68fa010f72599e6da5822feda5ae3a47a4e63d8Simon Peeters#define DEFAULT_FIELD_HASH_TABLE_SIZE (2047ULL*16ULL)
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering#define DEFAULT_WINDOW_SIZE (128ULL*1024ULL*1024ULL)
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering#define COMPRESSION_SIZE_THRESHOLD (512ULL)
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering/* This is the minimum journal file size */
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering#define JOURNAL_FILE_SIZE_MIN (64ULL*1024ULL) /* 64 KiB */
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering/* These are the lower and upper bounds if we deduce the max_use value
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering * from the file system size */
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering#define DEFAULT_MAX_USE_LOWER (1ULL*1024ULL*1024ULL) /* 1 MiB */
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering#define DEFAULT_MAX_USE_UPPER (4ULL*1024ULL*1024ULL*1024ULL) /* 4 GiB */
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering/* This is the upper bound if we deduce max_size from max_use */
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering#define DEFAULT_MAX_SIZE_UPPER (128ULL*1024ULL*1024ULL) /* 128 MiB */
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering/* This is the upper bound if we deduce the keep_free value from the
5841bd803f1b651c0d70c6ae114630723a76d1daZbigniew Jędrzejewski-Szmek * file system size */
5841bd803f1b651c0d70c6ae114630723a76d1daZbigniew Jędrzejewski-Szmek#define DEFAULT_KEEP_FREE_UPPER (4ULL*1024ULL*1024ULL*1024ULL) /* 4 GiB */
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering/* This is the keep_free value when we can't determine the system
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering#define DEFAULT_KEEP_FREE (1024ULL*1024ULL) /* 1 MB */
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poetteringstatic const char signature[] = { 'L', 'P', 'K', 'S', 'H', 'H', 'R', 'H' };
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering for (t = 0; t < _WINDOW_MAX; t++)
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering munmap(f->windows[t].ptr, f->windows[t].size);
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poetteringstatic int journal_file_init_header(JournalFile *f, JournalFile *template) {
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering h.arena_offset = htole64(ALIGN64(sizeof(h)));
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering h.seqnum_id = template->header->seqnum_id;
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering if (k != sizeof(h))
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poetteringstatic int journal_file_refresh_header(JournalFile *f) {
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering r = sd_id128_get_machine(&f->header->machine_id);
aaf7eb81be912e7bed939f31e3bc4c631b2552b3Lennart Poettering if (sd_id128_equal(boot_id, f->header->boot_id))
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poetteringstatic int journal_file_verify_header(JournalFile *f) {
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering if ((le64toh(f->header->incompatible_flags) & ~HEADER_INCOMPATIBLE_COMPRESSED) != 0)
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering if ((uint64_t) f->last_stat.st_size < (le64toh(f->header->arena_offset) + le64toh(f->header->arena_size)))
df758e98754016119a9c8d49213a636a80ffab22Kay Sievers if (!sd_id128_equal(machine_id, f->header->machine_id))
ccd06097c79218f7d5ea4c21721bbcbc7c467dcaZbigniew Jędrzejewski-Szmek log_debug("Journal file %s is already online. Assuming unclean closing. Ignoring.", f->path);
0bee65f0622c4faa8ac8ae771cc0c8a936dfa284Lennart Poettering log_debug("Journal file %s has unknown state %u. Ignoring.", f->path, state);
0bee65f0622c4faa8ac8ae771cc0c8a936dfa284Lennart Poetteringstatic int journal_file_allocate(JournalFile *f, uint64_t offset, uint64_t size) {
0bee65f0622c4faa8ac8ae771cc0c8a936dfa284Lennart Poettering /* We assume that this file is not sparse, and we know that
0bee65f0622c4faa8ac8ae771cc0c8a936dfa284Lennart Poettering * for sure, since we always call posix_fallocate()
0bee65f0622c4faa8ac8ae771cc0c8a936dfa284Lennart Poettering * ourselves */
6301a98cdf26dc073f5317506c806bfa69f74cc8Lennart Poettering if (new_size < le64toh(f->header->arena_offset))
bd3fa1d2434aa28564251ac4da34d01537de8c4bLennart Poettering new_size = le64toh(f->header->arena_offset);
return -E2BIG;
return -errno;
return -errno;
static int journal_file_map(
JournalFile *f,
void **_window,
void **ret) {
void *window;
assert(f);
return -EADDRNOTAVAIL;
return -errno;
if (_window)
if (_woffset)
if (_wsize)
static int journal_file_move_to(JournalFile *f, int wt, uint64_t offset, uint64_t size, void **ret) {
void *p = NULL;
Window *w;
assert(f);
return -EADDRNOTAVAIL;
if (w->ptr) {
return -errno;
delta = 0;
if (size <= 0)
return -EADDRNOTAVAIL;
r = journal_file_map(f,
assert(o);
Object *o;
uint64_t s;
assert(f);
o = (Object*) t;
if (s < sizeof(ObjectHeader))
return -EBADMSG;
return -EBADMSG;
if (s > sizeof(ObjectHeader)) {
o = (Object*) t;
if (!verify_hash(o))
return -EBADMSG;
*ret = o;
uint64_t r;
assert(f);
if (seqnum) {
*seqnum = r;
static int journal_file_append_object(JournalFile *f, int type, uint64_t size, Object **ret, uint64_t *offset) {
uint64_t p;
assert(f);
o = (Object*) t;
*ret = o;
*offset = p;
uint64_t s, p;
Object *o;
assert(f);
r = journal_file_append_object(f,
uint64_t s, p;
Object *o;
assert(f);
r = journal_file_append_object(f,
uint64_t s, p;
assert(f);
r = journal_file_move_to(f,
f->data_hash_table = t;
uint64_t s, p;
assert(f);
r = journal_file_move_to(f,
f->field_hash_table = t;
uint64_t p, h;
assert(f);
assert(o);
JournalFile *f,
assert(f);
return -EBADMSG;
Object *o;
goto next;
#ifdef HAVE_XZ
return -EBADMSG;
return -EBADMSG;
if (ret)
*ret = o;
if (offset)
*offset = p;
return -EPROTONOSUPPORT;
if (ret)
*ret = o;
if (offset)
*offset = p;
next:
JournalFile *f,
assert(f);
return journal_file_find_data_object_with_hash(f,
static int journal_file_append_data(
JournalFile *f,
Object *o;
bool compressed = false;
assert(f);
if (ret)
*ret = o;
if (offset)
*offset = p;
#ifdef HAVE_XZ
if (f->compress &&
if (compressed) {
f->header->incompatible_flags = htole32(le32toh(f->header->incompatible_flags) | HEADER_INCOMPATIBLE_COMPRESSED);
if (!compressed)
if (ret)
*ret = o;
if (offset)
*offset = p;
assert(o);
assert(o);
uint64_t p) {
Object *o;
assert(f);
assert(p > 0);
n = journal_file_entry_array_n_items(o);
ap = a;
if (hidx > n)
if (ap == 0)
uint64_t p) {
assert(f);
assert(p > 0);
if (*idx == 0)
uint64_t i;
uint64_t p;
assert(f);
assert(o);
return -EINVAL;
return link_entry_into_array_plus_one(f,
offset);
uint64_t n, i;
assert(f);
assert(o);
r = link_entry_into_array(f,
offset);
/* log_debug("=> %s seqnr=%lu n_entries=%lu", f->path, (unsigned long) o->entry.seqnum, (unsigned long) f->header->n_entries); */
f->tail_entry_monotonic_valid = true;
n = journal_file_entry_n_items(o);
static int journal_file_append_entry_internal(
JournalFile *f,
Object *o;
assert(f);
if (ret)
*ret = o;
if (offset)
assert(f);
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) {
assert(f);
if (!f->writable)
return -EPERM;
if (!ts) {
if (f->tail_entry_monotonic_valid &&
return -EINVAL;
for (i = 0; i < n_iovec; i++) {
uint64_t p;
Object *o;
uint64_t i,
Object *o;
uint64_t p = 0, a;
assert(f);
a = first;
uint64_t n;
n = journal_file_entry_array_n_items(o);
if (ret)
*ret = o;
if (offset)
*offset = p;
uint64_t i,
Object *o;
assert(f);
if (ret)
*ret = o;
if (offset)
uint64_t n,
bool subtract_one = false;
assert(f);
a = first;
if (right <= 0)
return -EBADMSG;
if (r == TEST_FOUND)
if (r == TEST_RIGHT) {
left = 0;
subtract_one = true;
i = left;
goto found;
return -EBADMSG;
if (r == TEST_FOUND)
if (r == TEST_RIGHT)
right = i;
if (subtract_one && t == 0 && i == 0)
if (subtract_one && i == 0)
p = last_p;
else if (subtract_one)
if (ret)
*ret = o;
if (offset)
*offset = p;
if (idx)
uint64_t n,
assert(f);
else if (r == TEST_FOUND) {
Object *o;
if (ret)
*ret = o;
if (offset)
if (idx)
*idx = 0;
} else if (r == TEST_RIGHT)
(*idx) ++;
Object *o;
assert(f);
assert(p > 0);
return TEST_FOUND;
return TEST_LEFT;
return TEST_RIGHT;
JournalFile *f,
return generic_array_bisect(f,
Object *o;
assert(f);
assert(p > 0);
return TEST_FOUND;
return TEST_LEFT;
return TEST_RIGHT;
JournalFile *f,
return generic_array_bisect(f,
Object *o;
assert(f);
assert(p > 0);
return TEST_FOUND;
return TEST_LEFT;
return TEST_RIGHT;
JournalFile *f,
Object *o;
return -ENOENT;
return generic_array_bisect_plus_one(f,
assert(f);
assert(p > 0);
if (p == needle)
return TEST_FOUND;
else if (p < needle)
return TEST_LEFT;
return TEST_RIGHT;
JournalFile *f,
uint64_t i, n;
assert(f);
assert(p > 0 || !o);
return -EINVAL;
r = generic_array_bisect(f,
return generic_array_get(f,
JournalFile *f,
uint64_t i, n;
assert(f);
assert(o);
assert(p > 0);
return -EINVAL;
r = generic_array_bisect(f,
if (skip < 0) {
return -EBADMSG;
return generic_array_get(f,
JournalFile *f,
uint64_t n, i;
Object *d;
assert(f);
assert(p > 0 || !o);
return -EINVAL;
return generic_array_get_plus_one(f,
JournalFile *f,
Object *d;
return generic_array_bisect_plus_one(f,
JournalFile *f,
Object *d;
return generic_array_bisect_plus_one(f,
Object *o;
uint64_t p;
assert(f);
f->path,
goto fail;
case OBJECT_UNUSED:
case OBJECT_DATA:
case OBJECT_ENTRY:
case OBJECT_FIELD_HASH_TABLE:
case OBJECT_DATA_HASH_TABLE:
case OBJECT_ENTRY_ARRAY:
fail:
int journal_file_open(
const char *fname,
int flags,
JournalFile *f;
bool newly_created = false;
return -EINVAL;
return -EINVAL;
return -ENOMEM;
if (template) {
if (!f->path) {
r = -ENOMEM;
goto fail;
if (f->fd < 0) {
r = -errno;
goto fail;
r = -errno;
goto fail;
newly_created = true;
goto fail;
r = -errno;
goto fail;
r = -EIO;
goto fail;
r = -errno;
goto fail;
if (!newly_created) {
r = journal_file_verify_header(f);
goto fail;
if (f->writable) {
r = journal_file_refresh_header(f);
goto fail;
if (newly_created) {
goto fail;
goto fail;
goto fail;
r = journal_file_map_data_hash_table(f);
goto fail;
if (ret)
*ret = f;
fail:
size_t l;
assert(f);
assert(*f);
old_file = *f;
return -EINVAL;
return -EINVAL;
return -ENOMEM;
free(p);
return -errno;
*f = new_file;
const char *fname,
int flags,
size_t l;
random_ull()) < 0)
return -ENOMEM;
free(p);
return -errno;
struct vacuum_info {
char *filename;
bool have_seqnum;
const struct vacuum_info *a, *b;
a = _a;
b = _b;
DIR *d;
if (max_use <= 0)
return -errno;
size_t q;
bool have_seqnum;
goto finish;
if (!de)
r = -ENOMEM;
goto finish;
free(p);
free(p);
have_seqnum = true;
unsigned long long tmp;
r = -ENOMEM;
goto finish;
free(p);
have_seqnum = false;
struct vacuum_info *j;
free(p);
r = -ENOMEM;
goto finish;
list = j;
n_list ++;
for(i = 0; i < n_list; i++) {
r = -errno;
goto finish;
for (i = 0; i < n_list; i++)
closedir(d);
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;
assert(o);
assert(p);
return -EPERM;
return -EINVAL;
return -EINVAL;
n = journal_file_entry_n_items(o);
size_t t;
void *data;
Object *u;
return -EBADMSG;
t = (size_t) l;
if ((uint64_t) t != l)
return -E2BIG;
#ifdef HAVE_XZ
if (!uncompress_blob(o->data.payload, l, &from->compress_buffer, &from->compress_buffer_size, &rsize))
return -EBADMSG;
l = rsize;
return -EPROTONOSUPPORT;
assert(m);
if (fs_size > 0) {
if (fs_size > 0) {