diff --git a/Knot.files b/Knot.files index 8faa4dc8589f2fb38540d001adfbe54bdbf350ea..742fcd0d154c4f256b3caf13a779f4c33f985b26 100644 --- a/Knot.files +++ b/Knot.files @@ -258,10 +258,12 @@ src/knot/events/handlers/refresh.c src/knot/events/handlers/update.c src/knot/events/replan.c src/knot/events/replan.h -src/knot/journal/serialization.c -src/knot/journal/serialization.h src/knot/journal/journal.c src/knot/journal/journal.h +src/knot/journal/old_journal.c +src/knot/journal/old_journal.h +src/knot/journal/serialization.c +src/knot/journal/serialization.h src/knot/modules/dnsproxy/dnsproxy.c src/knot/modules/dnsproxy/dnsproxy.h src/knot/modules/dnstap/dnstap.c diff --git a/src/Makefile.am b/src/Makefile.am index 8a8adc303a8afef57a7c24e6088093a16cf9f8a2..00b1f98951d36ba171858587970481df7b14ad1e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -337,6 +337,8 @@ libknotd_la_SOURCES = \ knot/server/dthreads.h \ knot/journal/journal.c \ knot/journal/journal.h \ + knot/journal/old_journal.c \ + knot/journal/old_journal.h \ knot/journal/serialization.c \ knot/journal/serialization.h \ knot/server/server.c \ diff --git a/src/knot/conf/conf.c b/src/knot/conf/conf.c index c0e42472f3192c1712608bb0102efeeb892ce072..b5c6d57e9cd3a9b81e452c614890e66156b08527 100644 --- a/src/knot/conf/conf.c +++ b/src/knot/conf/conf.c @@ -1046,17 +1046,41 @@ char* conf_zonefile_txn( return get_filename(conf, txn, zone, file); } -char* conf_journalfile_txn( +char* conf_old_journalfile( conf_t *conf, - knot_db_txn_t *txn) + const knot_dname_t *zone) { - conf_val_t val; + if (zone == NULL) { + return NULL; + } - val = conf_default_get_txn(conf, txn, C_STORAGE); - char *storage = conf_abs_path(&val, NULL); + conf_val_t val = conf_zone_get(conf, C_JOURNAL, zone); + const char *journal = conf_str(&val); - val = conf_default_get_txn(conf, txn, C_JOURNAL); + // Use default journalfile name pattern if not specified. + if (journal == NULL) { + journal = "%s.db"; + } else { + CONF_LOG_ZONE(LOG_NOTICE, zone, "obsolete configuration 'journal', " + "use 'template.journal-db'' instead"); + } + val = conf_zone_get(conf, C_MAX_JOURNAL_SIZE, zone); + if (val.code == KNOT_EOK) { + CONF_LOG_ZONE(LOG_NOTICE, zone, "obsolete configuration 'max-journal-size', " + "use 'max-journal-usage' and 'template.journal-db' instead"); + } + + return get_filename(conf, &conf->read_txn, zone, journal); +} + +char* conf_journalfile_txn( + conf_t *conf, + knot_db_txn_t *txn) +{ + conf_val_t val = conf_default_get_txn(conf, txn, C_STORAGE); + char *storage = conf_abs_path(&val, NULL); + val = conf_default_get_txn(conf, txn, C_JOURNAL_DB); char *journaldir = conf_abs_path(&val, storage); free(storage); diff --git a/src/knot/conf/conf.h b/src/knot/conf/conf.h index cfdacad2a890dac453132d545b571932c066a5af..6cfa6cfa2f464a5c345c90ccaa028cb084881165 100644 --- a/src/knot/conf/conf.h +++ b/src/knot/conf/conf.h @@ -580,11 +580,11 @@ static inline char* conf_zonefile( * * \param[in] conf Configuration. * \param[in] txn Configuration DB transaction. - * \param[in] zone Zone name. * * \return Absolute journal file path string pointer. */ -char* conf_journalfile_txn(conf_t *conf, +char* conf_journalfile_txn( + conf_t *conf, knot_db_txn_t *txn); static inline char* conf_journalfile( conf_t *conf) @@ -592,6 +592,11 @@ static inline char* conf_journalfile( return conf_journalfile_txn(conf, &conf->read_txn); } +char* conf_old_journalfile( + conf_t *conf, + const knot_dname_t *zone +); + /*! * Gets the configured number of UDP threads. * diff --git a/src/knot/conf/scheme.c b/src/knot/conf/scheme.c index a6cc3fd65680d6418484b61acdc4ccb842553d06..7ae68fcd8d3dd12cd23f5f839dca85b04dacef52 100644 --- a/src/knot/conf/scheme.c +++ b/src/knot/conf/scheme.c @@ -45,11 +45,14 @@ #define HOURS(x) ((x) * 3600) #define DAYS(x) ((x) * HOURS(24)) -#define GIGA (1024LLU * 1024 * 1024) -#define TERA (1024 * GIGA) +#define KILO(x) (1024LLU * (x)) +#define MEGA(x) (KILO(1024) * (x)) +#define GIGA(x) (MEGA(1024) * (x)) +#define TERA(x) (GIGA(1024) * (x)) -#define VIRT_MEM_TOP_32BIT (2 * GIGA) -#define VIRT_MEM_LIMIT(x) (((sizeof(void *) < 8) && ((x) > VIRT_MEM_TOP_32BIT)) ? VIRT_MEM_TOP_32BIT : (x)) +#define VIRT_MEM_TOP_32BIT GIGA(2) +#define VIRT_MEM_LIMIT(x) (((sizeof(void *) < 8) && ((x) > VIRT_MEM_TOP_32BIT)) \ + ? VIRT_MEM_TOP_32BIT : (x)) #define FMOD (YP_FMULTI | CONF_IO_FRLD_MOD | CONF_IO_FRLD_ZONES) @@ -242,11 +245,9 @@ static const yp_item_t desc_remote[] = { { C_DISABLE_ANY, YP_TBOOL, YP_VNONE }, \ { C_ZONEFILE_SYNC, YP_TINT, YP_VINT = { -1, INT32_MAX, 0, YP_STIME } }, \ { C_IXFR_DIFF, YP_TBOOL, YP_VNONE }, \ - { C_MAX_ZONE_SIZE, YP_TINT, YP_VINT = { 0, INT64_MAX, INT64_MAX, YP_SSIZE }, \ - FLAGS }, \ - { C_MAX_JOURNAL_USAGE, YP_TINT, YP_VINT = { 40 * 1024, INT64_MAX, 100 * 1024 * 1024, \ - YP_SSIZE } }, \ - { C_MAX_JOURNAL_DEPTH, YP_TINT, YP_VINT = { 2, INT64_MAX, INT64_MAX, YP_SSIZE } }, \ + { C_MAX_ZONE_SIZE, YP_TINT, YP_VINT = { 0, INT64_MAX, INT64_MAX, YP_SSIZE }, FLAGS }, \ + { C_MAX_JOURNAL_USAGE, YP_TINT, YP_VINT = { KILO(40), INT64_MAX, MEGA(100), YP_SSIZE } }, \ + { C_MAX_JOURNAL_DEPTH, YP_TINT, YP_VINT = { 2, INT64_MAX, INT64_MAX } }, \ { C_KASP_DB, YP_TSTR, YP_VSTR = { "keys" }, FLAGS }, \ { C_DNSSEC_SIGNING, YP_TBOOL, YP_VNONE, FLAGS }, \ { C_DNSSEC_POLICY, YP_TREF, YP_VREF = { C_POLICY }, FLAGS, { check_ref_dflt } }, \ @@ -254,18 +255,21 @@ static const yp_item_t desc_remote[] = { { C_REQUEST_EDNS_OPTION, YP_TDATA, YP_VDATA = { 0, NULL, edns_opt_to_bin, edns_opt_to_txt } }, \ { C_MODULE, YP_TDATA, YP_VDATA = { 0, NULL, mod_id_to_bin, mod_id_to_txt }, \ YP_FMULTI | FLAGS, { check_modref } }, \ - { C_COMMENT, YP_TSTR, YP_VNONE }, + { C_COMMENT, YP_TSTR, YP_VNONE }, \ + /* Obsolete, old journal items. */ \ + { C_JOURNAL, YP_TSTR, YP_VNONE, FLAGS }, \ + { C_MAX_JOURNAL_SIZE, YP_TINT, YP_VINT = { 0, INT64_MAX, INT64_MAX, YP_SSIZE }, FLAGS }, \ static const yp_item_t desc_template[] = { { C_ID, YP_TSTR, YP_VNONE, CONF_IO_FREF }, ZONE_ITEMS(CONF_IO_FRLD_ZONES) - { C_TIMER_DB, YP_TSTR, YP_VSTR = { "timers" }, CONF_IO_FRLD_ZONES }, \ - { C_GLOBAL_MODULE, YP_TDATA, YP_VDATA = { 0, NULL, mod_id_to_bin, mod_id_to_txt }, \ - YP_FMULTI | CONF_IO_FRLD_MOD, { check_modref } }, \ - { C_JOURNAL, YP_TSTR, YP_VSTR = { "journal.db" }, CONF_IO_FRLD_SRV }, \ - { C_MAX_JOURNAL_SIZE, YP_TINT, YP_VINT = { 1024 * 1024, VIRT_MEM_LIMIT(100 * TERA), \ - VIRT_MEM_LIMIT(20 * GIGA), YP_SSIZE }, \ - CONF_IO_FRLD_SRV }, \ + { C_TIMER_DB, YP_TSTR, YP_VSTR = { "timers" }, CONF_IO_FRLD_ZONES }, + { C_GLOBAL_MODULE, YP_TDATA, YP_VDATA = { 0, NULL, mod_id_to_bin, mod_id_to_txt }, + YP_FMULTI | CONF_IO_FRLD_MOD, { check_modref } }, + { C_JOURNAL_DB, YP_TSTR, YP_VSTR = { "journal" }, CONF_IO_FRLD_SRV }, + { C_MAX_JOURNAL_DB_SIZE, YP_TINT, YP_VINT = { JOURNAL_MIN_FSLIMIT, VIRT_MEM_LIMIT(TERA(100)), + VIRT_MEM_LIMIT(GIGA(20)), YP_SSIZE }, + CONF_IO_FRLD_SRV }, { NULL } }; diff --git a/src/knot/conf/scheme.h b/src/knot/conf/scheme.h index 7deb2084d80cd41a2c74d6b09446f1dfea486beb..c81074efded3f4dcdd7ad0e44e4ca75ffeb8bf8c 100644 --- a/src/knot/conf/scheme.h +++ b/src/knot/conf/scheme.h @@ -53,7 +53,8 @@ #define C_IDENT "\x08""identity" #define C_INCL "\x07""include" #define C_IXFR_DIFF "\x15""ixfr-from-differences" -#define C_JOURNAL "\x07""journal" +#define C_JOURNAL "\x07""journal" /* obsolete, old journal compat */ +#define C_JOURNAL_DB "\x0A""journal-db" #define C_KASP_DB "\x07""kasp-db" #define C_KEY "\x03""key" #define C_KEYSTORE "\x08""keystore" @@ -62,7 +63,8 @@ #define C_LOG "\x03""log" #define C_MANUAL "\x06""manual" #define C_MASTER "\x06""master" -#define C_MAX_JOURNAL_SIZE "\x10""max-journal-size" +#define C_MAX_JOURNAL_SIZE "\x10""max-journal-size" /* obsolete, old journal compat */ +#define C_MAX_JOURNAL_DB_SIZE "\x13""max-journal-db-size" #define C_MAX_JOURNAL_USAGE "\x11""max-journal-usage" #define C_MAX_JOURNAL_DEPTH "\x11""max-journal-depth" #define C_MAX_TCP_CLIENTS "\x0F""max-tcp-clients" diff --git a/src/knot/conf/tools.c b/src/knot/conf/tools.c index fef40d85cfe7c2dfc0a216badd1f7be82b427c36..a3c88f4a660ee814ef605876b3b7f04cd99f7504 100644 --- a/src/knot/conf/tools.c +++ b/src/knot/conf/tools.c @@ -495,42 +495,19 @@ int check_template( return KNOT_EOK; } - // Check global-module. - conf_val_t g_module = conf_rawid_get_txn(args->conf, args->txn, C_TPL, - C_GLOBAL_MODULE, args->id, - args->id_len); - - if (g_module.code == KNOT_EOK) { - args->err_str = "global module in non-default template"; - return KNOT_EINVAL; - } - - // Check timer-db. - conf_val_t timer_db = conf_rawid_get_txn(args->conf, args->txn, C_TPL, - C_TIMER_DB, args->id, args->id_len); - - if (timer_db.code == KNOT_EOK) { - args->err_str = "timer database location in non-default template"; - return KNOT_EINVAL; - } - - // Check journal. - conf_val_t journal = conf_rawid_get_txn(args->conf, args->txn, C_TPL, - C_JOURNAL, args->id, args->id_len); - - if (journal.code == KNOT_EOK) { - args->err_str = "journal location in non-default template"; - return KNOT_EINVAL; - } - - // Check max-journal-size. - conf_val_t max_journal_size = conf_rawid_get_txn(args->conf, args->txn, C_TPL, - C_MAX_JOURNAL_SIZE, args->id, args->id_len); + conf_val_t val; + #define CHECK_DFLT(item, name) \ + val = conf_rawid_get_txn(args->conf, args->txn, C_TPL, item, \ + args->id, args->id_len); \ + if (val.code == KNOT_EOK) { \ + args->err_str = name " in non-default template"; \ + return KNOT_EINVAL; \ + } - if (max_journal_size.code == KNOT_EOK) { - args->err_str = "journal size in non-default template"; - return KNOT_EINVAL; - } + CHECK_DFLT(C_TIMER_DB, "timer database"); + CHECK_DFLT(C_GLOBAL_MODULE, "global module"); + CHECK_DFLT(C_JOURNAL_DB, "journal database path"); + CHECK_DFLT(C_MAX_JOURNAL_DB_SIZE, "journal database maximum size"); return KNOT_EOK; } diff --git a/src/knot/journal/old_journal.c b/src/knot/journal/old_journal.c new file mode 100644 index 0000000000000000000000000000000000000000..9fa273e2279a1ac1342d7fa6cfcd9a4a01cb3125 --- /dev/null +++ b/src/knot/journal/old_journal.c @@ -0,0 +1,479 @@ +/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <assert.h> + +#include "knot/common/log.h" +#include "contrib/files.h" +#include "knot/journal/old_journal.h" +#include "knot/journal/serialization.h" +#include "libknot/libknot.h" + +typedef enum { + JOURNAL_NULL = 0 << 0, /*!< Invalid journal entry. */ + JOURNAL_FREE = 1 << 0, /*!< Free journal entry. */ + JOURNAL_VALID = 1 << 1, /*!< Valid journal entry. */ + JOURNAL_DIRTY = 1 << 2 /*!< Journal entry cannot be evicted. */ +} journal_flag_t; + +typedef struct { + uint64_t id; /*!< Node ID. */ + uint16_t flags; /*!< Node flags. */ + uint16_t next; /*!< UNUSED */ + uint32_t pos; /*!< Position in journal file. */ + uint32_t len; /*!< Entry data length. */ +} journal_node_t; + +typedef struct { + int fd; + char *path; /*!< Path to journal file. */ + uint16_t tmark; /*!< Transaction start mark. */ + uint16_t max_nodes; /*!< Number of nodes. */ + uint16_t qhead; /*!< Node queue head. */ + uint16_t qtail; /*!< Node queue tail. */ + uint16_t bflags; /*!< Initial flags for each written node. */ + size_t fsize; /*!< Journal file size. */ + size_t fslimit; /*!< File size limit. */ + journal_node_t free; /*!< Free segment. */ + journal_node_t *nodes; /*!< Array of nodes. */ +} old_journal_t; + +#define JOURNAL_NCOUNT 1024 /*!< Default node count. */ +#define JOURNAL_MAGIC {'k', 'n', 'o', 't', '1', '5', '2'} +#define MAGIC_LENGTH 7 +/* HEADER = magic, crc, max_entries, qhead, qtail */ +#define JOURNAL_HSIZE (MAGIC_LENGTH + sizeof(uint32_t) + sizeof(uint16_t) * 3) + +/*! \brief Infinite file size limit. */ +#define FSLIMIT_INF (~((size_t)0)) + +/*! \brief Next node. */ +#define jnode_next(j, i) (((i) + 1) % (j)->max_nodes) + +/*! \brief Previous node. */ +#define jnode_prev(j, i) (((i) == 0) ? (j)->max_nodes - 1 : (i) - 1) + +/*! \bref Starting node data position. */ +#define jnode_base_pos(max_nodes) (JOURNAL_HSIZE + (max_nodes + 1) * sizeof(journal_node_t)) + +static inline int sfread(void *dst, size_t len, int fd) +{ + return read(fd, dst, len) == len; +} + +/*! \brief Return 'serial_from' part of the key. */ +static inline uint32_t journal_key_from(uint64_t k) +{ + return (uint32_t)(k & ((uint64_t)0x00000000ffffffff)); +} + +/*! \brief Compare function to match entries with starting serial. */ +static inline int journal_key_from_cmp(uint64_t k, uint64_t from) +{ + return ((uint64_t)journal_key_from(k)) - from; +} + +/*! \brief Open journal file for r/w (returns error if not exists). */ +static int old_journal_open_file(old_journal_t *j) +{ + assert(j != NULL); + + int ret = KNOT_EOK; + j->fd = open(j->path, O_RDWR); + if (j->fd < 0) { + return knot_map_errno(); + } + + /* File lock. */ + struct flock lock = { .l_type = F_WRLCK, .l_whence = SEEK_SET, + .l_start = 0, .l_len = 0, .l_pid = 0 }; + /* Attempt to lock. */ + ret = fcntl(j->fd, F_SETLKW, &lock); + if (ret < 0) { + return knot_map_errno(); + } + + /* Read magic bytes. */ + const char magic_req[MAGIC_LENGTH] = JOURNAL_MAGIC; + char magic[MAGIC_LENGTH]; + if (!sfread(magic, MAGIC_LENGTH, j->fd)) { + goto open_file_error; + } + if (memcmp(magic, magic_req, MAGIC_LENGTH) != 0) { + log_warning("old journal '%s', version too old", j->path); + close(j->fd); + j->fd = -1; + return KNOT_ENOTSUP; + } + + /* Skip CRC */ + if (lseek(j->fd, MAGIC_LENGTH + sizeof(uint32_t), SEEK_SET) < 0) { + goto open_file_error; + } + + /* Get journal file size. */ + struct stat st; + if (fstat(j->fd, &st) < 0) { + goto open_file_error; + } + + /* Set file size. */ + j->fsize = st.st_size; + + /* Read maximum number of entries. */ + if (!sfread(&j->max_nodes, sizeof(uint16_t), j->fd)) { + goto open_file_error; + } + + /* Allocate nodes. */ + const size_t node_len = sizeof(journal_node_t); + j->nodes = malloc(j->max_nodes * node_len); + if (j->nodes == NULL) { + goto open_file_error; + } else { + memset(j->nodes, 0, j->max_nodes * node_len); + } + + /* Load node queue state. */ + j->qhead = j->qtail = 0; + if (!sfread(&j->qhead, sizeof(uint16_t), j->fd)) { + goto open_file_error; + } + + /* Load queue tail. */ + if (!sfread(&j->qtail, sizeof(uint16_t), j->fd)) { + goto open_file_error; + } + + /* Load empty segment descriptor. */ + if (!sfread(&j->free, node_len, j->fd)) { + goto open_file_error; + } + + /* Read journal descriptors table. */ + if (!sfread(j->nodes, j->max_nodes * node_len, j->fd)) { + goto open_file_error; + } + + /* Save file lock and return. */ + return KNOT_EOK; + + /* Unlock and close file and return error. */ +open_file_error: + free(j->nodes); + j->nodes = NULL; + close(j->fd); + j->fd = -1; + return KNOT_ERROR; +} + +/*! \brief Close journal file. */ +static int old_journal_close_file(old_journal_t *journal) +{ + /* Close file. */ + if (journal->fd > 0) { + close(journal->fd); + journal->fd = -1; + } + + /* Free nodes. */ + free(journal->nodes); + journal->nodes = NULL; + + return KNOT_EOK; +} + +static int old_journal_close(old_journal_t *journal) +{ + /* Check journal. */ + if (journal == NULL) { + return KNOT_EINVAL; + } + + /* Close file. */ + old_journal_close_file(journal); + + /* Free allocated resources. */ + free(journal->path); + free(journal); + + return KNOT_EOK; +} + +static int old_journal_open(old_journal_t **journal, const char *path, size_t fslimit) +{ + if (journal == NULL || path == NULL) { + return KNOT_EINVAL; + } + + old_journal_t *j = malloc(sizeof(*j)); + if (j == NULL) { + return KNOT_ENOMEM; + } + + memset(j, 0, sizeof(*j)); + j->bflags = JOURNAL_DIRTY; + j->fd = -1; + + j->fslimit = fslimit; + + /* Copy path. */ + j->path = strdup(path); + if (j->path == NULL) { + free(j); + return KNOT_ENOMEM; + } + + /* Open journal file. */ + int ret = old_journal_open_file(j); + if (ret != KNOT_EOK) { + log_error("old journal '%s', failed to open (%s)", path, + knot_strerror(ret)); + old_journal_close(j); + return ret; + } + + *journal = j; + + return KNOT_EOK; +} + +typedef int (*journal_cmp_t)(uint64_t k1, uint64_t k2); + +static int old_journal_fetch(old_journal_t *journal, uint64_t id, + journal_cmp_t cf, journal_node_t** dst) +{ + if (journal == NULL || dst == NULL) { + return KNOT_EINVAL; + } + + size_t i = jnode_prev(journal, journal->qtail); + size_t endp = jnode_prev(journal, journal->qhead); + for(; i != endp; i = jnode_prev(journal, i)) { + journal_node_t *n = journal->nodes + i; + + /* Skip invalid nodes. */ + if (!(n->flags & JOURNAL_VALID)) { + continue; + } + + if (cf(n->id, id) == 0) { + *dst = journal->nodes + i; + return KNOT_EOK; + } + } + + return KNOT_ENOENT; +} + +static int old_journal_read_node(old_journal_t *journal, journal_node_t *n, char *dst) +{ + /* Check valid flag. */ + if (!(n->flags & JOURNAL_VALID)) { + return KNOT_EINVAL; + } + + /* Seek journal node. */ + int seek_ret = lseek(journal->fd, n->pos, SEEK_SET); + + /* Read journal node content. */ + if (seek_ret < 0 || !sfread(dst, n->len, journal->fd)) { + return KNOT_ERROR; + } + + return KNOT_EOK; +} + +bool old_journal_exists(const char *path) +{ + if (path == NULL) { + return false; + } + struct stat st; + return stat(path, &st) == 0; +} + +/*! \brief No doc here. Moved from zones.h (@mvavrusa) */ +static int changesets_unpack(changeset_t *chs) +{ + /* Read changeset flags. */ + if (chs->data == NULL) { + return KNOT_EMALF; + } + size_t remaining = chs->size; + + /* Read initial changeset RRSet - SOA. */ + uint8_t *stream = chs->data + (chs->size - remaining); + knot_rrset_t rrset; + int ret = rrset_deserialize(stream, &remaining, &rrset); + if (ret != KNOT_EOK) { + return KNOT_EMALF; + } + + assert(rrset.type == KNOT_RRTYPE_SOA); + chs->soa_from = knot_rrset_copy(&rrset, NULL); + knot_rrset_clear(&rrset, NULL); + if (chs->soa_from == NULL) { + return KNOT_ENOMEM; + } + + /* Read remaining RRSets */ + bool in_remove_section = true; + while (remaining > 0) { + + /* Parse next RRSet. */ + stream = chs->data + (chs->size - remaining); + knot_rrset_init_empty(&rrset); + ret = rrset_deserialize(stream, &remaining, &rrset); + if (ret != KNOT_EOK) { + return KNOT_EMALF; + } + + /* Check for next SOA. */ + if (rrset.type == KNOT_RRTYPE_SOA) { + /* Move to ADD section if in REMOVE. */ + if (in_remove_section) { + chs->soa_to = knot_rrset_copy(&rrset, NULL); + if (chs->soa_to == NULL) { + ret = KNOT_ENOMEM; + break; + } + in_remove_section = false; + } else { + /* Final SOA, no-op. */ + ; + } + } else { + /* Remove RRSets. */ + if (in_remove_section) { + ret = changeset_add_removal(chs, &rrset, 0); + } else { + /* Add RRSets. */ + ret = changeset_add_addition(chs, &rrset, 0); + } + } + knot_rrset_clear(&rrset, NULL); + if (ret != KNOT_EOK) { + break; + } + } + return ret; +} + +/*! \brief Helper for iterating journal (this is temporary until #80) */ +typedef int (*journal_apply_t)(old_journal_t *, journal_node_t *, const knot_dname_t *, list_t *); +static int old_journal_walk(const char *fn, uint32_t from, uint32_t to, + journal_apply_t cb, const knot_dname_t *zone, list_t *chgs) +{ + /* Open journal for reading. */ + old_journal_t *journal = NULL; + int ret = old_journal_open(&journal, fn, FSLIMIT_INF); + if (ret != KNOT_EOK) { + return ret; + } + /* Read entries from starting serial until finished. */ + uint32_t found_to = from; + journal_node_t *n = 0; + ret = old_journal_fetch(journal, from, journal_key_from_cmp, &n); + if (ret != KNOT_EOK) { + goto finish; + } + + size_t i = n - journal->nodes; + assert(i < journal->max_nodes); + + for (; i != journal->qtail; i = jnode_next(journal, i)) { + journal_node_t *n = journal->nodes + i; + + if (!(n->flags & JOURNAL_VALID)) { + continue; + } + if (to == found_to) { + break; + } + ret = cb(journal, n, zone, chgs); + if (ret != KNOT_EOK) { + break; + } + } + +finish: + old_journal_close(journal); + return ret; +} + +static int load_changeset(old_journal_t *journal, journal_node_t *n, + const knot_dname_t *zone, list_t *chgs) +{ + changeset_t *ch = changeset_new(zone); + if (ch == NULL) { + return KNOT_ENOMEM; + } + + /* Initialize changeset. */ + ch->data = malloc(n->len); + if (!ch->data) { + return KNOT_ENOMEM; + } + + /* Read journal entry. */ + int ret = old_journal_read_node(journal, n, (char*)ch->data); + if (ret != KNOT_EOK) { + return ret; + } + + /* Update changeset binary size. */ + ch->size = n->len; + + /* Insert into changeset list. */ + add_tail(chgs, &ch->n); + + return KNOT_EOK; +} + +int old_journal_load_changesets(const char *path, const knot_dname_t *zone, + list_t *dst, uint32_t from, uint32_t to) +{ + int ret = old_journal_walk(path, from, to, &load_changeset, zone, dst); + if (ret != KNOT_EOK) { + return ret; + } + + assert(dst != NULL); + + changeset_t* chs = NULL; + WALK_LIST(chs, *dst) { + ret = changesets_unpack(chs); + if (ret != KNOT_EOK) { + return ret; + } + } + + /* Check for complete history. */ + changeset_t *last = TAIL(*dst); + if (to != knot_soa_serial(&last->soa_to->rrs)) { + return KNOT_ERANGE; + } + + return KNOT_EOK; +} diff --git a/src/knot/journal/old_journal.h b/src/knot/journal/old_journal.h new file mode 100644 index 0000000000000000000000000000000000000000..57e07dda69c5d0f0194f71e75fa1932c490d4e89 --- /dev/null +++ b/src/knot/journal/old_journal.h @@ -0,0 +1,46 @@ +/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <stdint.h> + +#include "contrib/ucw/lists.h" + +/*! + * \brief Check if the journal file is used or not. + * + * \param path Journal file. + * + * \return true or false + */ +bool old_journal_exists(const char *path); + +/*! + * \brief Load changesets from journal. + * + * \param path Path to journal file. + * \param zone Corresponding zone. + * \param dst Store changesets here. + * \param from Start serial. + * \param to End serial. + * + * \retval KNOT_EOK on success. + * \retval KNOT_ERANGE if given entry was not found. + * \return < KNOT_EOK on error. + */ +int old_journal_load_changesets(const char *path, const knot_dname_t *zone, + list_t *dst, uint32_t from, uint32_t to); diff --git a/src/knot/server/server.c b/src/knot/server/server.c index 68f78c935a3424d7e4f1edec5afce0e1f1fc4720..3a768c756f1b4c2e5d6d3b25c7b3e931f1f1f427 100644 --- a/src/knot/server/server.c +++ b/src/knot/server/server.c @@ -380,7 +380,7 @@ int server_init(server_t *server, int bg_workers) } char * journal_dir = conf_journalfile(conf()); - conf_val_t journal_size = conf_default_get(conf(), C_MAX_JOURNAL_SIZE); + conf_val_t journal_size = conf_default_get(conf(), C_MAX_JOURNAL_DB_SIZE); int ret = init_journal_db(&server->journal_db, journal_dir, conf_int(&journal_size)); free(journal_dir); if (ret != KNOT_EOK) { diff --git a/src/knot/zone/zone-load.c b/src/knot/zone/zone-load.c index d384d983d830c3610da2a99751601ba98c18f83c..0c86935f7cd6b9883122c0bc6c7c7fe15c3882a1 100644 --- a/src/knot/zone/zone-load.c +++ b/src/knot/zone/zone-load.c @@ -16,6 +16,7 @@ #include "knot/common/log.h" #include "knot/journal/journal.h" +#include "knot/journal/old_journal.h" #include "knot/zone/zone-diff.h" #include "knot/zone/zone-load.h" #include "knot/zone/zonefile.h" @@ -90,6 +91,68 @@ int zone_load_check(conf_t *conf, zone_contents_t *contents) return KNOT_EOK; } +/*! + * \brief If old journal exists, warn the user and append the changes to chgs + * + * \todo Remove in the future together with journal/old_journal.[ch] and conf_old_journalfile() + */ +static void try_old_journal(conf_t *conf, zone_t *zone, uint32_t zone_c_serial, list_t *chgs) +{ + list_t old_chgs; + init_list(&old_chgs); + + // fetch old journal name + char *jfile = conf_old_journalfile(conf, zone->name); + if (jfile == NULL) { + return; + } + + if (!old_journal_exists(jfile)) { + goto toj_end; + } + log_zone_notice(zone->name, "journal, obsolete exists, file '%s'", jfile); + + // determine serial to load from + if (!EMPTY_LIST(*chgs)) { + changeset_t * lastch = TAIL(*chgs); + zone_c_serial = knot_soa_serial(&lastch->soa_to->rrs); + } + + // load changesets from old journal + int ret = old_journal_load_changesets(jfile, zone->name, &old_chgs, + zone_c_serial, zone_c_serial - 1); + if (ret != KNOT_ERANGE && ret != KNOT_ENOENT && ret != KNOT_EOK) { + log_zone_warning(zone->name, "journal, failed to load obsolete history (%s)", + knot_strerror(ret)); + goto toj_end; + } + + if (EMPTY_LIST(old_chgs)) { + goto toj_end; + } + log_zone_notice(zone->name, "journal, loaded obsolete history since serial '%u'", + zone_c_serial); + + // store them to new journal + ret = zone_changes_store(conf, zone, &old_chgs); + if (ret != KNOT_EOK) { + log_zone_warning(zone->name, "journal, failed to store obsolete history (%s)", + knot_strerror(ret)); + goto toj_end; + } + + // append them to chgs + changeset_t *ch, *nxt; + WALK_LIST_DELSAFE(ch, nxt, old_chgs) { + rem_node(&ch->n); + add_tail(chgs, &ch->n); + } + +toj_end: + changesets_free(&old_chgs); + free(jfile); +} + int zone_load_journal(conf_t *conf, zone_t *zone, zone_contents_t *contents) { if (conf == NULL || zone == NULL || contents == NULL) { @@ -104,19 +167,20 @@ int zone_load_journal(conf_t *conf, zone_t *zone, zone_contents_t *contents) /* Fetch SOA serial. */ uint32_t serial = zone_contents_serial(contents); - /*! \todo Check what should be the upper bound. */ + /* Load journal */ list_t chgs; init_list(&chgs); - int ret = zone_changes_load(conf, zone, &chgs, serial); - if (ret != KNOT_EOK) { + if (ret != KNOT_EOK && ret != KNOT_ENOENT) { changesets_free(&chgs); - /* Absence of records is not an error. */ - if (ret == KNOT_ENOENT) { - return KNOT_EOK; - } else { - return ret; - } + return ret; + } + + /* Load old journal (to be obsoleted) */ + try_old_journal(conf, zone, serial, &chgs); + + if (EMPTY_LIST(chgs)) { + return KNOT_EOK; } /* Apply changesets. */ diff --git a/tests-extra/tools/dnstest/server.py b/tests-extra/tools/dnstest/server.py index 1e12368fadee604bb0f8af50857c74ddf1a3fe7b..165e383a0f167e59c6121aadc74679161ea347f4 100644 --- a/tests-extra/tools/dnstest/server.py +++ b/tests-extra/tools/dnstest/server.py @@ -118,8 +118,8 @@ class Server(object): self.max_udp6_payload = None self.disable_any = None self.disable_notify = None - self.zonefile_sync = None - self.journal_size = 20 * 1024 * 1024 + self.zonefile_sync = "1d" + self.journal_db_size = 20 * 1024 * 1024 self.zone_size_limit = None self.inquirer = None @@ -1044,10 +1044,8 @@ class Knot(Server): s.id_item("id", "default") s.item_str("storage", self.dir) s.item_str("kasp-db", self.keydir) - if self.zonefile_sync: - s.item_str("zonefile-sync", self.zonefile_sync) - else: - s.item_str("zonefile-sync", "1d") + s.item_str("zonefile-sync", self.zonefile_sync) + s.item_str("max-journal-db-size", self.journal_db_size) s.item_str("semantic-checks", "on") if self.disable_any: s.item_str("disable-any", "on") @@ -1060,7 +1058,6 @@ class Knot(Server): s.item("global-module", "[%s]" % modules) if self.zone_size_limit: s.item("max-zone-size", self.zone_size_limit) - s.item_str("max-journal-usage", self.journal_size) s.end() s.begin("zone")