Commit a6f82225 authored by Libor Peltan's avatar Libor Peltan Committed by Daniel Salzman

changesets: preapply_fix feature

this 'cleans up' the changeset going to be applied to a zone contents
from any removals/additions not applicable
parent 46e4e14c
/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
/* Copyright (C) 2017 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
......@@ -164,7 +164,7 @@ static void check_redundancy(zone_contents_t *counterpart, const knot_rrset_t *r
// Remove empty type.
node_remove_rdataset(node, rr->type);
if (node->rrset_count == 0) {
if (node->rrset_count == 0 && node != counterpart->apex) {
// Remove empty node.
zone_tree_t *t = knot_rrset_is_nsec3rel(rr) ?
counterpart->nsec3_nodes : counterpart->nodes;
......@@ -381,6 +381,10 @@ int changeset_merge(changeset_t *ch1, const changeset_t *ch2)
// Use soa_to and serial from the second changeset
// soa_to from the first changeset is redundant, delete it
if (ch2->soa_to == NULL && ch2->soa_from == NULL) {
// but not if ch2 has no soa change
return KNOT_EOK;
}
knot_rrset_t *soa_copy = knot_rrset_copy(ch2->soa_to, NULL);
if (soa_copy == NULL && ch2->soa_to) {
return KNOT_ENOMEM;
......@@ -391,6 +395,109 @@ int changeset_merge(changeset_t *ch1, const changeset_t *ch2)
return KNOT_EOK;
}
typedef struct {
const zone_contents_t *zone;
changeset_t *fixing;
knot_mm_t *mm;
} preapply_fix_ctx;
static int preapply_fix_rrset(const knot_rrset_t *apply, bool adding, void *data)
{
preapply_fix_ctx *ctx = (preapply_fix_ctx *)data;
const zone_node_t *znode = zone_contents_find_node(ctx->zone, apply->owner);
const knot_rdataset_t *zrdataset = node_rdataset(znode, apply->type);
if (adding && zrdataset == NULL) {
return KNOT_EOK;
}
knot_rrset_t *fixrrset;
int ret = KNOT_EOK;
if (adding) {
fixrrset = knot_rrset_new(apply->owner, apply->type, apply->rclass, ctx->mm);
} else {
fixrrset = knot_rrset_copy(apply, ctx->mm);
}
if (fixrrset == NULL) {
return KNOT_ENOMEM;
}
if (adding) {
ret = knot_rdataset_intersect(zrdataset, &apply->rrs, &fixrrset->rrs, ctx->mm);
} else {
if (zrdataset != NULL) {
ret = knot_rdataset_subtract(&fixrrset->rrs, zrdataset, ctx->mm);
}
}
if (ret == KNOT_EOK && !knot_rrset_empty(fixrrset)) {
if (adding) {
ret = changeset_add_removal(ctx->fixing, fixrrset, 0);
} else {
ret = changeset_add_addition(ctx->fixing, fixrrset, 0);
}
}
knot_rrset_free(&fixrrset, ctx->mm);
return ret;
}
static int subtract_callback(const knot_rrset_t *rrset, bool addition, void *subtractfrom)
{
changeset_t *chsf = (changeset_t *)subtractfrom;
if (addition) {
return changeset_remove_removal(chsf, rrset);
} else {
return changeset_remove_addition(chsf, rrset);
}
}
static int subtract(changeset_t *from, const changeset_t *what)
{
return changeset_walk(what, subtract_callback, (void *)from);
}
int changeset_preapply_fix(const zone_contents_t *zone, changeset_t *change)
{
if (zone == NULL || change == NULL) {
return KNOT_EINVAL;
}
changeset_t fixing;
int ret = changeset_init(&fixing, zone->apex->owner);
if (ret != KNOT_EOK) {
return ret;
}
preapply_fix_ctx ctx = { .zone = zone, .fixing = &fixing, .mm = NULL };
ret = changeset_walk(change, preapply_fix_rrset, (void *)&ctx);
if (ret == KNOT_EOK) {
ret = subtract(change, &fixing);
}
changeset_clear(&fixing);
return ret;
}
int changeset_cancelout(changeset_t *change)
{
if (change == NULL) {
return KNOT_EINVAL;
}
changeset_t fixing;
int ret = changeset_init(&fixing, change->add->apex->owner);
if (ret != KNOT_EOK) {
return ret;
}
preapply_fix_ctx ctx = { .zone = change->remove, .fixing = &fixing, .mm = NULL };
ret = changeset_walk(change, preapply_fix_rrset, (void *)&ctx);
if (ret == KNOT_EOK) {
assert(zone_contents_is_empty(fixing.add));
zone_contents_t *fixing_add_bck = fixing.add;
fixing.add = fixing.remove;
ret = subtract(change, &fixing);
fixing.add = fixing_add_bck;
}
changeset_clear(&fixing);
return ret;
}
zone_contents_t *changeset_to_contents(changeset_t *ch)
{
assert(ch->soa_from == NULL);
......@@ -533,3 +640,41 @@ void changeset_iter_clear(changeset_iter_t *it)
it->node_pos = 0;
}
}
int changeset_walk(const changeset_t *changeset, changeset_walk_callback callback, void *ctx)
{
changeset_iter_t it;
int ret = changeset_iter_rem(&it, changeset);
if (ret != KNOT_EOK) {
return ret;
}
knot_rrset_t rrset = changeset_iter_next(&it);
while (!knot_rrset_empty(&rrset)) {
ret = callback(&rrset, false, ctx);
if (ret != KNOT_EOK) {
changeset_iter_clear(&it);
return ret;
}
rrset = changeset_iter_next(&it);
}
changeset_iter_clear(&it);
ret = changeset_iter_add(&it, changeset);
if (ret != KNOT_EOK) {
return ret;
}
rrset = changeset_iter_next(&it);
while (!knot_rrset_empty(&rrset)) {
ret = callback(&rrset, true, ctx);
if (ret != KNOT_EOK) {
changeset_iter_clear(&it);
return ret;
}
rrset = changeset_iter_next(&it);
}
changeset_iter_clear(&it);
return KNOT_EOK;
}
/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
/* Copyright (C) 2017 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
......@@ -13,14 +13,6 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*!
* \file
*
* \brief Structure for representing zone change and its API.
*
* \addtogroup ddns
* @{
*/
#pragma once
......@@ -143,6 +135,25 @@ int changeset_remove_removal(changeset_t *ch, const knot_rrset_t *rrset);
*/
int changeset_merge(changeset_t *ch1, const changeset_t *ch2);
/*!
* \brief Remove from changeset those rdata which won't be added/removed from zone.
*
* \param zone The zone the changeset is going to be applied on.
* \param change The cheangeset to be fixed.
*
* \return KNOT_E*
*/
int changeset_preapply_fix(const zone_contents_t *zone, changeset_t *change);
/*!
* \brief Remove from changeset records which are removed and added the same.
*
* \param change Changeset to be fixed.
*
* \return KNOT_E*
*/
int changeset_cancelout(changeset_t *change);
/*!
* \brief Loads zone contents from botstrap changeset.
*
......@@ -242,4 +253,25 @@ knot_rrset_t changeset_iter_next(changeset_iter_t *it);
*/
void changeset_iter_clear(changeset_iter_t *it);
/*! @} */
/*!
* \brief A pointer type for callback for changeset_walk() function.
*
* \param rrset An actual removal/addition inside the changeset.
* \param addition Indicates addition against removal.
* \param ctx A context passed to the changeset_walk() function.
*
* \retval KNOT_EOK if all ok, iteration will continue
* \return KNOT_E* if error, iteration will stop immediately and changeset_walk() returns this error.
*/
typedef int (*changeset_walk_callback)(const knot_rrset_t *rrset, bool addition, void *ctx);
/*!
* \brief Calls a callback for each removal/addition in the changeset.
*
* \param changeset Changeset.
* \param callback Callback.
* \param ctx Arbitrary context passed to the callback.
*
* \return KNOT_E*
*/
int changeset_walk(const changeset_t *changeset, changeset_walk_callback callback, void *ctx);
/* Copyright (C) 2014 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
/* Copyright (C) 2017 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 <tap/basic.h>
#include "libknot/errcode.h"
#include "libknot/error.h"
#include "knot/updates/changesets.h"
int main(int argc, char *argv[])
......@@ -145,6 +146,28 @@ int main(int argc, char *argv[])
ret = changeset_merge(ch, ch2);
ok(ret == KNOT_EOK && changeset_size(ch) == 5, "changeset: merge");
// Test preapply fix.
zone_contents_t *z = zone_contents_new((const knot_dname_t *)"\x04""test");
knot_dname_free(&apex_txt_rr->owner, NULL);
apex_txt_rr->owner = knot_dname_from_str_alloc("something.test.");
assert(apex_txt_rr->owner);
zone_node_t *znode = NULL;
ret = zone_contents_add_rr(z, apex_txt_rr, &znode);
assert(ret == KNOT_EOK);
ret = changeset_preapply_fix(z, ch2);
ok(ret == KNOT_EOK, "changeset: preapply fix ok (%s)", knot_strerror(ret));
ok(changeset_empty(ch2), "changeset: preapply fix works");
zone_contents_deep_free(&z);
// Test cancelout.
ret = changeset_add_removal(ch2, apex_txt_rr, 0);
assert(ret == KNOT_EOK);
ret = changeset_add_addition(ch2, apex_txt_rr, 0);
assert(ret == KNOT_EOK);
ret = changeset_cancelout(ch2);
ok(ret == KNOT_EOK, "changeset: cancelout ok (%s)", knot_strerror(ret));
ok(changeset_empty(ch2), "changeset: cancelout works");
// Test cleanup.
changeset_clear(ch);
ok(changeset_empty(ch), "changeset: clear");
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment