diff --git a/src/knot/catalog/interpret.c b/src/knot/catalog/interpret.c index 7d0ae343356f6913863d4838a51ce34dffe8269f..28e1cfdd3e2fbd3f78c91d9101468ee79cf0e440 100644 --- a/src/knot/catalog/interpret.c +++ b/src/knot/catalog/interpret.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* Copyright (C) 2022 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 @@ -18,7 +18,7 @@ #include <stdio.h> #include "knot/catalog/interpret.h" -#include "knot/zone/contents.h" +#include "knot/journal/serialization.h" struct cat_upd_ctx; typedef int (*cat_interpret_cb_t)(zone_node_t *node, struct cat_upd_ctx *ctx); @@ -28,6 +28,7 @@ typedef struct cat_upd_ctx { const zone_contents_t *complete_conts; int apex_labels; bool remove; + bool zone_diff; catalog_t *check; cat_interpret_cb_t member_cb; cat_interpret_cb_t property_cb; @@ -80,19 +81,19 @@ static int interpret_node(zone_node_t *node, void * _ctx) } } -static int interpret_zone(const zone_contents_t *zone, cat_upd_ctx_t *ctx) +static int interpret_zone(zone_diff_t *zdiff, cat_upd_ctx_t *ctx) { knot_dname_storage_t sub; if (knot_dname_store(sub, (uint8_t *)CATALOG_ZONES_LABEL) == 0 || - catalog_dname_append(sub, zone->apex->owner) == 0) { + catalog_dname_append(sub, zdiff->apex->owner) == 0) { return KNOT_EINVAL; } - if (zone_contents_find_node(zone, sub) == NULL) { + if (zone_tree_get(&zdiff->nodes, sub) == NULL) { return KNOT_EOK; } - return zone_tree_sub_apply(zone->nodes, sub, true, interpret_node, ctx); + return zone_tree_sub_apply(&zdiff->nodes, sub, true, interpret_node, ctx); } static const knot_dname_t *property_get_member(const zone_node_t *prop_node, @@ -127,6 +128,11 @@ static int cat_update_add_memb(zone_node_t *node, cat_upd_ctx_t *ctx) return KNOT_ERROR; } + const knot_rdataset_t *counter_ptr = node_rdataset(binode_counterpart(node), KNOT_RRTYPE_PTR); + if (knot_rdataset_subset(ptr, counter_ptr)) { + return KNOT_EOK; + } + knot_rdata_t *rdata = ptr->rdata; int ret = KNOT_EOK; for (int i = 0; ret == KNOT_EOK && i < ptr->count; i++) { @@ -158,6 +164,11 @@ static int cat_update_add_grp(zone_node_t *node, cat_upd_ctx_t *ctx) return KNOT_ERROR; } + const knot_rdataset_t *counter_txt = node_rdataset(binode_counterpart(node), KNOT_RRTYPE_TXT); + if (knot_rdataset_subset(txt, counter_txt)) { + return KNOT_EOK; + } + const char *newgr = ""; size_t grlen = 0; if (!ctx->remove) { @@ -175,15 +186,34 @@ static int cat_update_add_grp(zone_node_t *node, cat_upd_ctx_t *ctx) } int catalog_update_from_zone(catalog_update_t *u, struct zone_contents *zone, + const zone_diff_t *zone_diff, const struct zone_contents *complete_contents, bool remove, catalog_t *check, ssize_t *upd_count) { - cat_upd_ctx_t ctx = { u, complete_contents, knot_dname_labels(zone->apex->owner, NULL), - remove, check, cat_update_add_memb, cat_update_add_grp }; + int ret = KNOT_EOK; + zone_diff_t zdiff; + assert(zone == NULL || zone_diff == NULL); + if (zone != NULL) { + zone_diff_from_zone(&zdiff, zone); + } else { + zdiff = *zone_diff; + } + cat_upd_ctx_t ctx = { u, complete_contents, knot_dname_labels(zdiff.apex->owner, NULL), + remove, zone_diff != NULL, check, cat_update_add_memb, cat_update_add_grp }; pthread_mutex_lock(&u->mutex); *upd_count -= trie_weight(u->upd); - int ret = interpret_zone(zone, &ctx); + if (zone_diff != NULL) { + zone_diff_reverse(&zdiff); + ctx.remove = true; + ret = interpret_zone(&zdiff, &ctx); + zone_diff_reverse(&zdiff); + ctx.remove = false; + ctx.check = NULL; + } + if (ret == KNOT_EOK) { + ret = interpret_zone(&zdiff, &ctx); + } *upd_count += trie_weight(u->upd); pthread_mutex_unlock(&u->mutex); return ret; @@ -213,11 +243,14 @@ static int prop_verify(zone_node_t *node, cat_upd_ctx_t *ctx) int catalog_zone_verify(const struct zone_contents *zone) { cat_upd_ctx_t ctx = { NULL, zone, knot_dname_labels(zone->apex->owner, NULL), - false, NULL, member_verify, prop_verify }; + false, false, NULL, member_verify, prop_verify }; if (!check_zone_version(zone)) { return KNOT_EZONEINVAL; } - return interpret_zone(zone, &ctx); + zone_diff_t zdiff; + zone_diff_from_zone(&zdiff, zone); + + return interpret_zone(&zdiff, &ctx); } diff --git a/src/knot/catalog/interpret.h b/src/knot/catalog/interpret.h index 94a95fd71a9ed26677e3e5282a89b0d9b3ea8a28..20928b797b5a893ba74abf338ec2b36fa2128ad2 100644 --- a/src/knot/catalog/interpret.h +++ b/src/knot/catalog/interpret.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* Copyright (C) 2022 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 @@ -19,6 +19,7 @@ #include "knot/catalog/catalog_update.h" struct zone_contents; +struct zone_diff; /*! * \brief Validate if given zone is valid catalog. @@ -36,6 +37,7 @@ int catalog_zone_verify(const struct zone_contents *zone); * * \param u Catalog update to be updated. * \param zone Zone contents to be searched for member PTR records. + * \param zone_diff Zone diff to interpret for removals and additions. * \param complete_contents Complete zone contents (zone might be from a changeset). * \param remove Add removals of found member zones. * \param check Optional: existing catalog database to be checked for existence @@ -45,5 +47,6 @@ int catalog_zone_verify(const struct zone_contents *zone); * \return KNOT_E* */ int catalog_update_from_zone(catalog_update_t *u, struct zone_contents *zone, + const struct zone_diff *zone_diff, const struct zone_contents *complete_contents, bool remove, catalog_t *check, ssize_t *upd_count); diff --git a/src/knot/events/handlers/dnssec.c b/src/knot/events/handlers/dnssec.c index 67aecf6ad41c77cf5c4a6468bc4cc6bc42cf1ff8..4b2d9e3fc2806417629fff96d2e0e463271c3040 100644 --- a/src/knot/events/handlers/dnssec.c +++ b/src/knot/events/handlers/dnssec.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* Copyright (C) 2022 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 @@ -92,7 +92,7 @@ int event_dnssec(conf_t *conf, zone_t *zone) } zone_update_t up; - int ret = zone_update_init(&up, zone, UPDATE_INCREMENTAL); + int ret = zone_update_init(&up, zone, UPDATE_INCREMENTAL | UPDATE_NO_CHSET); if (ret != KNOT_EOK) { return ret; } diff --git a/src/knot/events/handlers/refresh.c b/src/knot/events/handlers/refresh.c index 063621ada281224bfcdabe3be90c0db5f0fdfcbe..0855b5c506b5cc2adb42e4bb2b83fe950cbf8e29 100644 --- a/src/knot/events/handlers/refresh.c +++ b/src/knot/events/handlers/refresh.c @@ -502,7 +502,7 @@ static int ixfr_finalize(struct refresh_data *data) } zone_update_t up = { 0 }; - int ret = zone_update_init(&up, data->zone, UPDATE_INCREMENTAL | UPDATE_STRICT); + int ret = zone_update_init(&up, data->zone, UPDATE_INCREMENTAL | UPDATE_STRICT | UPDATE_NO_CHSET); if (ret != KNOT_EOK) { data->fallback_axfr = false; data->fallback->remote = false; diff --git a/src/knot/events/handlers/update.c b/src/knot/events/handlers/update.c index 2b17375b0be04887c7187d6e4bcbebdc2bb84ed7..51d7940e01195bb8c54b7c50e519cc0fd08d5a38 100644 --- a/src/knot/events/handlers/update.c +++ b/src/knot/events/handlers/update.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* Copyright (C) 2022 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 @@ -138,7 +138,7 @@ static int process_normal(conf_t *conf, zone_t *zone, list_t *requests) // Init zone update structure zone_update_t up; - int ret = zone_update_init(&up, zone, UPDATE_INCREMENTAL); + int ret = zone_update_init(&up, zone, UPDATE_INCREMENTAL | UPDATE_NO_CHSET); if (ret != KNOT_EOK) { set_rcodes(requests, KNOT_RCODE_SERVFAIL); return ret; diff --git a/src/knot/journal/journal_basic.c b/src/knot/journal/journal_basic.c index 0285630ed34a7c59db55e1f2148e94dc61274bec..825130a243fff2f6074e971aa076d2f22eea968e 100644 --- a/src/knot/journal/journal_basic.c +++ b/src/knot/journal/journal_basic.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* Copyright (C) 2022 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 @@ -27,12 +27,12 @@ MDB_val journal_changeset_id_to_key(bool zone_in_journal, uint32_t serial, const } } -MDB_val journal_changeset_to_chunk_key(const changeset_t *ch, uint32_t chunk_id) +MDB_val journal_make_chunk_key(const knot_dname_t *apex, uint32_t ch_from, bool zij, uint32_t chunk_id) { - if (ch->soa_from == NULL) { - return knot_lmdb_make_key("NISI", ch->add->apex->owner, (uint32_t)0, "bootstrap", chunk_id); + if (zij) { + return knot_lmdb_make_key("NISI", apex, (uint32_t)0, "bootstrap", chunk_id); } else { - return knot_lmdb_make_key("NIII", ch->add->apex->owner, (uint32_t)0, changeset_from(ch), chunk_id); + return knot_lmdb_make_key("NIII", apex, (uint32_t)0, ch_from, chunk_id); } } diff --git a/src/knot/journal/journal_basic.h b/src/knot/journal/journal_basic.h index 9234de68079d64784b26574f1e269f425bdce655..8804d7bdf5cc61147a88c8778e3c9ace3d14fcf4 100644 --- a/src/knot/journal/journal_basic.h +++ b/src/knot/journal/journal_basic.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* Copyright (C) 2022 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 @@ -52,12 +52,14 @@ MDB_val journal_changeset_id_to_key(bool zone_in_journal, uint32_t serial, const /*! * \brief Create a database key for changeset chunk. * - * \param ch Corresponding changeset (perhaps to be stored). + * \param apex Zone apex owner name. + * \param ch_from Serial "from" of the stored changeset. + * \param zij Zone-in-journal is stored. * \param chunk_id Ordinal number of this changeset's chunk. * * \return DB key. 'mv_data' shall be freed later. 'mv_data' is NULL on failure. */ -MDB_val journal_changeset_to_chunk_key(const changeset_t *ch, uint32_t chunk_id); +MDB_val journal_make_chunk_key(const knot_dname_t *apex, uint32_t ch_from, bool zij, uint32_t chunk_id); /*! * \brief Return a key prefix to operate with all zone-related records. diff --git a/src/knot/journal/journal_write.c b/src/knot/journal/journal_write.c index af1e540981e0107d9942b0839d69f1694fdadb5c..ad1247bd7c84355c9cb74ff7dc4d85f9061e5860 100644 --- a/src/knot/journal/journal_write.c +++ b/src/knot/journal/journal_write.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* Copyright (C) 2022 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 @@ -22,7 +22,8 @@ #include "knot/journal/serialization.h" #include "libknot/error.h" -static void journal_write_serialize(knot_lmdb_txn_t *txn, serialize_ctx_t *ser, const changeset_t *ch, uint32_t ch_serial_to) +static void journal_write_serialize(knot_lmdb_txn_t *txn, serialize_ctx_t *ser, + const knot_dname_t *apex, bool zij, uint32_t ch_from, uint32_t ch_to) { MDB_val chunk; uint32_t i = 0; @@ -34,16 +35,18 @@ static void journal_write_serialize(knot_lmdb_txn_t *txn, serialize_ctx_t *ser, } chunk.mv_size += JOURNAL_HEADER_SIZE; chunk.mv_data = NULL; - MDB_val key = journal_changeset_to_chunk_key(ch, i); + MDB_val key = journal_make_chunk_key(apex, ch_from, zij, i); if (knot_lmdb_insert(txn, &key, &chunk)) { - journal_make_header(chunk.mv_data, ch_serial_to); + journal_make_header(chunk.mv_data, ch_to); serialize_chunk(ser, chunk.mv_data + JOURNAL_HEADER_SIZE, chunk.mv_size - JOURNAL_HEADER_SIZE); } free(key.mv_data); i++; } - serialize_deinit(ser); - // return value is in the txn + int ret = serialize_deinit(ser); + if (txn->ret == KNOT_EOK) { + txn->ret = ret; + } } void journal_write_changeset(knot_lmdb_txn_t *txn, const changeset_t *ch) @@ -53,7 +56,11 @@ void journal_write_changeset(knot_lmdb_txn_t *txn, const changeset_t *ch) txn->ret = KNOT_ENOMEM; return; } - journal_write_serialize(txn, ser, ch, changeset_to(ch)); + if (ch->remove == NULL) { + journal_write_serialize(txn, ser, ch->soa_to->owner, true, 0, changeset_to(ch)); + } else { + journal_write_serialize(txn, ser, ch->soa_to->owner, false, changeset_from(ch), changeset_to(ch)); + } } void journal_write_zone(knot_lmdb_txn_t *txn, const zone_contents_t *z) @@ -63,10 +70,17 @@ void journal_write_zone(knot_lmdb_txn_t *txn, const zone_contents_t *z) txn->ret = KNOT_ENOMEM; return; } - changeset_t fake_ch; - fake_ch.soa_from = NULL; - fake_ch.add = (zone_contents_t *)z; - journal_write_serialize(txn, ser, &fake_ch, zone_contents_serial(z)); + journal_write_serialize(txn, ser, z->apex->owner, true, 0, zone_contents_serial(z)); +} + +void journal_write_zone_diff(knot_lmdb_txn_t *txn, const zone_diff_t *z) +{ + serialize_ctx_t *ser = serialize_zone_diff_init(z); + if (ser == NULL) { + txn->ret = KNOT_ENOMEM; + return; + } + journal_write_serialize(txn, ser, z->apex->owner, false, zone_diff_from(z), zone_diff_to(z)); } static bool delete_one(knot_lmdb_txn_t *txn, bool del_zij, uint32_t del_serial, @@ -242,15 +256,22 @@ int journal_insert_zone(zone_journal_t j, const zone_contents_t *z) return txn.ret; } -int journal_insert(zone_journal_t j, const changeset_t *ch, const changeset_t *extra) +int journal_insert(zone_journal_t j, const changeset_t *ch, const changeset_t *extra, + const zone_diff_t *zdiff) { - size_t ch_size = changeset_serialized_size(ch); + assert(zdiff == NULL || (ch == NULL && extra == NULL)); + + size_t ch_size = zdiff == NULL ? changeset_serialized_size(ch) : + zone_diff_serialized_size(*zdiff); size_t max_usage = journal_conf_max_usage(j); if (ch_size >= max_usage) { return KNOT_ESPACE; } - if (extra != NULL && (changeset_to(extra) != changeset_to(ch) || - changeset_from(extra) == changeset_from(ch))) { + + uint32_t ch_from = zdiff == NULL ? changeset_from(ch) : zone_diff_from(zdiff); + uint32_t ch_to = zdiff == NULL ? changeset_to(ch) : zone_diff_to(zdiff); + if (extra != NULL && (changeset_to(extra) != ch_to || + changeset_from(extra) == ch_from)) { return KNOT_EINVAL; } int ret = knot_lmdb_open(j.db); @@ -280,7 +301,7 @@ int journal_insert(zone_journal_t j, const changeset_t *ch, const changeset_t *e journal_fix_occupation(j, &txn, &md, max_usage - ch_size, chs_limit - 1); // avoid discontinuity - if ((md.flags & JOURNAL_SERIAL_TO_VALID) && md.serial_to != changeset_from(ch)) { + if ((md.flags & JOURNAL_SERIAL_TO_VALID) && md.serial_to != ch_from) { if (journal_contains(&txn, true, 0, j.zone)) { txn.ret = KNOT_ESEMCHECK; } else { @@ -290,12 +311,16 @@ int journal_insert(zone_journal_t j, const changeset_t *ch, const changeset_t *e } // avoid cycle - if (journal_contains(&txn, false, changeset_to(ch), j.zone)) { + if (journal_contains(&txn, false, ch_to, j.zone)) { journal_fix_occupation(j, &txn, &md, INT64_MAX, 1); } - journal_write_changeset(&txn, ch); - journal_metadata_after_insert(&md, changeset_from(ch), changeset_to(ch)); + if (zdiff == NULL) { + journal_write_changeset(&txn, ch); + } else { + journal_write_zone_diff(&txn, zdiff); + } + journal_metadata_after_insert(&md, ch_from, ch_to); if (extra != NULL) { journal_write_changeset(&txn, extra); diff --git a/src/knot/journal/journal_write.h b/src/knot/journal/journal_write.h index ad280e46db2a54fdf97452473971cf5305e45e2a..a55fd341bdfe06efaa7340a145e73ae342ee9c0f 100644 --- a/src/knot/journal/journal_write.h +++ b/src/knot/journal/journal_write.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* Copyright (C) 2022 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 @@ -18,6 +18,7 @@ #include "knot/journal/journal_basic.h" #include "knot/journal/journal_metadata.h" +#include "knot/journal/serialization.h" /*! * \brief Serialize a changeset into chunks and write it into DB with no checks and metadata update. @@ -107,6 +108,7 @@ int journal_insert_zone(zone_journal_t j, const zone_contents_t *z); * \param j Zone journal. * \param ch Changeset to be stored. * \param extra Extra changeset to be stored in the role of merged changeset. + * \param zdiff Zone diff to be stored instead of changeset. * * \note The extra changesetis being stored on zone load, it is basically the diff * between zonefile and loaded zone contents. Afterwards, it will be treated @@ -115,4 +117,5 @@ int journal_insert_zone(zone_journal_t j, const zone_contents_t *z); * * \return KNOT_E* */ -int journal_insert(zone_journal_t j, const changeset_t *ch, const changeset_t *extra); +int journal_insert(zone_journal_t j, const changeset_t *ch, const changeset_t *extra, + const zone_diff_t *zdiff); diff --git a/src/knot/journal/serialization.c b/src/knot/journal/serialization.c index 5946152defd7a279943b3bff27c81b5e9fb6683d..575848116ecdb819a2d75fdac50a5beefce55f21 100644 --- a/src/knot/journal/serialization.c +++ b/src/knot/journal/serialization.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* Copyright (C) 2022 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 @@ -18,7 +18,6 @@ #include "knot/journal/serialization.h" #include "knot/zone/zone-tree.h" -#include "libknot/libknot.h" #define SERIALIZE_RRSET_INIT (-1) #define SERIALIZE_RRSET_DONE ((1L<<16)+1) @@ -37,10 +36,13 @@ typedef enum { #define RRSET_BUF_MAXSIZE 256 struct serialize_ctx { - const zone_contents_t *z; + zone_diff_t zdiff; zone_tree_it_t zit; zone_node_t *n; uint16_t node_pos; + bool zone_diff; + bool zone_diff_add; + int ret; const changeset_t *ch; changeset_iter_t it; @@ -48,6 +50,7 @@ struct serialize_ctx { long rrset_phase; knot_rrset_t rrset_buf[RRSET_BUF_MAXSIZE]; size_t rrset_buf_size; + list_t free_rdatasets; }; serialize_ctx_t *serialize_init(const changeset_t *ch) @@ -61,6 +64,7 @@ serialize_ctx_t *serialize_init(const changeset_t *ch) ctx->changeset_phase = ch->soa_from != NULL ? PHASE_SOA_1 : PHASE_SOA_2; ctx->rrset_phase = SERIALIZE_RRSET_INIT; ctx->rrset_buf_size = 0; + init_list(&ctx->free_rdatasets); return ctx; } @@ -72,10 +76,30 @@ serialize_ctx_t *serialize_zone_init(const zone_contents_t *z) return NULL; } - ctx->z = z; + zone_diff_from_zone(&ctx->zdiff, z); ctx->changeset_phase = PHASE_ZONE_SOA; ctx->rrset_phase = SERIALIZE_RRSET_INIT; ctx->rrset_buf_size = 0; + init_list(&ctx->free_rdatasets); + + return ctx; +} + +serialize_ctx_t *serialize_zone_diff_init(const zone_diff_t *z) +{ + serialize_ctx_t *ctx = calloc(1, sizeof(*ctx)); + if (ctx == NULL) { + return NULL; + } + + ctx->zone_diff = true; + ctx->zdiff = *z; + zone_diff_reverse(&ctx->zdiff); // start with removals of counterparts + + ctx->changeset_phase = PHASE_ZONE_SOA; + ctx->rrset_phase = SERIALIZE_RRSET_INIT; + ctx->rrset_buf_size = 0; + init_list(&ctx->free_rdatasets); return ctx; } @@ -86,19 +110,28 @@ static knot_rrset_t get_next_rrset(serialize_ctx_t *ctx) knot_rrset_init_empty(&res); switch (ctx->changeset_phase) { case PHASE_ZONE_SOA: - zone_tree_it_begin(ctx->z->nodes, &ctx->zit); + zone_tree_it_begin(&ctx->zdiff.nodes, &ctx->zit); ctx->changeset_phase = PHASE_ZONE_NODES; - return node_rrset(ctx->z->apex, KNOT_RRTYPE_SOA); + return node_rrset(ctx->zdiff.apex, KNOT_RRTYPE_SOA); case PHASE_ZONE_NODES: case PHASE_ZONE_NSEC3: while (ctx->n == NULL || ctx->node_pos >= ctx->n->rrset_count) { if (zone_tree_it_finished(&ctx->zit)) { zone_tree_it_free(&ctx->zit); - if (ctx->changeset_phase == PHASE_ZONE_NSEC3 || zone_tree_is_empty(ctx->z->nsec3_nodes)) { - ctx->changeset_phase = PHASE_END; - return res; + if (ctx->changeset_phase == PHASE_ZONE_NSEC3 || + zone_tree_is_empty(&ctx->zdiff.nsec3s)) { + if (ctx->zone_diff && !ctx->zone_diff_add) { + ctx->zone_diff_add = true; + zone_diff_reverse(&ctx->zdiff); + zone_tree_it_begin(&ctx->zdiff.nodes, &ctx->zit); + ctx->changeset_phase = PHASE_ZONE_NODES; + return node_rrset(ctx->zdiff.apex, KNOT_RRTYPE_SOA); + } else { + ctx->changeset_phase = PHASE_END; + return res; + } } else { - zone_tree_it_begin(ctx->z->nsec3_nodes, &ctx->zit); + zone_tree_it_begin(&ctx->zdiff.nsec3s, &ctx->zit); ctx->changeset_phase = PHASE_ZONE_NSEC3; } } @@ -107,9 +140,27 @@ static knot_rrset_t get_next_rrset(serialize_ctx_t *ctx) ctx->node_pos = 0; } res = node_rrset_at(ctx->n, ctx->node_pos++); - if (ctx->n == ctx->z->apex && res.type == KNOT_RRTYPE_SOA) { + if (ctx->n == ctx->zdiff.apex && res.type == KNOT_RRTYPE_SOA) { return get_next_rrset(ctx); } + if (ctx->zone_diff) { + knot_rrset_t counter_rr = node_rrset(binode_counterpart(ctx->n), res.type); + if (counter_rr.ttl == res.ttl && !knot_rrset_empty(&counter_rr)) { + if (knot_rdataset_subset(&res.rrs, &counter_rr.rrs)) { + return get_next_rrset(ctx); + } + knot_rdataset_t rd_copy; + ctx->ret = knot_rdataset_copy(&rd_copy, &res.rrs, NULL); + if (ctx->ret == KNOT_EOK) { + knot_rdataset_subtract(&rd_copy, &counter_rr.rrs, NULL); + ptrlist_add(&ctx->free_rdatasets, rd_copy.rdata, NULL); + res.rrs = rd_copy; + assert(!knot_rrset_empty(&res)); + } else { + ctx->changeset_phase = PHASE_END; + } + } + } return res; case PHASE_SOA_1: changeset_iter_rem(&ctx->it, ctx->ch); @@ -152,6 +203,16 @@ void serialize_prepare(serialize_ctx_t *ctx, size_t thresh_size, if (ctx->rrset_buf_size > 0) { ctx->rrset_buf[0] = ctx->rrset_buf[ctx->rrset_buf_size - 1]; ctx->rrset_buf_size = 1; + + // memory optimization: free all buffered rrsets except last one + ptrnode_t *n, *next; + WALK_LIST_DELSAFE(n, next, ctx->free_rdatasets) { + if (n != TAIL(ctx->free_rdatasets)) { + free(n->d); + rem_node(&n->n); + free(n); + } + } } else { ctx->rrset_buf[0] = get_next_rrset(ctx); if (ctx->changeset_phase == PHASE_END) { @@ -237,7 +298,7 @@ bool serialize_unfinished(serialize_ctx_t *ctx) return ctx->changeset_phase < PHASE_END; } -void serialize_deinit(serialize_ctx_t *ctx) +int serialize_deinit(serialize_ctx_t *ctx) { if (ctx->it.node != NULL) { changeset_iter_clear(&ctx->it); @@ -245,7 +306,15 @@ void serialize_deinit(serialize_ctx_t *ctx) if (ctx->zit.tree != NULL) { zone_tree_it_free(&ctx->zit); } + ptrnode_t *n, *next; + WALK_LIST_DELSAFE(n, next, ctx->free_rdatasets) { + free(n->d); + rem_node(&n->n); + free(n); + } + int ret = ctx->ret; free(ctx); + return ret; } static uint64_t rrset_binary_size(const knot_rrset_t *rrset) @@ -268,6 +337,40 @@ static uint64_t rrset_binary_size(const knot_rrset_t *rrset) return size; } +static size_t node_diff_size(zone_node_t *node) +{ + size_t res = 0; + knot_rrset_t rr, counter_rr; + for (int i = 0; i < node->rrset_count; i++) { + rr = node_rrset_at(node, i); + counter_rr = node_rrset(binode_counterpart(node), rr.type); + if (!knot_rrset_equal(&rr, &counter_rr, true)) { + res += rrset_binary_size(&rr); + } + } + return res; +} + +size_t zone_diff_serialized_size(zone_diff_t diff) +{ + size_t res = 0; + for (int i = 0; i < 2; i++) { + zone_diff_reverse(&diff); + zone_tree_it_t it = { 0 }; + int ret = zone_tree_it_double_begin(&diff.nodes, diff.nsec3s.trie != NULL ? + &diff.nsec3s : NULL, &it); + if (ret != KNOT_EOK) { + return 0; + } + while (!zone_tree_it_finished(&it)) { + res += node_diff_size(zone_tree_it_val(&it)); + zone_tree_it_next(&it); + } + zone_tree_it_free(&it); + } + return res; +} + size_t changeset_serialized_size(const changeset_t *ch) { if (ch == NULL) { diff --git a/src/knot/journal/serialization.h b/src/knot/journal/serialization.h index b4f80a364ff8b64bf944cbee1f12ff91e5454e00..621dcdb5f01e4f143254c716f21b39c40086daed 100644 --- a/src/knot/journal/serialization.h +++ b/src/knot/journal/serialization.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* Copyright (C) 2022 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 @@ -17,10 +17,46 @@ #pragma once #include <stdint.h> + #include "libknot/rrset.h" +#include "libknot/rrtype/soa.h" #include "knot/updates/changesets.h" #include "contrib/wire_ctx.h" +typedef struct zone_diff { + zone_tree_t nodes; + zone_tree_t nsec3s; + zone_node_t *apex; +} zone_diff_t; + +inline static void zone_diff_reverse(zone_diff_t *diff) +{ + diff->nodes.flags ^= ZONE_TREE_BINO_SECOND; + diff->nsec3s.flags ^= ZONE_TREE_BINO_SECOND; + diff->apex = binode_counterpart(diff->apex); +} + +inline static void zone_diff_from_zone(zone_diff_t *diff, const zone_contents_t *z) +{ + diff->nodes = *z->nodes; + if (z->nsec3_nodes != NULL) { + diff->nsec3s = *z->nsec3_nodes; + } else { + memset(&diff->nsec3s, 0, sizeof(diff->nsec3s)); + } + diff->apex = z->apex; +} + +inline static uint32_t zone_diff_to(const zone_diff_t *diff) +{ + return knot_soa_serial(node_rdataset(diff->apex, KNOT_RRTYPE_SOA)->rdata); +} + +inline static uint32_t zone_diff_from(const zone_diff_t *diff) +{ + return knot_soa_serial(node_rdataset(binode_counterpart(diff->apex), KNOT_RRTYPE_SOA)->rdata); +} + typedef struct serialize_ctx serialize_ctx_t; /*! @@ -41,6 +77,15 @@ serialize_ctx_t *serialize_init(const changeset_t *ch); */ serialize_ctx_t *serialize_zone_init(const zone_contents_t *z); +/*! + * \brief Init serialization context. + * + * \param z Zone with binodes being updated. + * + * \return Context. + */ +serialize_ctx_t *serialize_zone_diff_init(const zone_diff_t *z); + /*! * \brief Pre-check and space computation before serializing a chunk. * @@ -67,8 +112,23 @@ void serialize_chunk(serialize_ctx_t *ctx, uint8_t *chunk, size_t chunk_size); * to be serialized into next chunk(s) yet. */ bool serialize_unfinished(serialize_ctx_t *ctx); -/*! \brief Free serialization context. */ -void serialize_deinit(serialize_ctx_t *ctx); +/*! + * \brief Free serialization context. + * + * \return KNOT_E* if there were errors during serialization. + */ +int serialize_deinit(serialize_ctx_t *ctx); + +/*! + * \brief Returns size of serialized changeset from zone diff. + * + * \warning Not accurate! This is an upper bound, suitable for policy enforcement etc. + * + * \param[in] diff Zone diff structure to create changeset from. + * + * \return Size of the resulting changeset. + */ +size_t zone_diff_serialized_size(zone_diff_t diff); /*! * \brief Returns size of changeset in serialized form. diff --git a/src/knot/updates/ddns.c b/src/knot/updates/ddns.c index 6648ca851e3537e43bba86966e7a71a4ac2ca224..7e6ba29610e7e09a7d3938c202097edca97c6883 100644 --- a/src/knot/updates/ddns.c +++ b/src/knot/updates/ddns.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* Copyright (C) 2022 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 @@ -24,8 +24,6 @@ #include "libknot/libknot.h" #include "contrib/ucw/lists.h" -/* ----------------------------- prereq check ------------------------------- */ - /*!< \brief Clears prereq RRSet list. */ static void rrset_list_clear(list_t *l) { @@ -173,7 +171,7 @@ static bool rrset_empty(const knot_rrset_t *rrset) } } -/*< \brief Returns true if DDNS should deny updating DNSSEC-related record. */ +/*!< \brief Returns true if DDNS should deny updating DNSSEC-related record. */ static bool is_dnssec_protected(uint16_t type, bool is_apex) { switch (type) { @@ -239,10 +237,6 @@ static int process_prereq(const knot_rrset_t *rrset, uint16_t qclass, } } -/* --------------------------- DDNS processing ------------------------------ */ - -/* --------------------- true/false helper functions ------------------------ */ - static inline bool is_addition(const knot_rrset_t *rr) { return rr->rclass == KNOT_CLASS_IN; @@ -320,49 +314,26 @@ static bool skip_soa(const knot_rrset_t *rr, int64_t sn) return false; } -/* ---------------------- changeset manipulation ---------------------------- */ - /*!< \brief Replaces possible singleton RR type in changeset. */ -static bool singleton_replaced(changeset_t *changeset, - const knot_rrset_t *rr) +static bool singleton_replaced(zone_update_t *update, const knot_rrset_t *rr) { if (!should_replace(rr)) { return false; } - zone_node_t *n = zone_contents_find_node_for_rr(changeset->add, rr); - if (n == NULL) { - return false; - } - - knot_rdataset_t *rrs = node_rdataset(n, rr->type); - if (rrs == NULL) { - return false; - } - - // Replace singleton RR. - knot_rdataset_clear(rrs, NULL); - node_remove_rdataset(n, rr->type); - node_add_rrset(n, rr, NULL); - - return true; + return zone_update_remove_rrset(update, rr->owner, rr->type) == KNOT_EOK; } /*!< \brief Adds RR into add section of changeset if it is deemed worthy. */ -static int add_rr_to_changeset(const knot_rrset_t *rr, - zone_update_t *update) +static int add_rr_to_changeset(const knot_rrset_t *rr, zone_update_t *update) { - if (singleton_replaced(&update->change, rr)) { + if (singleton_replaced(update, rr)) { return KNOT_EOK; } return zone_update_add(update, rr); } -/* ------------------------ RR processing logic ----------------------------- */ - -/* --------------------------- RR additions --------------------------------- */ - /*!< \brief Processes CNAME addition (replace or ignore) */ static int process_add_cname(const zone_node_t *node, const knot_rrset_t *rr, @@ -473,8 +444,6 @@ static int process_add(const knot_rrset_t *rr, } } -/* --------------------------- RR deletions --------------------------------- */ - /*!< \brief Removes single RR from zone. */ static int process_rem_rr(const knot_rrset_t *rr, const zone_node_t *node, @@ -586,8 +555,6 @@ static int process_remove(const knot_rrset_t *rr, } } -/* --------------------------- validity checks ------------------------------ */ - /*!< \brief Checks whether addition has not violated DNAME rules. */ static bool sem_check(const knot_rrset_t *rr, const zone_node_t *zone_node, zone_update_t *update) @@ -695,8 +662,6 @@ static uint16_t ret_to_rcode(int ret) } } -/* ---------------------------------- API ----------------------------------- */ - int ddns_process_prereqs(const knot_pkt_t *query, zone_update_t *update, uint16_t *rcode) { diff --git a/src/knot/updates/zone-update.c b/src/knot/updates/zone-update.c index 944a6f9ceaf232e34895813d6b1d506f1e455757..3c56c81521662558aa6057a8b7da0771b4376c02 100644 --- a/src/knot/updates/zone-update.c +++ b/src/knot/updates/zone-update.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* Copyright (C) 2022 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 @@ -361,7 +361,11 @@ const knot_rdataset_t *zone_update_to(zone_update_t *update) return NULL; } - if (update->flags & UPDATE_FULL) { + if (update->flags & UPDATE_NO_CHSET) { + zone_diff_t diff = { .apex = update->new_cont->apex }; + return zone_diff_to(&diff) == zone_diff_from(&diff) ? + NULL : node_rdataset(update->new_cont->apex, KNOT_RRTYPE_SOA); + } else if (update->flags & UPDATE_FULL) { const zone_node_t *apex = update->new_cont->apex; return node_rdataset(apex, KNOT_RRTYPE_SOA); } else { @@ -459,10 +463,11 @@ int zone_update_add(zone_update_t *update, const knot_rrset_t *rrset) if (update->flags & (UPDATE_INCREMENTAL | UPDATE_HYBRID)) { int ret = solve_add_different_ttl(update, rrset); - if (ret == KNOT_EOK) { + if (ret == KNOT_EOK && !(update->flags & UPDATE_NO_CHSET)) { ret = changeset_add_addition(&update->change, rrset, CHANGESET_CHECK); } if (ret == KNOT_EOK && (update->flags & UPDATE_EXTRA_CHSET)) { + assert(!(update->flags & UPDATE_NO_CHSET)); ret = changeset_add_addition(&update->extra_ch, rrset, CHANGESET_CHECK); } if (ret != KNOT_EOK) { @@ -474,7 +479,7 @@ int zone_update_add(zone_update_t *update, const knot_rrset_t *rrset) if (rrset->type == KNOT_RRTYPE_SOA) { // replace previous SOA int ret = apply_replace_soa(update->a_ctx, rrset); - if (ret != KNOT_EOK) { + if (ret != KNOT_EOK && !(update->flags & UPDATE_NO_CHSET)) { changeset_remove_addition(&update->change, rrset); } return ret; @@ -482,7 +487,9 @@ int zone_update_add(zone_update_t *update, const knot_rrset_t *rrset) int ret = apply_add_rr(update->a_ctx, rrset); if (ret != KNOT_EOK) { - changeset_remove_addition(&update->change, rrset); + if (!(update->flags & UPDATE_NO_CHSET)) { + changeset_remove_addition(&update->change, rrset); + } return ret; } @@ -525,9 +532,11 @@ int zone_update_remove(zone_update_t *update, const knot_rrset_t *rrset) return KNOT_EOK; } - if ((update->flags & (UPDATE_INCREMENTAL | UPDATE_HYBRID)) && rrset->type != KNOT_RRTYPE_SOA) { + if ((update->flags & (UPDATE_INCREMENTAL | UPDATE_HYBRID)) && + rrset->type != KNOT_RRTYPE_SOA && !(update->flags & UPDATE_NO_CHSET)) { int ret = changeset_add_removal(&update->change, rrset, CHANGESET_CHECK); if (ret == KNOT_EOK && (update->flags & UPDATE_EXTRA_CHSET)) { + assert(!(update->flags & UPDATE_NO_CHSET)); ret = changeset_add_removal(&update->extra_ch, rrset, CHANGESET_CHECK); } if (ret != KNOT_EOK) { @@ -543,7 +552,9 @@ int zone_update_remove(zone_update_t *update, const knot_rrset_t *rrset) int ret = apply_remove_rr(update->a_ctx, rrset); if (ret != KNOT_EOK) { - changeset_remove_removal(&update->change, rrset); + if (!(update->flags & UPDATE_NO_CHSET)) { + changeset_remove_removal(&update->change, rrset); + } return ret; } @@ -667,15 +678,28 @@ int zone_update_increment_soa(zone_update_t *update, conf_t *conf) return set_new_soa(update, conf_opt(&val)); } +static void get_zone_diff(zone_diff_t *zdiff, zone_update_t *up) +{ + zdiff->nodes = *up->a_ctx->node_ptrs; + zdiff->nsec3s = *up->a_ctx->nsec3_ptrs; + zdiff->apex = up->new_cont->apex; +} + static int commit_journal(conf_t *conf, zone_update_t *update) { conf_val_t val = conf_zone_get(conf, C_JOURNAL_CONTENT, update->zone->name); unsigned content = conf_opt(&val); int ret = KNOT_EOK; - if ((update->flags & UPDATE_INCREMENTAL) || - (update->flags & UPDATE_HYBRID)) { + if (update->flags & UPDATE_NO_CHSET) { + zone_diff_t diff; + get_zone_diff(&diff, update); + if (content != JOURNAL_CONTENT_NONE && !zone_update_no_change(update)) { + ret = zone_diff_store(conf, update->zone, &diff); + } + } else if ((update->flags & UPDATE_INCREMENTAL) || + (update->flags & UPDATE_HYBRID)) { changeset_t *extra = (update->flags & UPDATE_EXTRA_CHSET) ? &update->extra_ch : NULL; - if (content != JOURNAL_CONTENT_NONE && !changeset_empty(&update->change)) { + if (content != JOURNAL_CONTENT_NONE && !zone_update_no_change(update)) { ret = zone_change_store(conf, update->zone, &update->change, extra); } } else { @@ -692,7 +716,7 @@ static int commit_incremental(conf_t *conf, zone_update_t *update) { assert(update); - if (zone_update_to(update) == NULL && !changeset_empty(&update->change)) { + if (zone_update_to(update) == NULL && !zone_update_no_change(update)) { /* No SOA in the update, create one according to the current policy */ int ret = zone_update_increment_soa(update, conf); if (ret != KNOT_EOK) { @@ -732,13 +756,19 @@ static int update_catalog(conf_t *conf, zone_update_t *update) } ssize_t upd_count = 0; - if ((update->flags & UPDATE_INCREMENTAL)) { + if ((update->flags & UPDATE_NO_CHSET)) { + zone_diff_t diff; + get_zone_diff(&diff, update); ret = catalog_update_from_zone(zone_catalog_upd(update->zone), - update->change.remove, update->new_cont, + NULL, &diff, update->new_cont, + false, zone_catalog(update->zone), &upd_count); + } else if ((update->flags & UPDATE_INCREMENTAL)) { + ret = catalog_update_from_zone(zone_catalog_upd(update->zone), + update->change.remove, NULL, update->new_cont, true, zone_catalog(update->zone), &upd_count); if (ret == KNOT_EOK) { ret = catalog_update_from_zone(zone_catalog_upd(update->zone), - update->change.add, update->new_cont, + update->change.add, NULL, update->new_cont, false, NULL, &upd_count); } } else { @@ -747,7 +777,7 @@ static int update_catalog(conf_t *conf, zone_update_t *update) update->zone->name, &upd_count); if (ret == KNOT_EOK) { ret = catalog_update_from_zone(zone_catalog_upd(update->zone), - update->new_cont, update->new_cont, + update->new_cont, NULL, update->new_cont, false, NULL, &upd_count); } } @@ -879,7 +909,7 @@ int zone_update_commit(conf_t *conf, zone_update_t *update) int ret = KNOT_EOK; - if ((update->flags & UPDATE_INCREMENTAL) && changeset_empty(&update->change)) { + if ((update->flags & UPDATE_INCREMENTAL) && zone_update_no_change(update)) { zone_update_clear(update); return KNOT_EOK; } @@ -1003,7 +1033,11 @@ bool zone_update_no_change(zone_update_t *update) return true; } - if (update->flags & (UPDATE_INCREMENTAL | UPDATE_HYBRID)) { + if (update->flags & UPDATE_NO_CHSET) { + zone_diff_t diff; + get_zone_diff(&diff, update); + return (zone_diff_serialized_size(diff) == 0); + } else if (update->flags & (UPDATE_INCREMENTAL | UPDATE_HYBRID)) { return changeset_empty(&update->change); } else { /* This branch does not make much sense and FULL update will most likely @@ -1012,6 +1046,17 @@ bool zone_update_no_change(zone_update_t *update) } } +static bool zone_diff_rdataset(const zone_contents_t *c, uint16_t rrtype) +{ + const knot_rdataset_t *a = node_rdataset(binode_counterpart(c->apex), rrtype); + const knot_rdataset_t *b = node_rdataset(c->apex, rrtype); + if ((a == NULL && b == NULL) || (a != NULL && b != NULL && a->rdata == b->rdata)) { + return false; + } else { + return !knot_rdataset_eq(a, b); + } +} + static bool contents_have_dnskey(const zone_contents_t *contents) { if (contents == NULL) { @@ -1025,7 +1070,11 @@ static bool contents_have_dnskey(const zone_contents_t *contents) bool zone_update_changes_dnskey(zone_update_t *update) { - if (update->flags & UPDATE_FULL) { + if (update->flags & UPDATE_NO_CHSET) { + return (zone_diff_rdataset(update->new_cont, KNOT_RRTYPE_DNSKEY) || + zone_diff_rdataset(update->new_cont, KNOT_RRTYPE_CDNSKEY) || + zone_diff_rdataset(update->new_cont, KNOT_RRTYPE_CDS)); + } else if (update->flags & UPDATE_FULL) { return contents_have_dnskey(update->new_cont); } else { return (contents_have_dnskey(update->change.remove) || diff --git a/src/knot/updates/zone-update.h b/src/knot/updates/zone-update.h index 8a2d7803b5269920afb4d81ce63b36873fbf0a74..1cda54b262b798a4135a8ad09e9913d087a9fe5b 100644 --- a/src/knot/updates/zone-update.h +++ b/src/knot/updates/zone-update.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* Copyright (C) 2022 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 @@ -56,6 +56,7 @@ typedef enum { UPDATE_STRICT = 1 << 4, /*!< Apply changes strictly, i.e. fail when removing nonexistent RR. */ UPDATE_EXTRA_CHSET = 1 << 6, /*!< Extra changeset in use, to store diff btwn zonefile and final contents. */ UPDATE_CHANGED_NSEC = 1 << 7, /*!< This incremental update affects NSEC or NSEC3 nodes in zone. */ + UPDATE_NO_CHSET = 1 << 8, /*!< Avoid using changeset and serialize to journal from diff of bi-nodes. */ } zone_update_flags_t; /*! @@ -153,6 +154,8 @@ const knot_rdataset_t *zone_update_from(zone_update_t *update); * \param update Zone update. * * \return NULL if no new SOA has been added, new SOA otherwise. + * + * \todo Refactor this function according to its use. */ const knot_rdataset_t *zone_update_to(zone_update_t *update); diff --git a/src/knot/zone/zone-tree.c b/src/knot/zone/zone-tree.c index 275f5069c0d2a84e742d2bdff5c3d9fadd3e8294..87dde1878459dde5b358a2d1557515f0921c155a 100644 --- a/src/knot/zone/zone-tree.c +++ b/src/knot/zone/zone-tree.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* Copyright (C) 2022 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 @@ -351,9 +351,16 @@ int zone_tree_it_double_begin(zone_tree_t *first, zone_tree_t *second, zone_tree if (it->it == NULL) { return KNOT_ENOMEM; } - it->tree = first; - it->binode_second = ((first->flags & ZONE_TREE_BINO_SECOND) ? 1 : 0); - it->next_tree = second; + if (trie_it_finished(it->it) && second != NULL) { // first tree is empty + trie_it_free(it->it); + it->it = trie_it_begin(second->trie); + it->tree = second; + it->next_tree = NULL; + } else { + it->tree = first; + it->next_tree = second; + } + it->binode_second = ((it->tree->flags & ZONE_TREE_BINO_SECOND) ? 1 : 0); } return KNOT_EOK; } diff --git a/src/knot/zone/zone-tree.h b/src/knot/zone/zone-tree.h index bf8a6600d48dffef7bd99d20f8d2a491018a5695..384e87efdfc7cd8a463d15c7a3684d7d8dbaf6d2 100644 --- a/src/knot/zone/zone-tree.h +++ b/src/knot/zone/zone-tree.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* Copyright (C) 2022 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 @@ -86,7 +86,7 @@ zone_tree_t *zone_tree_shallow_copy(zone_tree_t *from); */ inline static size_t zone_tree_count(const zone_tree_t *tree) { - if (tree == NULL) { + if (tree == NULL || tree->trie == NULL) { return 0; } diff --git a/src/knot/zone/zone.c b/src/knot/zone/zone.c index 321f3f5e66661a8b48ddd325e295a61482e133a0..8d412613012ce3c6053c04f896107f188b486844 100644 --- a/src/knot/zone/zone.c +++ b/src/knot/zone/zone.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* Copyright (C) 2022 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 @@ -280,14 +280,36 @@ int zone_change_store(conf_t *conf, zone_t *zone, changeset_t *change, changeset zone_journal_t j = { zone_journaldb(zone), zone->name, conf }; - int ret = journal_insert(j, change, extra); + int ret = journal_insert(j, change, extra, NULL); if (ret == KNOT_EBUSY) { log_zone_notice(zone->name, "journal is full, flushing"); /* Transaction rolled back, journal released, we may flush. */ ret = flush_journal(conf, zone, true, false); if (ret == KNOT_EOK) { - ret = journal_insert(j, change, extra); + ret = journal_insert(j, change, extra, NULL); + } + } + + return ret; +} + +int zone_diff_store(conf_t *conf, zone_t *zone, const zone_diff_t *diff) +{ + if (conf == NULL || zone == NULL || diff == NULL) { + return KNOT_EINVAL; + } + + zone_journal_t j = { zone_journaldb(zone), zone->name, conf }; + + int ret = journal_insert(j, NULL, NULL, diff); + if (ret == KNOT_EBUSY) { + log_zone_notice(zone->name, "journal is full, flushing"); + + /* Transaction rolled back, journal released, we may flush. */ + ret = flush_journal(conf, zone, true, false); + if (ret == KNOT_EOK) { + ret = journal_insert(j, NULL, NULL, diff); } } diff --git a/src/knot/zone/zone.h b/src/knot/zone/zone.h index 7a8c2ac0a567eb67b190b08373c2311855c991cd..6623c43848456f2c290cb8f291515bb9d3dbc9a5 100644 --- a/src/knot/zone/zone.h +++ b/src/knot/zone/zone.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* Copyright (C) 2022 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 @@ -21,6 +21,7 @@ #include "knot/conf/conf.h" #include "knot/conf/confio.h" #include "knot/journal/journal_basic.h" +#include "knot/journal/serialization.h" #include "knot/events/events.h" #include "knot/updates/changesets.h" #include "knot/zone/contents.h" @@ -153,6 +154,7 @@ inline static zone_journal_t zone_journal(zone_t *zone) } int zone_change_store(conf_t *conf, zone_t *zone, changeset_t *change, changeset_t *extra); +int zone_diff_store(conf_t *conf, zone_t *zone, const zone_diff_t *diff); int zone_changes_clear(conf_t *conf, zone_t *zone); int zone_in_journal_store(conf_t *conf, zone_t *zone, zone_contents_t *new_contents); diff --git a/src/libknot/rdataset.c b/src/libknot/rdataset.c index 6e5c81c8f06408e4b80c9bd8e8489a96aa3b8707..0b9b4fd8c611358a74ad1f62e3733cf7965966b8 100644 --- a/src/libknot/rdataset.c +++ b/src/libknot/rdataset.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* Copyright (C) 2022 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 @@ -246,6 +246,24 @@ bool knot_rdataset_member(const knot_rdataset_t *rrs, const knot_rdata_t *rr) return false; } +_public_ +bool knot_rdataset_subset(const knot_rdataset_t *subset, const knot_rdataset_t *of) +{ + if (subset == NULL || (of != NULL && subset->rdata == of->rdata)) { + return true; + } + + knot_rdata_t *rd = subset->rdata; + for (uint16_t i = 0; i < subset->count; ++i) { + if (!knot_rdataset_member(of, rd)) { + return false; + } + rd = knot_rdataset_next(rd); + } + + return true; +} + _public_ int knot_rdataset_merge(knot_rdataset_t *rrs1, const knot_rdataset_t *rrs2, knot_mm_t *mm) diff --git a/src/libknot/rdataset.h b/src/libknot/rdataset.h index 5c49847fdd69374990eb4047c626eb0697f6bf1e..f3e65a86e63b49277c1af3a6ddd1d4e9d49eeb0b 100644 --- a/src/libknot/rdataset.h +++ b/src/libknot/rdataset.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* Copyright (C) 2022 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 @@ -134,6 +134,17 @@ bool knot_rdataset_eq(const knot_rdataset_t *rrs1, const knot_rdataset_t *rrs2); */ bool knot_rdataset_member(const knot_rdataset_t *rrs, const knot_rdata_t *rr); +/*! + * \brief Returns true if \a subset is a sub-set of \a of, false otherwise. + * + * \param subset RRS to check. + * \param of RRS to search in. + * + * \retval true if \a subset is a sub-set of \a of. + * \retval false if \a subset is not a sub-set of \a of. + */ +bool knot_rdataset_subset(const knot_rdataset_t *subset, const knot_rdataset_t *of); + /*! * \brief Merges two RRS into the first one. Second RRS is left intact. * Canonical order is preserved. diff --git a/tests/knot/test_journal.c b/tests/knot/test_journal.c index afc824422aa4e0f868cef6e215e7b980f63175a8..748ab02d02622269801a1596fd51e87c2621a612 100644 --- a/tests/knot/test_journal.c +++ b/tests/knot/test_journal.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* Copyright (C) 2022 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 @@ -315,7 +315,7 @@ static void test_store_load(const knot_dname_t *apex) changeset_t *m_ch = changeset_new(apex), r_ch, e_ch; init_random_changeset(m_ch, 0, 1, 128, apex, false); - int ret = journal_insert(jj, m_ch, NULL); + int ret = journal_insert(jj, m_ch, NULL, NULL); is_int(KNOT_EOK, ret, "journal: store changeset (%s)", knot_strerror(ret)); ret = journal_sem_check(jj); is_int(KNOT_EOK, ret, "journal: check after store changeset (%s)", knot_strerror(ret)); @@ -336,7 +336,7 @@ static void test_store_load(const knot_dname_t *apex) for (; ret == KNOT_EOK && serial < 40000; ++serial) { changeset_t *m_ch2 = changeset_new(apex); init_random_changeset(m_ch2, serial, serial + 1, 128, apex, false); - ret = journal_insert(jj, m_ch2, NULL); + ret = journal_insert(jj, m_ch2, NULL, NULL); if (ret != KNOT_EOK) { changeset_free(m_ch2); break; @@ -378,7 +378,7 @@ static void test_store_load(const knot_dname_t *apex) ret = changeset_init(&ch, apex); ok(ret == KNOT_EOK, "journal: changeset init (%d)", ret); init_random_changeset(&ch, serial, serial + 1, 555, apex, false); - ret = journal_insert(jj, &ch, NULL); + ret = journal_insert(jj, &ch, NULL, NULL); is_int(KNOT_EOK, ret, "journal: store after flush (%d)", ret); ret = journal_sem_check(jj); is_int(KNOT_EOK, ret, "journal check (%s)", knot_strerror(ret)); @@ -393,7 +393,7 @@ static void test_store_load(const knot_dname_t *apex) changeset_init(&ch, apex); init_random_changeset(&ch, 2, 3, 100, apex, false); - ret = journal_insert(jj, &ch, NULL); + ret = journal_insert(jj, &ch, NULL, NULL); is_int(KNOT_EOK, ret, "journal: insert discontinuous changeset (%s)", knot_strerror(ret)); ret = journal_sem_check(jj); is_int(KNOT_EOK, ret, "journal check (%s)", knot_strerror(ret)); @@ -412,14 +412,14 @@ static void test_store_load(const knot_dname_t *apex) changeset_clear(&ch); changeset_init(&ch, apex); init_random_changeset(&ch, serials[i], serials[i + 1], 100, apex, false); - ret = journal_insert(jj, &ch, NULL); + ret = journal_insert(jj, &ch, NULL, NULL); is_int(i == 4 ? KNOT_EBUSY : KNOT_EOK, ret, "journal: inserting cycle (%s)", knot_strerror(ret)); ret = journal_sem_check(jj); is_int(KNOT_EOK, ret, "journal check (%s)", knot_strerror(ret)); } ret = journal_set_flushed(jj); is_int(KNOT_EOK, ret, "journal: flush in cycle (%s)", knot_strerror(ret)); - ret = journal_insert(jj, &ch, NULL); + ret = journal_insert(jj, &ch, NULL, NULL); is_int(KNOT_EOK, ret, "journal: inserted cycle (%s)", knot_strerror(ret)); ret = journal_sem_check(jj); is_int(KNOT_EOK, ret, "journal check (%s)", knot_strerror(ret)); @@ -445,7 +445,7 @@ static void test_store_load(const knot_dname_t *apex) is_int(KNOT_EOK, ret, "journal: insert zone-in-journal (%s)", knot_strerror(ret)); changeset_init(&r_ch, apex); init_random_changeset(&r_ch, 1, 2, 200, apex, false); - ret = journal_insert(jj, &r_ch, NULL); + ret = journal_insert(jj, &r_ch, NULL, NULL); is_int(KNOT_EOK, ret, "journal: insert after zone-in-journal (%s)", knot_strerror(ret)); ret = load_j_list(&jj, true, 0, &read, &l); is_int(KNOT_EOK, ret, "journal: load zone-in-journal (%s)", knot_strerror(ret)); @@ -470,7 +470,7 @@ static void test_size_control(const knot_dname_t *zone1, const knot_dname_t *zon zone_journal_t jj2 = { &jdb, zone2, conf() }; changeset_t *small_ch2 = changeset_new(zone2); init_random_changeset(small_ch2, 1, 2, 100, zone2, false); - int ret = journal_insert(jj2, small_ch2, NULL); + int ret = journal_insert(jj2, small_ch2, NULL, NULL); is_int(KNOT_EOK, ret, "journal: storing small changeset must be ok"); changeset_t *big_zij = changeset_new(zone1); @@ -482,7 +482,7 @@ static void test_size_control(const knot_dname_t *zone1, const knot_dname_t *zon changeset_t *big_ch2 = changeset_new(zone2); init_random_changeset(big_ch2, 2, 3, 750, zone2, false); - ret = journal_insert(jj2, big_ch2, NULL); + ret = journal_insert(jj2, big_ch2, NULL, NULL); is_int(KNOT_EOK, ret, "journal: second zone is not affected by storing big zij of other zone"); journal_read_t *read = NULL; @@ -490,7 +490,7 @@ static void test_size_control(const knot_dname_t *zone1, const knot_dname_t *zon init_list(&l); changeset_t *medium_ch1 = changeset_new(zone1); init_random_changeset(medium_ch1, 1, 2, 300, zone1, false); - ret = journal_insert(jj, medium_ch1, NULL); + ret = journal_insert(jj, medium_ch1, NULL, NULL); is_int(KNOT_EOK, ret, "journal: storing medium changeset must be ok"); ret = load_j_list(&jj, true, 0, &read, &l); is_int(KNOT_EOK, ret, "journal: load zone-in-journal (%s)", knot_strerror(ret)); @@ -500,7 +500,7 @@ static void test_size_control(const knot_dname_t *zone1, const knot_dname_t *zon changeset_t *small_ch1 = changeset_new(zone1); init_random_changeset(small_ch1, 2, 3, 100, zone1, false); - ret = journal_insert(jj, small_ch1, NULL); + ret = journal_insert(jj, small_ch1, NULL, NULL); is_int(KNOT_EOK, ret, "journal: storing small changeset must be ok"); ret = load_j_list(&jj, true, 0, &read, &l); is_int(KNOT_EOK, ret, "journal: load zone-in-journal (%s)", knot_strerror(ret)); @@ -510,7 +510,7 @@ static void test_size_control(const knot_dname_t *zone1, const knot_dname_t *zon changeset_t *medium_ch1b = changeset_new(zone1); init_random_changeset(medium_ch1b, 3, 4, 300, zone1, false); - ret = journal_insert(jj, medium_ch1b, NULL); + ret = journal_insert(jj, medium_ch1b, NULL, NULL); is_int(KNOT_ESPACE, ret, "journal: not able to free space for changeset by merging"); changeset_t *too_big_zij = changeset_new(zone1); @@ -726,7 +726,7 @@ static void test_merge(const knot_dname_t *apex) // insert stuff and check the merge for (i = 0; !merged_present() && i < 40000; i++) { - ret = journal_insert(jj, tm_chs(apex, i), NULL); + ret = journal_insert(jj, tm_chs(apex, i), NULL, NULL); assert(ret == KNOT_EOK); } ret = journal_sem_check(jj); @@ -743,7 +743,7 @@ static void test_merge(const knot_dname_t *apex) journal_read_end(read); // insert one more and check the #s of results - ret = journal_insert(jj, tm_chs(apex, i), NULL); + ret = journal_insert(jj, tm_chs(apex, i), NULL, NULL); is_int(KNOT_EOK, ret, "journal: insert one more (%s)", knot_strerror(ret)); ret = load_j_list(&jj, false, 0, &read, &l); is_int(KNOT_EOK, ret, "journal: journal_load_changesets2 must be ok"); @@ -764,7 +764,7 @@ static void test_merge(const knot_dname_t *apex) // insert changeset that will cancel it mostly out changeset_t *bigz_cancelout = tm2_chs_unzone(apex); - ret = journal_insert(jj, bigz_cancelout, NULL); + ret = journal_insert(jj, bigz_cancelout, NULL, NULL); changeset_free(bigz_cancelout); is_int(KNOT_EOK, ret, "journal: insert cancel-out changeset"); @@ -772,7 +772,7 @@ static void test_merge(const knot_dname_t *apex) tm_chs(apex, -1); while (changeset_to(tm_chs(apex, 0)) != 2) { } for (i = 0; i < 1600; i++) { - ret = journal_insert(jj, tm_chs(apex, i), NULL); + ret = journal_insert(jj, tm_chs(apex, i), NULL, NULL); assert(ret == KNOT_EOK); } @@ -817,7 +817,7 @@ static void test_stress_base(const knot_dname_t *apex, serial = 0; while (true) { changeset_set_soa_serials(&ch, serial, serial + 1, apex); - ret = journal_insert(jj, &ch, NULL); + ret = journal_insert(jj, &ch, NULL, NULL); if (ret == KNOT_EOK) { serial++; } else {