Commit 77aab373 authored by Jan Kadlec's avatar Jan Kadlec

changeset: Initial commit with single changeset application

- pending: Storing into journal ...
- I'll probably purge the changeset list in the future and merge all the
  incoming changes into one change ... but that will take time
parent 4fcc599c
......@@ -70,25 +70,13 @@ static bool zones_nsec3param_changed(const zone_contents_t *old_contents,
static int sign_update(zone_t *zone, const zone_contents_t *old_contents,
zone_contents_t *new_contents, changeset_t *ddns_ch,
list_t **sec_chs)
changeset_t *sec_ch)
{
assert(zone != NULL);
assert(old_contents != NULL);
assert(new_contents != NULL);
assert(ddns_ch != NULL);
changeset_t *sec_ch = changeset_new(zone->name);
if (sec_ch == NULL) {
return KNOT_ENOMEM;
}
*sec_chs = malloc(sizeof(list_t));
if (*sec_chs == NULL) {
changeset_free(sec_ch);
return KNOT_ENOMEM;
}
init_list(*sec_chs);
add_head(*sec_chs, &sec_ch->n);
/*
* Check if the UPDATE changed DNSKEYs or NSEC3PARAM.
* If so, we have to sign the whole zone.
......@@ -107,25 +95,19 @@ static int sign_update(zone_t *zone, const zone_contents_t *old_contents,
&refresh_at);
}
if (ret != KNOT_EOK) {
changesets_free(*sec_chs);
free(*sec_chs);
return ret;
}
// Apply DNSSEC changeset
ret = apply_changesets_directly(new_contents, *sec_chs);
ret = apply_changeset_directly(new_contents, sec_ch);
if (ret != KNOT_EOK) {
changesets_free(*sec_chs);
free(*sec_chs);
return ret;
}
// Merge changesets
ret = changeset_merge(ddns_ch, sec_ch);
if (ret != KNOT_EOK) {
update_cleanup(*sec_chs);
changesets_free(*sec_chs);
free(*sec_chs);
update_cleanup(sec_ch);
return ret;
}
......@@ -172,9 +154,7 @@ static int process_authenticated(uint16_t *rcode, struct query_data *qdata)
const bool change_made = !changeset_empty(&ddns_ch);
list_t apply;
if (change_made) {
init_list(&apply);
add_head(&apply, &ddns_ch.n);
ret = apply_changesets(zone, &apply, &new_contents);
ret = apply_changeset(zone, &ddns_ch, &new_contents);
if (ret != KNOT_EOK) {
if (ret == KNOT_ETTL) {
*rcode = KNOT_RCODE_REFUSED;
......@@ -191,13 +171,20 @@ static int process_authenticated(uint16_t *rcode, struct query_data *qdata)
}
assert(new_contents);
list_t *sec_chs = NULL;
changeset_t sec_ch;
if (zone->conf->dnssec_enable) {
ret = changeset_init(&sec_ch, zone->name);
if (ret != KNOT_EOK) {
*rcode = KNOT_RCODE_SERVFAIL;
return ret;
}
ret = sign_update(zone, zone->contents, new_contents, &ddns_ch,
&sec_chs);
&sec_ch);
if (ret != KNOT_EOK) {
update_rollback(&apply, &new_contents);
update_rollback(&ddns_ch);
update_free_old_zone(&new_contents);
changeset_clear(&ddns_ch);
changeset_clear(&sec_ch);
*rcode = KNOT_RCODE_SERVFAIL;
return ret;
}
......@@ -206,8 +193,12 @@ static int process_authenticated(uint16_t *rcode, struct query_data *qdata)
// Write changes to journal if all went well. (DNSSEC merged)
ret = zone_change_store(zone, &apply);
if (ret != KNOT_EOK) {
update_rollback(&apply, &new_contents);
update_rollback(&ddns_ch);
update_free_old_zone(&new_contents);
changeset_clear(&ddns_ch);
if (zone->conf->dnssec_enable) {
changeset_clear(&sec_ch);
}
*rcode = KNOT_RCODE_SERVFAIL;
return ret;
}
......@@ -217,14 +208,15 @@ static int process_authenticated(uint16_t *rcode, struct query_data *qdata)
synchronize_rcu();
// Clear DNSSEC changes
update_cleanup(sec_chs);
changesets_free(sec_chs);
free(sec_chs);
if (zone->conf->dnssec_enable) {
update_cleanup(&sec_ch);
changeset_clear(&sec_ch);
}
// Clear obsolete zone contents
update_free_old_zone(&old_contents);
update_cleanup(&apply);
updates_cleanup(&apply);
changeset_clear(&ddns_ch);
/* Sync zonefile immediately if configured. */
......
......@@ -306,7 +306,7 @@ static int apply_replace_soa(zone_contents_t *contents, changeset_t *chset)
}
/*! \brief Apply single change to zone contents structure. */
static int apply_changeset(zone_contents_t *contents, changeset_t *chset,
static int apply_single(zone_contents_t *contents, changeset_t *chset,
bool master)
{
/*
......@@ -482,9 +482,10 @@ int apply_changesets(zone_t *zone, list_t *chsets, zone_contents_t **new_content
changeset_t *set = NULL;
const bool master = (zone_master(zone) == NULL);
WALK_LIST(set, *chsets) {
ret = apply_changeset(contents_copy, set, master);
ret = apply_single(contents_copy, set, master);
if (ret != KNOT_EOK) {
update_rollback(chsets, &contents_copy);
updates_rollback(chsets);
update_free_old_zone(&contents_copy);
return ret;
}
}
......@@ -493,7 +494,8 @@ int apply_changesets(zone_t *zone, list_t *chsets, zone_contents_t **new_content
ret = finalize_updated_zone(contents_copy, true);
if (ret != KNOT_EOK) {
update_rollback(chsets, &contents_copy);
updates_rollback(chsets);
update_free_old_zone(&contents_copy);
return ret;
}
......@@ -502,6 +504,43 @@ int apply_changesets(zone_t *zone, list_t *chsets, zone_contents_t **new_content
return KNOT_EOK;
}
int apply_changeset(zone_t *zone, changeset_t *change, zone_contents_t **new_contents)
{
if (zone == NULL || change == NULL || new_contents == NULL) {
return KNOT_EINVAL;
}
zone_contents_t *old_contents = zone->contents;
if (!old_contents) {
return KNOT_EINVAL;
}
zone_contents_t *contents_copy = NULL;
int ret = prepare_zone_copy(old_contents, &contents_copy);
if (ret != KNOT_EOK) {
return ret;
}
const bool master = (zone_master(zone) == NULL);
ret = apply_single(contents_copy, change, master);
if (ret != KNOT_EOK) {
update_rollback(change);
update_free_old_zone(&contents_copy);
return ret;
}
ret = finalize_updated_zone(contents_copy, true);
if (ret != KNOT_EOK) {
update_rollback(change);
update_free_old_zone(&contents_copy);
return ret;
}
*new_contents = contents_copy;
return KNOT_EOK;
}
int apply_changesets_directly(zone_contents_t *contents, list_t *chsets)
{
if (contents == NULL || chsets == NULL) {
......@@ -511,57 +550,91 @@ int apply_changesets_directly(zone_contents_t *contents, list_t *chsets)
changeset_t *set = NULL;
WALK_LIST(set, *chsets) {
const bool master = true; // Only DNSSEC changesets are applied directly.
int ret = apply_changeset(contents, set, master);
int ret = apply_single(contents, set, master);
if (ret != KNOT_EOK) {
update_cleanup(chsets);
updates_cleanup(chsets);
return ret;
}
}
return finalize_updated_zone(contents, true);
int ret = finalize_updated_zone(contents, true);
if (ret != KNOT_EOK) {
updates_cleanup(chsets);
}
return ret;
}
void update_cleanup(list_t *chgs)
int apply_changeset_directly(zone_contents_t *contents, changeset_t *ch)
{
if (chgs == NULL || EMPTY_LIST(*chgs)) {
return;
if (contents == NULL || ch == NULL) {
return KNOT_EINVAL;
}
const bool master = true; // Only DNSSEC changesets are applied directly.
int ret = apply_single(contents, ch, master);
if (ret != KNOT_EOK) {
update_cleanup(ch);
return ret;
}
ret = finalize_updated_zone(contents, true);
if (ret != KNOT_EOK) {
update_cleanup(ch);
return ret;
}
return KNOT_EOK;
}
changeset_t *change = NULL;
WALK_LIST(change, *chgs) {
void update_cleanup(changeset_t *change)
{
if (change) {
// Delete old RR data
rrs_list_clear(&change->old_data, NULL);
init_list(&change->old_data);
// Keep new RR data
ptrlist_free(&change->new_data, NULL);
init_list(&change->new_data);
}
}
void updates_cleanup(list_t *chgs)
{
if (chgs == NULL || EMPTY_LIST(*chgs)) {
return;
}
changeset_t *change = NULL;
WALK_LIST(change, *chgs) {
update_cleanup(change);
};
}
void update_rollback(list_t *chgs, zone_contents_t **new_contents)
void update_rollback(changeset_t *change)
{
if (change) {
// Delete new RR data
rrs_list_clear(&change->new_data, NULL);
init_list(&change->new_data);
// Keep old RR data
ptrlist_free(&change->old_data, NULL);
init_list(&change->old_data);
}
}
void updates_rollback(list_t *chgs)
{
if (chgs != NULL && !EMPTY_LIST(*chgs)) {
changeset_t *change = NULL;
WALK_LIST(change, *chgs) {
// Delete new RR data
rrs_list_clear(&change->new_data, NULL);
init_list(&change->new_data);
// Keep old RR data
ptrlist_free(&change->old_data, NULL);
init_list(&change->old_data);
};
}
if (new_contents) {
update_free_old_zone(new_contents);
update_rollback(change);
}
}
}
void update_free_old_zone(zone_contents_t **contents)
{
/*
* Free the zone tree, but only the structure
* (nodes are already destroyed) and free additional arrays.
*/
zone_tree_apply((*contents)->nodes, free_additional, NULL);
zone_tree_deep_free(&(*contents)->nodes);
zone_tree_deep_free(&(*contents)->nsec3_nodes);
......
......@@ -45,6 +45,18 @@
int apply_changesets(zone_t *zone, list_t *chsets,
zone_contents_t **new_contents);
/*!
* \brief Applies changeset *with* zone shallow copy.
*
* \param zone Zone to be updated.
* \param ch Change to be made.
* \param new_contents New zone will be returned using this arg.
*
* \return KNOT_E*
*/
int apply_changeset(zone_t *zone, changeset_t *ch,
zone_contents_t **new_contents);
/*!
* \brief Applies changesets directly to the zone, without copying it.
*
......@@ -54,25 +66,28 @@ int apply_changesets(zone_t *zone, list_t *chsets,
* \return KNOT_E*
*/
int apply_changesets_directly(zone_contents_t *contents, list_t *chsets);
int apply_changeset_directly(zone_contents_t *contents, changeset_t *ch);
/*!
* \brief Cleanups successful update. (IXFR, DNSSEC, DDNS).
* \param chgs Changesets used to create the update.
*/
void update_cleanup(list_t *chgs);
void updates_cleanup(list_t *chgs);
void update_cleanup(changeset_t *change);
/*!
* \brief Rollbacks failed update (IXFR, DNSSEC, DDNS).
*
* \param chgs Changesets used to create the update.
* \param new_contents Created zone contents.
* \param chgs Changesets used to create the update.
*/
void update_rollback(list_t *chgs, zone_contents_t **new_contents);
void updates_rollback(list_t *chgs);
void update_rollback(changeset_t *change);
/*!
* \brief Frees old zone contents - i.e. contents that were used to create the
* shallow copy, but are now obsolete.
* \note Exported because of update.c, zone.c.
* \param contents Contents to free.
*/
void update_free_old_zone(zone_contents_t **contents);
......
......@@ -113,7 +113,7 @@ int zone_load_journal(zone_t *zone)
serial, zone_contents_serial(zone->contents),
knot_strerror(ret));
update_cleanup(&chgs);
updates_cleanup(&chgs);
changesets_free(&chgs);
return ret;
}
......@@ -143,14 +143,15 @@ int zone_load_post(zone_contents_t *contents, zone_t *zone, uint32_t *dnssec_ref
}
/* Apply DNSSEC changes. */
list_t apply;
init_list(&apply);
add_head(&apply, &change.n);
ret = zone_change_commit(contents, &apply);
update_cleanup(&apply);
if (ret != KNOT_EOK) {
if (!changeset_empty(&change)) {
ret = apply_changeset_directly(contents, &change);
update_cleanup(&change);
if (ret != KNOT_EOK) {
changeset_clear(&change);
return ret;
}
} else {
changeset_clear(&change);
return ret;
}
}
......
......@@ -105,18 +105,6 @@ void zone_free(zone_t **zone_ptr)
*zone_ptr = NULL;
}
int zone_change_commit(zone_contents_t *contents, list_t *chgs)
{
assert(contents && chgs);
if (EMPTY_LIST(*chgs)) {
return KNOT_EOK;
}
/* Apply DNSSEC changeset to the new zone. */
return apply_changesets_directly(contents, chgs);
}
int zone_change_store(zone_t *zone, list_t *chgs)
{
assert(zone);
......@@ -158,7 +146,8 @@ int zone_change_apply_and_store(list_t *chgs,
ret = zone_change_store(zone, chgs);
if (ret != KNOT_EOK) {
log_zone_error("%s Failed to store changesets.\n", msgpref);
update_rollback(chgs, &new_contents);
updates_rollback(chgs);
update_free_old_zone(&new_contents);
return ret;
}
......@@ -167,7 +156,7 @@ int zone_change_apply_and_store(list_t *chgs,
synchronize_rcu();
update_free_old_zone(&old_contents);
update_cleanup(chgs);
updates_cleanup(chgs);
return KNOT_EOK;
}
......
......@@ -92,10 +92,12 @@ void zone_free(zone_t **zone_ptr);
/*!
* \note Zone change API below, subject to change.
* \ref #223 New zone API
* \todo get rid of this
*/
int zone_change_commit(zone_contents_t *contents, list_t *chgs);
int zone_change_store(zone_t *zone, list_t *chgs);
/*! \note @mvavrusa Moved from zones.c, this needs a common API. */
/*! \note @mvavrusa Moved from zones.c, this needs a common API.
* \todo and this
*/
int zone_change_apply_and_store(list_t *chs,
zone_t *zone,
const char *msgpref);
......
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