changesets.c 7.46 KB
Newer Older
1
/*  Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
Lubos Slovak's avatar
Lubos Slovak committed
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 18 19
#include <string.h>
#include <stdlib.h>
#include <assert.h>
20
#include <stdarg.h>
21

22
#include "knot/updates/changesets.h"
Marek Vavrusa's avatar
Marek Vavrusa committed
23
#include "libknot/common.h"
24
#include "common/descriptor.h"
25 26
#include "common/mempattern.h"
#include "common/mempool.h"
27
#include "libknot/rrset.h"
28
#include "libknot/rrtype/soa.h"
29
#include "common/debug.h"
30

31 32 33 34 35
static int add_rr_to_zone(zone_contents_t *z, const knot_rrset_t *rrset)
{
	zone_node_t *n = NULL;
	int ret = zone_contents_add_rr(z, rrset, &n);
	UNUSED(n);
36
	return ret;
37 38
}

39
void changeset_init(changeset_t *ch, const knot_dname_t *apex, mm_ctx_t *mm)
40
{
41
	memset(ch, 0, sizeof(changeset_t));
42

43
	ch->mm = mm;
44

45 46 47
	// Init local changes
	ch->add = zone_contents_new(apex);
	ch->remove = zone_contents_new(apex);
Jan Kadlec's avatar
Jan Kadlec committed
48

49
	// Init change lists
50 51
	init_list(&ch->new_data);
	init_list(&ch->old_data);
52 53
}

54
changeset_t *changeset_new(mm_ctx_t *mm, const knot_dname_t *apex)
55
{
56 57 58
	changeset_t *ret = mm_alloc(mm, sizeof(changeset_t));
	if (ret == NULL) {
		ERR_ALLOC_FAILED;
59 60 61
		return NULL;
	}

62
	changeset_init(ret, apex, mm);
63
	return ret;
64 65
}

66
int changeset_add_rrset(changeset_t *ch, const knot_rrset_t *rrset)
67
{
68
	return add_rr_to_zone(ch->add, rrset);
69
}
70

71 72
int changeset_rem_rrset(changeset_t *ch, const knot_rrset_t *rrset)
{
73
	return add_rr_to_zone(ch->remove, rrset);
74 75
}

76
bool changeset_empty(const changeset_t *ch)
77
{
78
	if (ch == NULL || ch->add == NULL || ch->remove == NULL) {
79
		return true;
80 81
	}

82 83 84
	if (ch->soa_to) {
		return false;
	}
85

86 87
	changeset_iter_t itt;
	changeset_iter_all(&itt, ch, false);
88

89 90
	knot_rrset_t rr = changeset_iter_next(&itt);
	changeset_iter_clear(&itt);
91 92

	return knot_rrset_empty(&rr);
93 94
}

95
size_t changeset_size(const changeset_t *ch)
96
{
97
	if (ch == NULL) {
98 99
		return 0;
	}
100

101 102
	changeset_iter_t itt;
	changeset_iter_all(&itt, ch, false);
103 104

	size_t size = 0;
105
	knot_rrset_t rr = changeset_iter_next(&itt);
106 107
	while(!knot_rrset_empty(&rr)) {
		++size;
108
		rr = changeset_iter_next(&itt);
109
	}
110
	changeset_iter_clear(&itt);
111 112

	return size;
113 114
}

115
int changeset_merge(changeset_t *ch1, changeset_t *ch2)
116
{
117
#warning slow slow slow slow
118 119
	changeset_iter_t itt;
	changeset_iter_add(&itt, ch2, false);
120

121
	knot_rrset_t rrset = changeset_iter_next(&itt);
122
	while (!knot_rrset_empty(&rrset)) {
123
		int ret = changeset_add_rrset(ch1, &rrset);
124
		if (ret != KNOT_EOK) {
125 126
			changeset_iter_clear(&itt);
			return ret;
127
		}
128
		rrset = changeset_iter_next(&itt);
129
	}
130
	changeset_iter_clear(&itt);
131

132
	changeset_iter_add(&itt, ch2, false);
133

134
	rrset = changeset_iter_next(&itt);
135
	while (!knot_rrset_empty(&rrset)) {
136
		int ret = changeset_rem_rrset(ch1, &rrset);
137
		if (ret != KNOT_EOK) {
138 139
			changeset_iter_clear(&itt);
			return ret;
140
		}
141
		rrset = changeset_iter_next(&itt);
142
	}
143
	changeset_iter_clear(&itt);
144

145 146
	// Use soa_to and serial from the second changeset
	// soa_to from the first changeset is redundant, delete it
147
	knot_rrset_free(&ch1->soa_to, NULL);
148
	knot_rrset_free(&ch2->soa_from, NULL);
149
	ch1->soa_to = ch2->soa_to;
150
	ch2->soa_to = NULL;
151 152

	return KNOT_EOK;
153 154
}

155
void changeset_clear(changeset_t *ch)
156
{
157
	if (ch == NULL) {
158
		return;
159 160
	}

Jan Kadlec's avatar
Jan Kadlec committed
161
	// Delete RRSets in lists, in case there are any left
162 163
	zone_contents_deep_free(&ch->add);
	zone_contents_deep_free(&ch->remove);
164

165 166
	knot_rrset_free(&ch->soa_from, NULL);
	knot_rrset_free(&ch->soa_to, NULL);
Jan Kadlec's avatar
Jan Kadlec committed
167

168
	// Delete binary data
169
	free(ch->data);
170 171
}

172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
void changeset_free(changeset_t *ch)
{
	changeset_clear(ch);
	free(ch);
}

void changesets_clear(list_t *chgs)
{
	if (chgs) {
		changeset_t *chg, *nxt;
		WALK_LIST_DELSAFE(chg, nxt, *chgs) {
			changeset_clear(chg);
			rem_node(&chg->n);
		}
		init_list(chgs);
	}
}

void changesets_free(list_t *chgs)
191
{
192
	if (chgs) {
193 194
		changeset_t *chg, *nxt;
		WALK_LIST_DELSAFE(chg, nxt, *chgs) {
195
			rem_node(&chg->n);
196
			changeset_free(chg);
197
		}
198
		init_list(chgs);
199
	}
200 201
}

202
static void cleanup_iter_list(list_t *l)
203
{
204 205
	ptrnode_t *n, *nxt;
	WALK_LIST_DELSAFE(n, nxt, *l) {
206 207 208
		hattrie_iter_t *it = (hattrie_iter_t *)n->d;
		hattrie_iter_free(it);
		rem_node(&n->n);
209
		free(n);
210
	}
211
	init_list(l);
212
}
213

214 215
static int changeset_iter_init(changeset_iter_t *ch_it,
                               const changeset_t *ch, bool sorted, size_t tries, ...)
216
{
217 218
	memset(ch_it, 0, sizeof(*ch_it));
	init_list(&ch_it->iters);
219

220 221 222 223
	va_list args;
	va_start(args, tries);
	for (size_t i = 0; i < tries; ++i) {
		hattrie_t *t = va_arg(args, hattrie_t *);
224
		if (t) {
225 226 227
			if (sorted) {
				hattrie_build_index(t);
			}
228 229
			hattrie_iter_t *it = hattrie_iter_begin(t, sorted);
			if (it == NULL) {
230 231
				cleanup_iter_list(&ch_it->iters);
				return KNOT_ENOMEM;
232
			}
233 234 235
			if (ptrlist_add(&ch_it->iters, it, NULL) == NULL) {
				cleanup_iter_list(&ch_it->iters);
				return KNOT_ENOMEM;
236
			}
237
		}
238 239
	}

240 241 242
	va_end(args);

	return KNOT_EOK;
243 244
}

245
int changeset_iter_add(changeset_iter_t *itt, const changeset_t *ch, bool sorted)
246
{
247 248
	return changeset_iter_init(itt, ch, sorted, 2,
	                           ch->add->nodes, ch->add->nsec3_nodes);
249 250
}

251
int changeset_iter_rem(changeset_iter_t *itt, const changeset_t *ch, bool sorted)
252
{
253 254
	return changeset_iter_init(itt, ch, sorted, 2,
	                           ch->remove->nodes, ch->remove->nsec3_nodes);
255 256
}

257
int changeset_iter_all(changeset_iter_t *itt, const changeset_t *ch, bool sorted)
258
{
259 260 261
	return changeset_iter_init(itt, ch, sorted, 4,
	                           ch->add->nodes, ch->add->nsec3_nodes,
	                           ch->remove->nodes, ch->remove->nsec3_nodes);
262 263 264 265
}

static void iter_next_node(changeset_iter_t *ch_it, hattrie_iter_t *t_it)
{
266
	assert(!hattrie_iter_finished(t_it));
267 268 269 270 271 272 273 274 275 276
	// Get next node, but not for the very first call.
	if (ch_it->node) {
		hattrie_iter_next(t_it);
	}
	if (hattrie_iter_finished(t_it)) {
		ch_it->node = NULL;
		return;
	}

	ch_it->node = (zone_node_t *)*hattrie_iter_val(t_it);
277 278
	assert(ch_it->node);
	while (ch_it->node && ch_it->node->rrset_count == 0) {
279 280
		// Skip empty non-terminals.
		hattrie_iter_next(t_it);
281 282 283 284 285 286
		if (hattrie_iter_finished(t_it)) {
			ch_it->node = NULL;
		} else {
			ch_it->node = (zone_node_t *)*hattrie_iter_val(t_it);
			assert(ch_it->node);
		}
287 288 289 290 291 292 293 294 295
	}

	ch_it->node_pos = 0;
}

static knot_rrset_t get_next_rr(changeset_iter_t *ch_it, hattrie_iter_t *t_it) // pun intented
{
	if (ch_it->node == NULL || ch_it->node_pos == ch_it->node->rrset_count) {
		iter_next_node(ch_it, t_it);
296
		if (ch_it->node == NULL) {
297 298 299 300
			assert(hattrie_iter_finished(t_it));
			knot_rrset_t rr;
			knot_rrset_init_empty(&rr);
			return rr;
301 302
		}
	}
303
	
304
	return node_rrset_at(ch_it->node, ch_it->node_pos++);
305 306 307 308
}

knot_rrset_t changeset_iter_next(changeset_iter_t *it)
{
309
	assert(it);
310 311 312 313 314 315 316 317 318 319 320 321 322 323
	ptrnode_t *n = NULL;
	knot_rrset_t rr;
	knot_rrset_init_empty(&rr);
	WALK_LIST(n, it->iters) {
		hattrie_iter_t *t_it = (hattrie_iter_t *)n->d;
		if (hattrie_iter_finished(t_it)) {
			continue;
		}

		rr = get_next_rr(it, t_it);
		if (!knot_rrset_empty(&rr)) {
			// Got valid RRSet.
			return rr;
		}
324 325
	}

326
	return rr;
327 328
}

329
void changeset_iter_clear(changeset_iter_t *it)
330
{
331
	if (it) {
332
		cleanup_iter_list(&it->iters);
333 334
		it->node = NULL;
		it->node_pos = 0;
335
	}
336 337
}