journal-file.c revision 23b0b2b2647d3f0f3fd0219c877d6233b4585421
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers This file is part of systemd.
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers Copyright 2011 Lennart Poettering
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers systemd is free software; you can redistribute it and/or modify it
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers under the terms of the GNU Lesser General Public License as published by
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers the Free Software Foundation; either version 2.1 of the License, or
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers (at your option) any later version.
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers systemd is distributed in the hope that it will be useful, but
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers WITHOUT ANY WARRANTY; without even the implied warranty of
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers Lesser General Public License for more details.
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers You should have received a copy of the GNU Lesser General Public License
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers along with systemd; If not, see <http://www.gnu.org/licenses/>.
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers#define DEFAULT_DATA_HASH_TABLE_SIZE (2047ULL*16ULL)
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers#define DEFAULT_FIELD_HASH_TABLE_SIZE (2047ULL*16ULL)
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers#define DEFAULT_WINDOW_SIZE (8ULL*1024ULL*1024ULL)
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers/* This is the minimum journal file size */
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers#define JOURNAL_FILE_SIZE_MIN (64ULL*1024ULL) /* 64 KiB */
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers/* These are the lower and upper bounds if we deduce the max_use value
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers * from the file system size */
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers#define DEFAULT_MAX_USE_LOWER (1ULL*1024ULL*1024ULL) /* 1 MiB */
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers#define DEFAULT_MAX_USE_UPPER (4ULL*1024ULL*1024ULL*1024ULL) /* 4 GiB */
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers/* This is the upper bound if we deduce max_size from max_use */
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers#define DEFAULT_MAX_SIZE_UPPER (128ULL*1024ULL*1024ULL) /* 128 MiB */
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers/* This is the upper bound if we deduce the keep_free value from the
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers * file system size */
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers#define DEFAULT_KEEP_FREE_UPPER (4ULL*1024ULL*1024ULL*1024ULL) /* 4 GiB */
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers/* This is the keep_free value when we can't determine the system
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sievers#define DEFAULT_KEEP_FREE (1024ULL*1024ULL) /* 1 MB */
c51d84dc09476d9c06b8aac726220bf3c7d62e8dKay Sieversstatic const char signature[] = { 'L', 'P', 'K', 'S', 'H', 'H', 'R', 'H' };
assert(f);
if (f->header) {
if (f->writable)
for (t = 0; t < _WINDOW_MAX; t++)
if (f->fd >= 0)
#ifdef HAVE_XZ
free(f);
Header h;
ssize_t k;
assert(f);
zero(h);
if (template) {
return -errno;
return -EIO;
assert(f);
f->tail_entry_monotonic_valid = true;
assert(f);
return -EBADMSG;
#ifdef HAVE_XZ
return -EPROTONOSUPPORT;
return -EPROTONOSUPPORT;
return -EBADMSG;
if ((uint64_t) f->last_stat.st_size < (le64toh(f->header->header_size) + le64toh(f->header->arena_size)))
return -ENODATA;
if (f->writable) {
return -EHOSTDOWN;
return -ESHUTDOWN;
assert(f);
old_size =
return -E2BIG;
available = 0;
return -E2BIG;
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)
le64_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);
uint64_t l, h;
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) {