zone-update.c 21.3 KB
Newer Older
1
/*  Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

    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/>.
 */

17
#include "knot/common/log.h"
18
#include "knot/dnssec/zone-events.h"
Daniel Salzman's avatar
Daniel Salzman committed
19
#include "knot/updates/zone-update.h"
20
#include "knot/zone/serial.h"
21
#include "knot/zone/zone-diff.h"
22
#include "contrib/mempattern.h"
23
#include "contrib/trim.h"
24
#include "contrib/ucw/lists.h"
25
#include "contrib/ucw/mempool.h"
26

27 28
#include <urcu.h>

29 30
static int init_incremental(zone_update_t *update, zone_t *zone)
{
31 32 33
	if (zone->contents == NULL) {
		return KNOT_EINVAL;
	}
34

35 36 37 38
	int ret = changeset_init(&update->change, zone->name);
	if (ret != KNOT_EOK) {
		return ret;
	}
39

40 41
	update->new_cont_deep_copy = false;

42 43 44 45 46 47
	ret = apply_prepare_zone_copy(zone->contents, &update->new_cont);
	if (ret != KNOT_EOK) {
		changeset_clear(&update->change);
		return ret;
	}

48
	uint32_t apply_flags = update->flags & UPDATE_STRICT ? APPLY_STRICT : 0;
49
	apply_init_ctx(update->a_ctx, update->new_cont, apply_flags);
50

51
	/* Copy base SOA RR. */
52
	update->change.soa_from =
53
		node_create_rrset(zone->contents->apex, KNOT_RRTYPE_SOA);
54
	if (update->change.soa_from == NULL) {
55
		zone_contents_free(update->new_cont);
56
		changeset_clear(&update->change);
57 58
		return KNOT_ENOMEM;
	}
59 60 61 62 63 64 65 66 67 68 69

	return KNOT_EOK;
}

static int init_full(zone_update_t *update, zone_t *zone)
{
	update->new_cont = zone_contents_new(zone->name);
	if (update->new_cont == NULL) {
		return KNOT_ENOMEM;
	}

70 71
	update->new_cont_deep_copy = true;

72
	apply_init_ctx(update->a_ctx, update->new_cont, 0);
73

74 75 76
	return KNOT_EOK;
}

77 78 79 80 81 82 83 84 85 86 87 88 89 90
static int replace_soa(zone_contents_t *contents, const knot_rrset_t *rr)
{
	/* SOA possible only within apex. */
	if (!knot_dname_is_equal(rr->owner, contents->apex->owner)) {
		return KNOT_EDENIED;
	}

	knot_rrset_t old_soa = node_rrset(contents->apex, KNOT_RRTYPE_SOA);
	zone_node_t *n = contents->apex;
	int ret = zone_contents_remove_rr(contents, &old_soa, &n);
	if (ret != KNOT_EOK && ret != KNOT_EINVAL) {
		return ret;
	}

91 92 93 94 95 96
	ret = zone_contents_add_rr(contents, rr, &n);
	if (ret == KNOT_ETTL) {
		return KNOT_EOK;
	}

	return ret;
97 98
}

99 100 101 102 103 104 105 106 107 108 109 110 111 112
/* ------------------------------- API -------------------------------------- */

int zone_update_init(zone_update_t *update, zone_t *zone, zone_update_flags_t flags)
{
	if (update == NULL || zone == NULL) {
		return KNOT_EINVAL;
	}

	memset(update, 0, sizeof(*update));
	update->zone = zone;

	mm_ctx_mempool(&update->mm, MM_DEFAULT_BLKSIZE);
	update->flags = flags;

113 114 115 116 117 118
	update->a_ctx = calloc(1, sizeof(*update->a_ctx));
	if (update->a_ctx == NULL) {
		return KNOT_ENOMEM;
	}

	int ret = KNOT_EINVAL;
119
	if (flags & UPDATE_INCREMENTAL) {
120
		ret = init_incremental(update, zone);
121
	} else if (flags & UPDATE_FULL) {
122 123 124 125
		ret = init_full(update, zone);
	}
	if (ret != KNOT_EOK) {
		free(update->a_ctx);
126
	}
127 128

	return ret;
129 130
}

131
int zone_update_from_differences(zone_update_t *update, zone_t *zone, zone_contents_t *old_cont,
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
                                 zone_contents_t *new_cont, zone_update_flags_t flags)
{
	if (update == NULL || zone == NULL || new_cont == NULL ||
	    !(flags & UPDATE_INCREMENTAL) || (flags & UPDATE_FULL)) {
		return KNOT_EINVAL;
	}

	memset(update, 0, sizeof(*update));
	update->zone = zone;

	mm_ctx_mempool(&update->mm, MM_DEFAULT_BLKSIZE);
	update->flags = flags;

	update->new_cont = new_cont;
	update->new_cont_deep_copy = true;

148 149 150 151 152
	update->a_ctx = calloc(1, sizeof(*update->a_ctx));
	if (update->a_ctx == NULL) {
		return KNOT_ENOMEM;
	}

153 154
	int ret = changeset_init(&update->change, zone->name);
	if (ret != KNOT_EOK) {
155
		free(update->a_ctx);
156 157 158
		return ret;
	}

159
	ret = zone_contents_diff(old_cont, new_cont, &update->change);
160
	if (ret != KNOT_EOK && ret != KNOT_ENODIFF && ret != KNOT_ESEMCHECK) {
161
		free(update->a_ctx);
162 163 164 165 166
		changeset_clear(&update->change);
		return ret;
	}

	uint32_t apply_flags = update->flags & UPDATE_STRICT ? APPLY_STRICT : 0;
167
	apply_init_ctx(update->a_ctx, update->new_cont, apply_flags);
168

169 170 171 172 173 174 175 176 177
	if (ret == KNOT_ESEMCHECK) {
		ret = zone_update_increment_soa(update, conf());
		if (ret != KNOT_EOK) {
			zone_update_clear(update);
			return ret;
		}
		log_zone_info(zone->name, "automatic SOA serial increment");
	}

178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
	return KNOT_EOK;
}

int zone_update_from_contents(zone_update_t *update, zone_t *zone_without_contents,
                              zone_contents_t *new_cont, zone_update_flags_t flags)
{
	if (update == NULL || zone_without_contents == NULL || new_cont == NULL) {
		return KNOT_EINVAL;
	}

	memset(update, 0, sizeof(*update));
	update->zone = zone_without_contents;

	mm_ctx_mempool(&update->mm, MM_DEFAULT_BLKSIZE);
	update->flags = flags;

	update->new_cont = new_cont;
	update->new_cont_deep_copy = true;

197 198 199 200 201
	update->a_ctx = calloc(1, sizeof(*update->a_ctx));
	if (update->a_ctx == NULL) {
		return KNOT_ENOMEM;
	}

202 203 204
	if (flags & UPDATE_INCREMENTAL) {
		int ret = changeset_init(&update->change, zone_without_contents->name);
		if (ret != KNOT_EOK) {
205
			free(update->a_ctx);
206 207 208 209 210 211
			return ret;
		}

		update->change.soa_from = node_create_rrset(new_cont->apex, KNOT_RRTYPE_SOA);
		if (update->change.soa_from == NULL) {
			changeset_clear(&update->change);
212
			free(update->a_ctx);
213 214 215 216 217
			return KNOT_ENOMEM;
		}
	}

	uint32_t apply_flags = update->flags & UPDATE_STRICT ? APPLY_STRICT : 0;
218
	apply_init_ctx(update->a_ctx, update->new_cont, apply_flags);
219 220 221 222

	return KNOT_EOK;
}

223 224 225 226 227 228
const zone_node_t *zone_update_get_node(zone_update_t *update, const knot_dname_t *dname)
{
	if (update == NULL || dname == NULL) {
		return NULL;
	}

229
	return zone_contents_find_node(update->new_cont, dname);
230 231
}

232 233
const zone_node_t *zone_update_get_apex(zone_update_t *update)
{
234 235 236 237
	if (update == NULL) {
		return NULL;
	}

238 239 240 241 242 243
	return zone_update_get_node(update, update->zone->name);
}

uint32_t zone_update_current_serial(zone_update_t *update)
{
	const zone_node_t *apex = zone_update_get_apex(update);
244
	if (apex != NULL) {
245
		return knot_soa_serial(node_rdataset(apex, KNOT_RRTYPE_SOA)->rdata);
246 247 248 249 250 251 252
	} else {
		return 0;
	}
}

const knot_rdataset_t *zone_update_from(zone_update_t *update)
{
253 254 255 256
	if (update == NULL) {
		return NULL;
	}

257 258 259 260 261 262
	if (update->flags & UPDATE_INCREMENTAL) {
		const zone_node_t *apex = update->zone->contents->apex;
		return node_rdataset(apex, KNOT_RRTYPE_SOA);
	}

	return NULL;
263 264 265 266
}

const knot_rdataset_t *zone_update_to(zone_update_t *update)
{
267 268 269
	if (update == NULL) {
		return NULL;
	}
270

271 272 273 274 275 276 277 278
	if (update->flags & UPDATE_FULL) {
		const zone_node_t *apex = update->new_cont->apex;
		return node_rdataset(apex, KNOT_RRTYPE_SOA);
	} else if (update->flags & UPDATE_INCREMENTAL) {
		if (update->change.soa_to == NULL) {
			return NULL;
		}
		return &update->change.soa_to->rrs;
279 280
	}

281
	return NULL;
282 283
}

284 285
void zone_update_clear(zone_update_t *update)
{
286 287
	if (update == NULL) {
		return;
288
	}
289 290 291

	if (update->flags & UPDATE_INCREMENTAL) {
		/* Revert any changes on error, do nothing on success. */
292
		if (update->new_cont_deep_copy) {
293
			update_cleanup(update->a_ctx);
294
			zone_contents_deep_free(update->new_cont);
295
		} else {
296
			update_rollback(update->a_ctx);
297
			update_free_zone(update->new_cont);
298
		}
299 300
		changeset_clear(&update->change);
	} else if (update->flags & UPDATE_FULL) {
301
		assert(update->new_cont_deep_copy);
302
		zone_contents_deep_free(update->new_cont);
303
	}
304
	free(update->a_ctx);
305 306
	mp_delete(update->mm.ctx);
	memset(update, 0, sizeof(*update));
307
}
308

309 310
int zone_update_add(zone_update_t *update, const knot_rrset_t *rrset)
{
311
	if (update == NULL || rrset == NULL) {
312 313 314
		return KNOT_EINVAL;
	}

315
	if (update->flags & UPDATE_INCREMENTAL) {
316 317 318 319 320 321 322
		int ret = changeset_add_addition(&update->change, rrset, CHANGESET_CHECK);
		if (ret != KNOT_EOK) {
			return ret;
		}

		if (rrset->type == KNOT_RRTYPE_SOA) {
			/* replace previous SOA */
323
			ret = apply_replace_soa(update->a_ctx, &update->change);
324 325 326 327 328 329
			if (ret != KNOT_EOK) {
				changeset_remove_addition(&update->change, rrset);
			}
			return ret;
		}

330
		ret = apply_add_rr(update->a_ctx, rrset);
331 332 333 334 335 336
		if (ret != KNOT_EOK) {
			changeset_remove_addition(&update->change, rrset);
			return ret;
		}

		return KNOT_EOK;
337
	} else if (update->flags & UPDATE_FULL) {
338 339 340 341 342
		if (rrset->type == KNOT_RRTYPE_SOA) {
			/* replace previous SOA */
			return replace_soa(update->new_cont, rrset);
		}

343
		zone_node_t *n = NULL;
344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
		int ret = zone_contents_add_rr(update->new_cont, rrset, &n);
		if (ret == KNOT_ETTL) {
			char buff[KNOT_DNAME_TXT_MAXLEN + 1];
			char *owner = knot_dname_to_str(buff, rrset->owner, sizeof(buff));
			if (owner == NULL) {
				owner = "";
			}
			char type[16] = { '\0' };
			knot_rrtype_to_string(rrset->type, type, sizeof(type));
			log_zone_notice(update->new_cont->apex->owner,
			                "TTL mismatch, owner %s, type %s, "
			                "TTL set to %u", owner, type, rrset->ttl);
			return KNOT_EOK;
		}

		return ret;
360 361 362 363 364 365 366
	} else {
		return KNOT_EINVAL;
	}
}

int zone_update_remove(zone_update_t *update, const knot_rrset_t *rrset)
{
367
	if (update == NULL || rrset == NULL) {
368 369 370
		return KNOT_EINVAL;
	}

371
	if (update->flags & UPDATE_INCREMENTAL) {
372 373 374 375 376 377 378 379 380 381
		int ret = changeset_add_removal(&update->change, rrset, CHANGESET_CHECK);
		if (ret != KNOT_EOK) {
			return ret;
		}

		if (rrset->type == KNOT_RRTYPE_SOA) {
			/* SOA is replaced with addition */
			return KNOT_EOK;
		}

382
		ret = apply_remove_rr(update->a_ctx, rrset);
383 384 385 386 387 388
		if (ret != KNOT_EOK) {
			changeset_remove_removal(&update->change, rrset);
			return ret;
		}

		return KNOT_EOK;
389 390
	} else if (update->flags & UPDATE_FULL) {
		zone_node_t *n = NULL;
391 392
		knot_rrset_t *rrs_copy = knot_rrset_copy(rrset, &update->mm);
		int ret = zone_contents_remove_rr(update->new_cont, rrs_copy, &n);
393
		knot_rrset_free(rrs_copy, &update->mm);
394
		return ret;
395
	} else {
396
		return KNOT_EINVAL;
397 398 399
	}
}

400 401 402 403 404 405 406 407
int zone_update_remove_rrset(zone_update_t *update, knot_dname_t *owner, uint16_t type)
{
	if (update == NULL || owner == NULL) {
		return KNOT_EINVAL;
	}

	if (update->flags & UPDATE_INCREMENTAL) {
		/* Remove the RRSet from the original node */
408
		const zone_node_t *node = zone_contents_find_node(update->new_cont, owner);
409 410
		if (node != NULL) {
			knot_rrset_t rrset = node_rrset(node, type);
411 412 413
			if (rrset.owner == NULL) {
				return KNOT_ENOENT;
			}
Daniel Salzman's avatar
Daniel Salzman committed
414 415
			int ret = changeset_add_removal(&update->change, &rrset,
			                                CHANGESET_CHECK);
416 417 418 419
			if (ret != KNOT_EOK) {
				return ret;
			}

420 421 422 423 424
			if (type == KNOT_RRTYPE_SOA) {
				/* SOA is replaced with addition */
				return KNOT_EOK;
			}

425
			ret = apply_remove_rr(update->a_ctx, &rrset);
426 427 428
			if (ret != KNOT_EOK) {
				return ret;
			}
429
		} else {
430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455
			return KNOT_ENONODE;
		}
	} else if (update->flags & UPDATE_FULL) {
		/* Remove the RRSet from the non-synthesized new node */
		const zone_node_t *node = zone_contents_find_node(update->new_cont, owner);
		if (node == NULL) {
			return KNOT_ENONODE;
		}

		knot_rrset_t rrset = node_rrset(node, type);
		int ret = zone_update_remove(update, &rrset);
		if (ret != KNOT_EOK) {
			return ret;
		}
	}

	return KNOT_EOK;
}

int zone_update_remove_node(zone_update_t *update, const knot_dname_t *owner)
{
	if (update == NULL || owner == NULL) {
		return KNOT_EINVAL;
	}

	if (update->flags & UPDATE_INCREMENTAL) {
456 457
		/* Remove all RRSets from the new node */
		const zone_node_t *node = zone_contents_find_node(update->new_cont, owner);
458 459 460 461
		if (node != NULL) {
			size_t rrset_count = node->rrset_count;
			for (int i = 0; i < rrset_count; ++i) {
				knot_rrset_t rrset = node_rrset_at(node, rrset_count - 1 - i);
Daniel Salzman's avatar
Daniel Salzman committed
462 463
				int ret = changeset_add_removal(&update->change, &rrset,
				                                CHANGESET_CHECK);
464 465 466 467
				if (ret != KNOT_EOK) {
					return ret;
				}

468 469
				if (rrset.type == KNOT_RRTYPE_SOA) {
					/* SOA is replaced with addition */
470
					continue;
471 472
				}

473
				ret = apply_remove_rr(update->a_ctx, &rrset);
474 475 476 477
				if (ret != KNOT_EOK) {
					return ret;
				}
			}
478
		} else {
479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500
			return KNOT_ENONODE;
		}
	} else if (update->flags & UPDATE_FULL) {
		/* Remove all RRSets from the non-synthesized new node */
		const zone_node_t *node = zone_contents_find_node(update->new_cont, owner);
		if (node == NULL) {
			return KNOT_ENONODE;
		}

		size_t rrset_count = node->rrset_count;
		for (int i = 0; i < rrset_count; ++i) {
			knot_rrset_t rrset = node_rrset_at(node, rrset_count - 1 - i);
			int ret = zone_update_remove(update, &rrset);
			if (ret != KNOT_EOK) {
				return ret;
			}
		}
	}

	return KNOT_EOK;
}

501 502 503 504
int zone_update_apply_changeset(zone_update_t *update, const changeset_t *changes)
{
	int ret = KNOT_EOK;
	if (update->flags & UPDATE_INCREMENTAL) {
505
		ret = changeset_merge(&update->change, changes, CHANGESET_CHECK_CANCELOUT);
506 507
	}
	if (ret == KNOT_EOK) {
508
		ret = apply_changeset_directly(update->a_ctx, changes);
509 510 511 512 513 514 515 516 517 518
	}
	return ret;
}

int zone_update_apply_changeset_fix(zone_update_t *update, changeset_t *changes)
{
	int ret = changeset_cancelout(changes);
	if (ret == KNOT_EOK) {
		ret = changeset_preapply_fix(update->new_cont, changes);
	}
519 520
	if (ret == KNOT_EOK) {
		ret = zone_update_apply_changeset(update, changes);
521
	}
522
	return ret;
523 524
}

525
static int set_new_soa(zone_update_t *update, unsigned serial_policy)
526
{
527 528
	assert(update);

Daniel Salzman's avatar
Daniel Salzman committed
529 530
	knot_rrset_t *soa_cpy = node_create_rrset(zone_update_get_apex(update),
	                                          KNOT_RRTYPE_SOA);
531 532 533 534
	if (soa_cpy == NULL) {
		return KNOT_ENOMEM;
	}

535 536
	int ret = zone_update_remove(update, soa_cpy);
	if (ret != KNOT_EOK) {
537
		knot_rrset_free(soa_cpy, NULL);
538
		return ret;
539 540
	}

541
	uint32_t old_serial = knot_soa_serial(soa_cpy->rrs.rdata);
542
	uint32_t new_serial = serial_next(old_serial, serial_policy);
543
	if (serial_compare(old_serial, new_serial) != SERIAL_LOWER) {
544
		log_zone_warning(update->zone->name, "updated SOA serial is lower "
545
		                 "than current, serial %u -> %u",
546
		                 old_serial, new_serial);
547 548
		ret = KNOT_ESOAINVAL;
	} else {
549
		knot_soa_serial_set(soa_cpy->rrs.rdata, new_serial);
550

551 552
		ret = zone_update_add(update, soa_cpy);
	}
553
	knot_rrset_free(soa_cpy, NULL);
554 555 556 557 558 559 560 561 562 563 564 565

	return ret;
}

int zone_update_increment_soa(zone_update_t *update, conf_t *conf)
{
	if (update == NULL || conf == NULL) {
		return KNOT_EINVAL;
	}

	conf_val_t val = conf_zone_get(conf, C_SERIAL_POLICY, update->zone->name);
	return set_new_soa(update, conf_opt(&val));
566 567
}

Daniel Salzman's avatar
Daniel Salzman committed
568 569
static int commit_incremental(conf_t *conf, zone_update_t *update,
                              zone_contents_t **contents_out)
570
{
571
	assert(update);
572
	assert(contents_out);
573

574 575
	if (changeset_empty(&update->change)) {
		changeset_clear(&update->change);
576
		if (update->zone->contents == NULL || update->new_cont_deep_copy) {
577 578
			*contents_out = update->new_cont;
		}
579 580 581
		return KNOT_EOK;
	}

582
	zone_contents_t *new_contents = update->new_cont;
583
	int ret = KNOT_EOK;
584
	if (zone_update_to(update) == NULL) {
585
		/* No SOA in the update, create one according to the current policy */
586
		ret = zone_update_increment_soa(update, conf);
587
		if (ret != KNOT_EOK) {
588
			zone_update_clear(update);
589 590
			return ret;
		}
591
	}
592

593
	ret = apply_finalize(update->a_ctx);
594 595 596 597
	if (ret != KNOT_EOK) {
		zone_update_clear(update);
		return ret;
	}
598

599
	/* Write changes to journal if all went well. */
600 601
	conf_val_t val = conf_zone_get(conf, C_JOURNAL_CONTENT, update->zone->name);
	if (conf_opt(&val) != JOURNAL_CONTENT_NONE) {
602
		ret = zone_change_store(conf, update->zone, &update->change);
603
		if (ret != KNOT_EOK) {
604 605
			return ret;
		}
606
	}
607

608 609
	*contents_out = new_contents;

610 611 612
	return KNOT_EOK;
}

613 614
static int commit_full(conf_t *conf, zone_update_t *update, zone_contents_t **contents_out)
{
615 616 617 618 619 620 621 622 623
	assert(update);
	assert(contents_out);

	/* Check if we have SOA. We might consider adding full semantic check here.
	 * But if we wanted full sem-check I'd consider being it controlled by a flag
	 * - to enable/disable it on demand. */
	if (!node_rrtype_exists(update->new_cont->apex, KNOT_RRTYPE_SOA)) {
		return KNOT_ESEMCHECK;
	}
624

625 626 627 628 629
	int ret = zone_contents_adjust_full(update->new_cont);
	if (ret != KNOT_EOK) {
		zone_update_clear(update);
		return ret;
	}
630

631 632 633
	/* Store new zone contents in journal. */
	conf_val_t val = conf_zone_get(conf, C_JOURNAL_CONTENT, update->zone->name);
	if (conf_opt(&val) == JOURNAL_CONTENT_ALL) {
634
		ret = zone_in_journal_store(conf, update->zone, update->new_cont);
635
		if (ret != KNOT_EOK) {
636 637 638 639 640
			return ret;
		}
	}

	*contents_out = update->new_cont;
641

642 643 644
	return KNOT_EOK;
}

645 646
/*! \brief Routine for calling call_rcu() easier way.
 *
647
 * Consider moving elsewhere, as it has no direct relation to zone-update.
648 649 650
 */
typedef struct {
	struct rcu_head rcuhead;
651 652
	void (*callback)(void *);
	void *ctx;
653
	bool free_ctx;
654 655 656 657
} callrcu_wrapper_t;

static void callrcu_wrapper_cb(struct rcu_head *param)
{
658
	callrcu_wrapper_t *wrap = (callrcu_wrapper_t *)param;
659 660
	wrap->callback(wrap->ctx);
	if (wrap->free_ctx) {
661 662
		free(wrap->ctx);
	}
663
	free(wrap);
664 665 666

	// Trim extra heap.
	mem_trim();
667 668
}

669
/* NOTE: Does nothing if not enough memory. */
670
static void callrcu_wrapper(void *ctx, void *callback, bool free_ctx)
671
{
672
	callrcu_wrapper_t *wrap = calloc(1, sizeof(callrcu_wrapper_t));
673
	if (wrap != NULL) {
674 675
		wrap->callback = callback;
		wrap->ctx = ctx;
676
		wrap->free_ctx = free_ctx;
677 678 679 680
		call_rcu((struct rcu_head *)wrap, callrcu_wrapper_cb);
	}
}

681
int zone_update_commit(conf_t *conf, zone_update_t *update)
682
{
683
	if (conf == NULL || update == NULL) {
684 685 686 687 688
		return KNOT_EINVAL;
	}

	int ret = KNOT_EOK;
	zone_contents_t *new_contents = NULL;
689
	if (update->flags & UPDATE_INCREMENTAL) {
690 691
		ret = commit_incremental(conf, update, &new_contents);
	} else {
692
		ret = commit_full(conf, update, &new_contents);
693 694 695
	}
	if (ret != KNOT_EOK) {
		return ret;
696 697
	}

698 699 700 701 702 703
	/* If there is anything to change. */
	if (new_contents == NULL) {
		return KNOT_EOK;
	}

	/* Check the zone size. */
704
	conf_val_t val = conf_zone_get(conf, C_MAX_ZONE_SIZE, update->zone->name);
705
	size_t size_limit = conf_int(&val);
706

707 708
	if (new_contents->size > size_limit) {
		/* Recoverable error. */
709 710 711
		return KNOT_EZONESIZE;
	}

712
	/* Switch zone contents. */
713 714
	zone_contents_t *old_contents;
	old_contents = zone_switch_contents(update->zone, new_contents);
715 716 717

	/* Sync RCU. */
	if (update->flags & UPDATE_FULL) {
718
		assert(update->new_cont_deep_copy);
719
		callrcu_wrapper(old_contents, zone_contents_deep_free, false);
720
	} else if (update->flags & UPDATE_INCREMENTAL) {
721
		if (update->new_cont_deep_copy) {
722
			callrcu_wrapper(old_contents, zone_contents_deep_free, false);
723
		} else {
724
			callrcu_wrapper(old_contents, update_free_zone, false);
725
		}
726
		changeset_clear(&update->change);
727
	}
728
	callrcu_wrapper(update->a_ctx, update_cleanup, true);
729
	update->a_ctx = NULL;
730
	update->new_cont = NULL;
731

732 733 734 735 736 737
	/* Sync zonefile immediately if configured. */
	val = conf_zone_get(conf, C_ZONEFILE_SYNC, update->zone->name);
	if (conf_int(&val) == 0) {
		zone_events_schedule_now(update->zone, ZONE_EVENT_FLUSH);
	}

738
	return KNOT_EOK;
739 740
}

741 742 743 744
static int iter_init_tree_iters(zone_update_iter_t *it, zone_update_t *update,
                                bool nsec3)
{
	/* Set zone iterator. */
745
	zone_contents_t *_contents = update->new_cont;
746 747

	/* Begin iteration. We can safely assume _contents is a valid pointer. */
748
	zone_tree_t *tree = nsec3 ? _contents->nsec3_nodes : _contents->nodes;
749
	it->tree_it = trie_it_begin(tree);
750
	if (it->tree_it == NULL) {
751 752 753
		return KNOT_ENOMEM;
	}

754
	it->cur_node = (zone_node_t *)(*trie_it_val(it->tree_it));
755

756 757 758
	return KNOT_EOK;
}

759
static int iter_get_next_node(zone_update_iter_t *it)
760
{
761 762 763
	trie_it_next(it->tree_it);
	if (trie_it_finished(it->tree_it)) {
		trie_it_free(it->tree_it);
764 765
		it->tree_it = NULL;
		it->cur_node = NULL;
766 767 768
		return KNOT_ENOENT;
	}

769
	it->cur_node = (zone_node_t *)(*trie_it_val(it->tree_it));
770 771 772 773 774 775 776 777 778 779 780 781 782 783 784

	return KNOT_EOK;
}

static int iter_init(zone_update_iter_t *it, zone_update_t *update, const bool nsec3)
{
	memset(it, 0, sizeof(*it));

	it->update = update;
	it->nsec3 = nsec3;
	int ret = iter_init_tree_iters(it, update, nsec3);
	if (ret != KNOT_EOK) {
		return ret;
	}

785
	it->cur_node = (zone_node_t *)(*trie_it_val(it->tree_it));
786 787 788 789 790 791

	return KNOT_EOK;
}

int zone_update_iter(zone_update_iter_t *it, zone_update_t *update)
{
792 793 794 795
	if (it == NULL || update == NULL) {
		return KNOT_EINVAL;
	}

796 797 798 799 800
	return iter_init(it, update, false);
}

int zone_update_iter_nsec3(zone_update_iter_t *it, zone_update_t *update)
{
801 802 803 804
	if (it == NULL || update == NULL) {
		return KNOT_EINVAL;
	}

805 806
	if (update->flags & UPDATE_FULL) {
		if (update->new_cont->nsec3_nodes == NULL) {
807
			/* No NSEC3 tree. */
808 809 810 811 812
			return KNOT_ENOENT;
		}
	} else {
		if (update->change.add->nsec3_nodes == NULL &&
		    update->change.remove->nsec3_nodes == NULL) {
813
			/* No NSEC3 changes. */
814 815 816 817 818 819 820 821 822 823 824 825 826
			return KNOT_ENOENT;
		}
	}

	return iter_init(it, update, true);
}

int zone_update_iter_next(zone_update_iter_t *it)
{
	if (it == NULL) {
		return KNOT_EINVAL;
	}

827 828
	if (it->tree_it != NULL) {
		int ret = iter_get_next_node(it);
829 830 831 832 833 834 835 836 837 838
		if (ret != KNOT_EOK && ret != KNOT_ENOENT) {
			return ret;
		}
	}

	return KNOT_EOK;
}

const zone_node_t *zone_update_iter_val(zone_update_iter_t *it)
{
839
	if (it != NULL) {
840
		return it->cur_node;
841 842 843 844 845
	} else {
		return NULL;
	}
}

846
void zone_update_iter_finish(zone_update_iter_t *it)
847
{
848
	if (it == NULL) {
849
		return;
850 851
	}

852
	trie_it_free(it->tree_it);
853 854
}

855
bool zone_update_no_change(zone_update_t *update)
856
{
857 858 859 860
	if (update == NULL) {
		return true;
	}

861 862 863
	if (update->flags & UPDATE_INCREMENTAL) {
		return changeset_empty(&update->change);
	} else {
864 865 866
		/* This branch does not make much sense and FULL update will most likely
		 * be a change every time anyway, just return false. */
		return false;
867
	}
868
}