/src/bind9/lib/dns/journal.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (C) Internet Systems Consortium, Inc. ("ISC") |
3 | | * |
4 | | * SPDX-License-Identifier: MPL-2.0 |
5 | | * |
6 | | * This Source Code Form is subject to the terms of the Mozilla Public |
7 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
8 | | * file, you can obtain one at https://mozilla.org/MPL/2.0/. |
9 | | * |
10 | | * See the COPYRIGHT file distributed with this work for additional |
11 | | * information regarding copyright ownership. |
12 | | */ |
13 | | |
14 | | #include <errno.h> |
15 | | #include <inttypes.h> |
16 | | #include <stdbool.h> |
17 | | #include <stdlib.h> |
18 | | #include <unistd.h> |
19 | | |
20 | | #include <isc/dir.h> |
21 | | #include <isc/file.h> |
22 | | #include <isc/mem.h> |
23 | | #include <isc/result.h> |
24 | | #include <isc/serial.h> |
25 | | #include <isc/stdio.h> |
26 | | #include <isc/string.h> |
27 | | #include <isc/util.h> |
28 | | |
29 | | #include <dns/compress.h> |
30 | | #include <dns/db.h> |
31 | | #include <dns/dbiterator.h> |
32 | | #include <dns/diff.h> |
33 | | #include <dns/fixedname.h> |
34 | | #include <dns/journal.h> |
35 | | #include <dns/log.h> |
36 | | #include <dns/rdataset.h> |
37 | | #include <dns/rdatasetiter.h> |
38 | | #include <dns/soa.h> |
39 | | |
40 | | /*! \file |
41 | | * \brief Journaling. |
42 | | * |
43 | | * A journal file consists of |
44 | | * |
45 | | * \li A fixed-size header of type journal_rawheader_t. |
46 | | * |
47 | | * \li The index. This is an unordered array of index entries |
48 | | * of type journal_rawpos_t giving the locations |
49 | | * of some arbitrary subset of the journal's addressable |
50 | | * transactions. The index entries are used as hints to |
51 | | * speed up the process of locating a transaction with a given |
52 | | * serial number. Unused index entries have an "offset" |
53 | | * field of zero. The size of the index can vary between |
54 | | * journal files, but does not change during the lifetime |
55 | | * of a file. The size can be zero. |
56 | | * |
57 | | * \li The journal data. This consists of one or more transactions. |
58 | | * Each transaction begins with a transaction header of type |
59 | | * journal_rawxhdr_t. The transaction header is followed by a |
60 | | * sequence of RRs, similar in structure to an IXFR difference |
61 | | * sequence (RFC1995). That is, the pre-transaction SOA, |
62 | | * zero or more other deleted RRs, the post-transaction SOA, |
63 | | * and zero or more other added RRs. Unlike in IXFR, each RR |
64 | | * is prefixed with a 32-bit length. |
65 | | * |
66 | | * The journal data part grows as new transactions are |
67 | | * appended to the file. Only those transactions |
68 | | * whose serial number is current-(2^31-1) to current |
69 | | * are considered "addressable" and may be pointed |
70 | | * to from the header or index. They may be preceded |
71 | | * by old transactions that are no longer addressable, |
72 | | * and they may be followed by transactions that were |
73 | | * appended to the journal but never committed by updating |
74 | | * the "end" position in the header. The latter will |
75 | | * be overwritten when new transactions are added. |
76 | | */ |
77 | | |
78 | | /**************************************************************************/ |
79 | | /* |
80 | | * Miscellaneous utilities. |
81 | | */ |
82 | | |
83 | | #define JOURNAL_COMMON_LOGARGS \ |
84 | 0 | dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_JOURNAL |
85 | | |
86 | 0 | #define JOURNAL_DEBUG_LOGARGS(n) JOURNAL_COMMON_LOGARGS, ISC_LOG_DEBUG(n) |
87 | | |
88 | | /*% |
89 | | * It would be non-sensical (or at least obtuse) to use FAIL() with an |
90 | | * ISC_R_SUCCESS code, but the test is there to keep the Solaris compiler |
91 | | * from complaining about "end-of-loop code not reached". |
92 | | */ |
93 | | #define FAIL(code) \ |
94 | 4 | do { \ |
95 | 4 | result = (code); \ |
96 | 4 | if (result != ISC_R_SUCCESS) \ |
97 | 4 | goto failure; \ |
98 | 4 | } while (0) |
99 | | |
100 | | #define CHECK(op) \ |
101 | 0 | do { \ |
102 | 0 | result = (op); \ |
103 | 0 | if (result != ISC_R_SUCCESS) \ |
104 | 0 | goto failure; \ |
105 | 0 | } while (0) |
106 | | |
107 | 0 | #define JOURNAL_SERIALSET 0x01U |
108 | | |
109 | | static isc_result_t |
110 | | index_to_disk(dns_journal_t *); |
111 | | |
112 | | static uint32_t |
113 | 0 | decode_uint32(unsigned char *p) { |
114 | 0 | return (((uint32_t)p[0] << 24) + ((uint32_t)p[1] << 16) + |
115 | 0 | ((uint32_t)p[2] << 8) + ((uint32_t)p[3] << 0)); |
116 | 0 | } |
117 | | |
118 | | static void |
119 | 0 | encode_uint32(uint32_t val, unsigned char *p) { |
120 | 0 | p[0] = (uint8_t)(val >> 24); |
121 | 0 | p[1] = (uint8_t)(val >> 16); |
122 | 0 | p[2] = (uint8_t)(val >> 8); |
123 | 0 | p[3] = (uint8_t)(val >> 0); |
124 | 0 | } |
125 | | |
126 | | isc_result_t |
127 | | dns_db_createsoatuple(dns_db_t *db, dns_dbversion_t *ver, isc_mem_t *mctx, |
128 | 0 | dns_diffop_t op, dns_difftuple_t **tp) { |
129 | 0 | isc_result_t result; |
130 | 0 | dns_dbnode_t *node; |
131 | 0 | dns_rdataset_t rdataset; |
132 | 0 | dns_rdata_t rdata = DNS_RDATA_INIT; |
133 | 0 | dns_fixedname_t fixed; |
134 | 0 | dns_name_t *zonename; |
135 | |
|
136 | 0 | zonename = dns_fixedname_initname(&fixed); |
137 | 0 | dns_name_copy(dns_db_origin(db), zonename); |
138 | |
|
139 | 0 | node = NULL; |
140 | 0 | result = dns_db_findnode(db, zonename, false, &node); |
141 | 0 | if (result != ISC_R_SUCCESS) { |
142 | 0 | goto nonode; |
143 | 0 | } |
144 | | |
145 | 0 | dns_rdataset_init(&rdataset); |
146 | 0 | result = dns_db_findrdataset(db, node, ver, dns_rdatatype_soa, 0, |
147 | 0 | (isc_stdtime_t)0, &rdataset, NULL); |
148 | 0 | if (result != ISC_R_SUCCESS) { |
149 | 0 | goto freenode; |
150 | 0 | } |
151 | | |
152 | 0 | result = dns_rdataset_first(&rdataset); |
153 | 0 | if (result != ISC_R_SUCCESS) { |
154 | 0 | goto freenode; |
155 | 0 | } |
156 | | |
157 | 0 | dns_rdataset_current(&rdataset, &rdata); |
158 | 0 | dns_rdataset_getownercase(&rdataset, zonename); |
159 | |
|
160 | 0 | result = dns_difftuple_create(mctx, op, zonename, rdataset.ttl, &rdata, |
161 | 0 | tp); |
162 | |
|
163 | 0 | dns_rdataset_disassociate(&rdataset); |
164 | 0 | dns_db_detachnode(db, &node); |
165 | 0 | return (result); |
166 | | |
167 | 0 | freenode: |
168 | 0 | dns_db_detachnode(db, &node); |
169 | 0 | nonode: |
170 | 0 | UNEXPECTED_ERROR("missing SOA"); |
171 | 0 | return (result); |
172 | 0 | } |
173 | | |
174 | | /* Journaling */ |
175 | | |
176 | | /*% |
177 | | * On-disk representation of a "pointer" to a journal entry. |
178 | | * These are used in the journal header to locate the beginning |
179 | | * and end of the journal, and in the journal index to locate |
180 | | * other transactions. |
181 | | */ |
182 | | typedef struct { |
183 | | unsigned char serial[4]; /*%< SOA serial before update. */ |
184 | | /* |
185 | | * XXXRTH Should offset be 8 bytes? |
186 | | * XXXDCL ... probably, since off_t is 8 bytes on many OSs. |
187 | | * XXXAG ... but we will not be able to seek >2G anyway on many |
188 | | * platforms as long as we are using fseek() rather |
189 | | * than lseek(). |
190 | | */ |
191 | | unsigned char offset[4]; /*%< Offset from beginning of file. */ |
192 | | } journal_rawpos_t; |
193 | | |
194 | | /*% |
195 | | * The header is of a fixed size, with some spare room for future |
196 | | * extensions. |
197 | | */ |
198 | | #define JOURNAL_HEADER_SIZE 64 /* Bytes. */ |
199 | | |
200 | | typedef enum { |
201 | | XHDR_VERSION1 = 1, |
202 | | XHDR_VERSION2 = 2, |
203 | | } xhdr_version_t; |
204 | | |
205 | | /*% |
206 | | * The on-disk representation of the journal header. |
207 | | * All numbers are stored in big-endian order. |
208 | | */ |
209 | | typedef union { |
210 | | struct { |
211 | | /*% File format version ID. */ |
212 | | unsigned char format[16]; |
213 | | /*% Position of the first addressable transaction */ |
214 | | journal_rawpos_t begin; |
215 | | /*% Position of the next (yet nonexistent) transaction. */ |
216 | | journal_rawpos_t end; |
217 | | /*% Number of index entries following the header. */ |
218 | | unsigned char index_size[4]; |
219 | | /*% Source serial number. */ |
220 | | unsigned char sourceserial[4]; |
221 | | unsigned char flags; |
222 | | } h; |
223 | | /* Pad the header to a fixed size. */ |
224 | | unsigned char pad[JOURNAL_HEADER_SIZE]; |
225 | | } journal_rawheader_t; |
226 | | |
227 | | /*% |
228 | | * The on-disk representation of the transaction header, version 2. |
229 | | * There is one of these at the beginning of each transaction. |
230 | | */ |
231 | | typedef struct { |
232 | | unsigned char size[4]; /*%< In bytes, excluding header. */ |
233 | | unsigned char count[4]; /*%< Number of records in transaction */ |
234 | | unsigned char serial0[4]; /*%< SOA serial before update. */ |
235 | | unsigned char serial1[4]; /*%< SOA serial after update. */ |
236 | | } journal_rawxhdr_t; |
237 | | |
238 | | /*% |
239 | | * Old-style raw transaction header, version 1, used for backward |
240 | | * compatibility mode. |
241 | | */ |
242 | | typedef struct { |
243 | | unsigned char size[4]; |
244 | | unsigned char serial0[4]; |
245 | | unsigned char serial1[4]; |
246 | | } journal_rawxhdr_ver1_t; |
247 | | |
248 | | /*% |
249 | | * The on-disk representation of the RR header. |
250 | | * There is one of these at the beginning of each RR. |
251 | | */ |
252 | | typedef struct { |
253 | | unsigned char size[4]; /*%< In bytes, excluding header. */ |
254 | | } journal_rawrrhdr_t; |
255 | | |
256 | | /*% |
257 | | * The in-core representation of the journal header. |
258 | | */ |
259 | | typedef struct { |
260 | | uint32_t serial; |
261 | | off_t offset; |
262 | | } journal_pos_t; |
263 | | |
264 | 0 | #define POS_VALID(pos) ((pos).offset != 0) |
265 | 0 | #define POS_INVALIDATE(pos) ((pos).offset = 0, (pos).serial = 0) |
266 | | |
267 | | typedef struct { |
268 | | unsigned char format[16]; |
269 | | journal_pos_t begin; |
270 | | journal_pos_t end; |
271 | | uint32_t index_size; |
272 | | uint32_t sourceserial; |
273 | | bool serialset; |
274 | | } journal_header_t; |
275 | | |
276 | | /*% |
277 | | * The in-core representation of the transaction header. |
278 | | */ |
279 | | typedef struct { |
280 | | uint32_t size; |
281 | | uint32_t count; |
282 | | uint32_t serial0; |
283 | | uint32_t serial1; |
284 | | } journal_xhdr_t; |
285 | | |
286 | | /*% |
287 | | * The in-core representation of the RR header. |
288 | | */ |
289 | | typedef struct { |
290 | | uint32_t size; |
291 | | } journal_rrhdr_t; |
292 | | |
293 | | /*% |
294 | | * Initial contents to store in the header of a newly created |
295 | | * journal file. |
296 | | * |
297 | | * The header starts with the magic string ";BIND LOG V9.2\n" |
298 | | * to identify the file as a BIND 9 journal file. An ASCII |
299 | | * identification string is used rather than a binary magic |
300 | | * number to be consistent with BIND 8 (BIND 8 journal files |
301 | | * are ASCII text files). |
302 | | */ |
303 | | |
304 | | static journal_header_t journal_header_ver1 = { |
305 | | ";BIND LOG V9\n", { 0, 0 }, { 0, 0 }, 0, 0, 0 |
306 | | }; |
307 | | static journal_header_t initial_journal_header = { |
308 | | ";BIND LOG V9.2\n", { 0, 0 }, { 0, 0 }, 0, 0, 0 |
309 | | }; |
310 | | |
311 | 0 | #define JOURNAL_EMPTY(h) ((h)->begin.offset == (h)->end.offset) |
312 | | |
313 | | typedef enum { |
314 | | JOURNAL_STATE_INVALID, |
315 | | JOURNAL_STATE_READ, |
316 | | JOURNAL_STATE_WRITE, |
317 | | JOURNAL_STATE_TRANSACTION, |
318 | | JOURNAL_STATE_INLINE |
319 | | } journal_state_t; |
320 | | |
321 | | struct dns_journal { |
322 | | unsigned int magic; /*%< JOUR */ |
323 | | isc_mem_t *mctx; /*%< Memory context */ |
324 | | journal_state_t state; |
325 | | xhdr_version_t xhdr_version; /*%< Expected transaction header version */ |
326 | | bool header_ver1; /*%< Transaction header compatibility |
327 | | * mode is allowed */ |
328 | | bool recovered; /*%< A recoverable error was found |
329 | | * while reading the journal */ |
330 | | char *filename; /*%< Journal file name */ |
331 | | FILE *fp; /*%< File handle */ |
332 | | off_t offset; /*%< Current file offset */ |
333 | | journal_xhdr_t curxhdr; /*%< Current transaction header */ |
334 | | journal_header_t header; /*%< In-core journal header */ |
335 | | unsigned char *rawindex; /*%< In-core buffer for journal index |
336 | | * in on-disk format */ |
337 | | journal_pos_t *index; /*%< In-core journal index */ |
338 | | |
339 | | /*% Current transaction state (when writing). */ |
340 | | struct { |
341 | | unsigned int n_soa; /*%< Number of SOAs seen */ |
342 | | unsigned int n_rr; /*%< Number of RRs to write */ |
343 | | journal_pos_t pos[2]; /*%< Begin/end position */ |
344 | | } x; |
345 | | |
346 | | /*% Iteration state (when reading). */ |
347 | | struct { |
348 | | /* These define the part of the journal we iterate over. */ |
349 | | journal_pos_t bpos; /*%< Position before first, */ |
350 | | journal_pos_t cpos; /*%< before current, */ |
351 | | journal_pos_t epos; /*%< and after last transaction */ |
352 | | /* The rest is iterator state. */ |
353 | | uint32_t current_serial; /*%< Current SOA serial */ |
354 | | isc_buffer_t source; /*%< Data from disk */ |
355 | | isc_buffer_t target; /*%< Data from _fromwire check */ |
356 | | dns_decompress_t dctx; /*%< Dummy decompression ctx */ |
357 | | dns_name_t name; /*%< Current domain name */ |
358 | | dns_rdata_t rdata; /*%< Current rdata */ |
359 | | uint32_t ttl; /*%< Current TTL */ |
360 | | unsigned int xsize; /*%< Size of transaction data */ |
361 | | unsigned int xpos; /*%< Current position in it */ |
362 | | isc_result_t result; /*%< Result of last call */ |
363 | | } it; |
364 | | }; |
365 | | |
366 | 0 | #define DNS_JOURNAL_MAGIC ISC_MAGIC('J', 'O', 'U', 'R') |
367 | | #define DNS_JOURNAL_VALID(t) ISC_MAGIC_VALID(t, DNS_JOURNAL_MAGIC) |
368 | | |
369 | | static void |
370 | 0 | journal_pos_decode(journal_rawpos_t *raw, journal_pos_t *cooked) { |
371 | 0 | cooked->serial = decode_uint32(raw->serial); |
372 | 0 | cooked->offset = decode_uint32(raw->offset); |
373 | 0 | } |
374 | | |
375 | | static void |
376 | 0 | journal_pos_encode(journal_rawpos_t *raw, journal_pos_t *cooked) { |
377 | 0 | encode_uint32(cooked->serial, raw->serial); |
378 | 0 | encode_uint32(cooked->offset, raw->offset); |
379 | 0 | } |
380 | | |
381 | | static void |
382 | 0 | journal_header_decode(journal_rawheader_t *raw, journal_header_t *cooked) { |
383 | 0 | INSIST(sizeof(cooked->format) == sizeof(raw->h.format)); |
384 | |
|
385 | 0 | memmove(cooked->format, raw->h.format, sizeof(cooked->format)); |
386 | 0 | journal_pos_decode(&raw->h.begin, &cooked->begin); |
387 | 0 | journal_pos_decode(&raw->h.end, &cooked->end); |
388 | 0 | cooked->index_size = decode_uint32(raw->h.index_size); |
389 | 0 | cooked->sourceserial = decode_uint32(raw->h.sourceserial); |
390 | 0 | cooked->serialset = ((raw->h.flags & JOURNAL_SERIALSET) != 0); |
391 | 0 | } |
392 | | |
393 | | static void |
394 | 0 | journal_header_encode(journal_header_t *cooked, journal_rawheader_t *raw) { |
395 | 0 | unsigned char flags = 0; |
396 | |
|
397 | 0 | INSIST(sizeof(cooked->format) == sizeof(raw->h.format)); |
398 | |
|
399 | 0 | memset(raw->pad, 0, sizeof(raw->pad)); |
400 | 0 | memmove(raw->h.format, cooked->format, sizeof(raw->h.format)); |
401 | 0 | journal_pos_encode(&raw->h.begin, &cooked->begin); |
402 | 0 | journal_pos_encode(&raw->h.end, &cooked->end); |
403 | 0 | encode_uint32(cooked->index_size, raw->h.index_size); |
404 | 0 | encode_uint32(cooked->sourceserial, raw->h.sourceserial); |
405 | 0 | if (cooked->serialset) { |
406 | 0 | flags |= JOURNAL_SERIALSET; |
407 | 0 | } |
408 | 0 | raw->h.flags = flags; |
409 | 0 | } |
410 | | |
411 | | /* |
412 | | * Journal file I/O subroutines, with error checking and reporting. |
413 | | */ |
414 | | static isc_result_t |
415 | 0 | journal_seek(dns_journal_t *j, uint32_t offset) { |
416 | 0 | isc_result_t result; |
417 | |
|
418 | 0 | result = isc_stdio_seek(j->fp, (off_t)offset, SEEK_SET); |
419 | 0 | if (result != ISC_R_SUCCESS) { |
420 | 0 | isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, |
421 | 0 | "%s: seek: %s", j->filename, |
422 | 0 | isc_result_totext(result)); |
423 | 0 | return (ISC_R_UNEXPECTED); |
424 | 0 | } |
425 | 0 | j->offset = offset; |
426 | 0 | return (ISC_R_SUCCESS); |
427 | 0 | } |
428 | | |
429 | | static isc_result_t |
430 | 0 | journal_read(dns_journal_t *j, void *mem, size_t nbytes) { |
431 | 0 | isc_result_t result; |
432 | |
|
433 | 0 | result = isc_stdio_read(mem, 1, nbytes, j->fp, NULL); |
434 | 0 | if (result != ISC_R_SUCCESS) { |
435 | 0 | if (result == ISC_R_EOF) { |
436 | 0 | return (ISC_R_NOMORE); |
437 | 0 | } |
438 | 0 | isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, |
439 | 0 | "%s: read: %s", j->filename, |
440 | 0 | isc_result_totext(result)); |
441 | 0 | return (ISC_R_UNEXPECTED); |
442 | 0 | } |
443 | 0 | j->offset += (off_t)nbytes; |
444 | 0 | return (ISC_R_SUCCESS); |
445 | 0 | } |
446 | | |
447 | | static isc_result_t |
448 | 0 | journal_write(dns_journal_t *j, void *mem, size_t nbytes) { |
449 | 0 | isc_result_t result; |
450 | |
|
451 | 0 | result = isc_stdio_write(mem, 1, nbytes, j->fp, NULL); |
452 | 0 | if (result != ISC_R_SUCCESS) { |
453 | 0 | isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, |
454 | 0 | "%s: write: %s", j->filename, |
455 | 0 | isc_result_totext(result)); |
456 | 0 | return (ISC_R_UNEXPECTED); |
457 | 0 | } |
458 | 0 | j->offset += (off_t)nbytes; |
459 | 0 | return (ISC_R_SUCCESS); |
460 | 0 | } |
461 | | |
462 | | static isc_result_t |
463 | 0 | journal_fsync(dns_journal_t *j) { |
464 | 0 | isc_result_t result; |
465 | |
|
466 | 0 | result = isc_stdio_flush(j->fp); |
467 | 0 | if (result != ISC_R_SUCCESS) { |
468 | 0 | isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, |
469 | 0 | "%s: flush: %s", j->filename, |
470 | 0 | isc_result_totext(result)); |
471 | 0 | return (ISC_R_UNEXPECTED); |
472 | 0 | } |
473 | 0 | result = isc_stdio_sync(j->fp); |
474 | 0 | if (result != ISC_R_SUCCESS) { |
475 | 0 | isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, |
476 | 0 | "%s: fsync: %s", j->filename, |
477 | 0 | isc_result_totext(result)); |
478 | 0 | return (ISC_R_UNEXPECTED); |
479 | 0 | } |
480 | 0 | return (ISC_R_SUCCESS); |
481 | 0 | } |
482 | | |
483 | | /* |
484 | | * Read/write a transaction header at the current file position. |
485 | | */ |
486 | | static isc_result_t |
487 | 0 | journal_read_xhdr(dns_journal_t *j, journal_xhdr_t *xhdr) { |
488 | 0 | isc_result_t result; |
489 | |
|
490 | 0 | j->it.cpos.offset = j->offset; |
491 | |
|
492 | 0 | switch (j->xhdr_version) { |
493 | 0 | case XHDR_VERSION1: { |
494 | 0 | journal_rawxhdr_ver1_t raw; |
495 | 0 | result = journal_read(j, &raw, sizeof(raw)); |
496 | 0 | if (result != ISC_R_SUCCESS) { |
497 | 0 | return (result); |
498 | 0 | } |
499 | 0 | xhdr->size = decode_uint32(raw.size); |
500 | 0 | xhdr->count = 0; |
501 | 0 | xhdr->serial0 = decode_uint32(raw.serial0); |
502 | 0 | xhdr->serial1 = decode_uint32(raw.serial1); |
503 | 0 | j->curxhdr = *xhdr; |
504 | 0 | return (ISC_R_SUCCESS); |
505 | 0 | } |
506 | | |
507 | 0 | case XHDR_VERSION2: { |
508 | 0 | journal_rawxhdr_t raw; |
509 | 0 | result = journal_read(j, &raw, sizeof(raw)); |
510 | 0 | if (result != ISC_R_SUCCESS) { |
511 | 0 | return (result); |
512 | 0 | } |
513 | 0 | xhdr->size = decode_uint32(raw.size); |
514 | 0 | xhdr->count = decode_uint32(raw.count); |
515 | 0 | xhdr->serial0 = decode_uint32(raw.serial0); |
516 | 0 | xhdr->serial1 = decode_uint32(raw.serial1); |
517 | 0 | j->curxhdr = *xhdr; |
518 | 0 | return (ISC_R_SUCCESS); |
519 | 0 | } |
520 | | |
521 | 0 | default: |
522 | 0 | return (ISC_R_NOTIMPLEMENTED); |
523 | 0 | } |
524 | 0 | } |
525 | | |
526 | | static isc_result_t |
527 | | journal_write_xhdr(dns_journal_t *j, uint32_t size, uint32_t count, |
528 | 0 | uint32_t serial0, uint32_t serial1) { |
529 | 0 | if (j->header_ver1) { |
530 | 0 | journal_rawxhdr_ver1_t raw; |
531 | 0 | encode_uint32(size, raw.size); |
532 | 0 | encode_uint32(serial0, raw.serial0); |
533 | 0 | encode_uint32(serial1, raw.serial1); |
534 | 0 | return (journal_write(j, &raw, sizeof(raw))); |
535 | 0 | } else { |
536 | 0 | journal_rawxhdr_t raw; |
537 | 0 | encode_uint32(size, raw.size); |
538 | 0 | encode_uint32(count, raw.count); |
539 | 0 | encode_uint32(serial0, raw.serial0); |
540 | 0 | encode_uint32(serial1, raw.serial1); |
541 | 0 | return (journal_write(j, &raw, sizeof(raw))); |
542 | 0 | } |
543 | 0 | } |
544 | | |
545 | | /* |
546 | | * Read an RR header at the current file position. |
547 | | */ |
548 | | |
549 | | static isc_result_t |
550 | 0 | journal_read_rrhdr(dns_journal_t *j, journal_rrhdr_t *rrhdr) { |
551 | 0 | journal_rawrrhdr_t raw; |
552 | 0 | isc_result_t result; |
553 | |
|
554 | 0 | result = journal_read(j, &raw, sizeof(raw)); |
555 | 0 | if (result != ISC_R_SUCCESS) { |
556 | 0 | return (result); |
557 | 0 | } |
558 | 0 | rrhdr->size = decode_uint32(raw.size); |
559 | 0 | return (ISC_R_SUCCESS); |
560 | 0 | } |
561 | | |
562 | | static isc_result_t |
563 | 0 | journal_file_create(isc_mem_t *mctx, bool downgrade, const char *filename) { |
564 | 0 | FILE *fp = NULL; |
565 | 0 | isc_result_t result; |
566 | 0 | journal_header_t header; |
567 | 0 | journal_rawheader_t rawheader; |
568 | 0 | int index_size = 56; /* XXX configurable */ |
569 | 0 | int size; |
570 | 0 | void *mem = NULL; /* Memory for temporary index image. */ |
571 | |
|
572 | 0 | INSIST(sizeof(journal_rawheader_t) == JOURNAL_HEADER_SIZE); |
573 | |
|
574 | 0 | result = isc_stdio_open(filename, "wb", &fp); |
575 | 0 | if (result != ISC_R_SUCCESS) { |
576 | 0 | isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, |
577 | 0 | "%s: create: %s", filename, |
578 | 0 | isc_result_totext(result)); |
579 | 0 | return (ISC_R_UNEXPECTED); |
580 | 0 | } |
581 | | |
582 | 0 | if (downgrade) { |
583 | 0 | header = journal_header_ver1; |
584 | 0 | } else { |
585 | 0 | header = initial_journal_header; |
586 | 0 | } |
587 | 0 | header.index_size = index_size; |
588 | 0 | journal_header_encode(&header, &rawheader); |
589 | |
|
590 | 0 | size = sizeof(journal_rawheader_t) + |
591 | 0 | index_size * sizeof(journal_rawpos_t); |
592 | |
|
593 | 0 | mem = isc_mem_getx(mctx, size, ISC_MEM_ZERO); |
594 | 0 | memmove(mem, &rawheader, sizeof(rawheader)); |
595 | |
|
596 | 0 | result = isc_stdio_write(mem, 1, (size_t)size, fp, NULL); |
597 | 0 | if (result != ISC_R_SUCCESS) { |
598 | 0 | isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, |
599 | 0 | "%s: write: %s", filename, |
600 | 0 | isc_result_totext(result)); |
601 | 0 | (void)isc_stdio_close(fp); |
602 | 0 | (void)isc_file_remove(filename); |
603 | 0 | isc_mem_put(mctx, mem, size); |
604 | 0 | return (ISC_R_UNEXPECTED); |
605 | 0 | } |
606 | 0 | isc_mem_put(mctx, mem, size); |
607 | |
|
608 | 0 | result = isc_stdio_close(fp); |
609 | 0 | if (result != ISC_R_SUCCESS) { |
610 | 0 | isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, |
611 | 0 | "%s: close: %s", filename, |
612 | 0 | isc_result_totext(result)); |
613 | 0 | (void)isc_file_remove(filename); |
614 | 0 | return (ISC_R_UNEXPECTED); |
615 | 0 | } |
616 | | |
617 | 0 | return (ISC_R_SUCCESS); |
618 | 0 | } |
619 | | |
620 | | static isc_result_t |
621 | | journal_open(isc_mem_t *mctx, const char *filename, bool writable, bool create, |
622 | 4 | bool downgrade, dns_journal_t **journalp) { |
623 | 4 | FILE *fp = NULL; |
624 | 4 | isc_result_t result; |
625 | 4 | journal_rawheader_t rawheader; |
626 | 4 | dns_journal_t *j; |
627 | | |
628 | 4 | REQUIRE(journalp != NULL && *journalp == NULL); |
629 | | |
630 | 4 | j = isc_mem_get(mctx, sizeof(*j)); |
631 | 4 | *j = (dns_journal_t){ .state = JOURNAL_STATE_INVALID, |
632 | 4 | .filename = isc_mem_strdup(mctx, filename), |
633 | 4 | .xhdr_version = XHDR_VERSION2 }; |
634 | 4 | isc_mem_attach(mctx, &j->mctx); |
635 | | |
636 | 4 | result = isc_stdio_open(j->filename, writable ? "rb+" : "rb", &fp); |
637 | 4 | if (result == ISC_R_FILENOTFOUND) { |
638 | 4 | if (create) { |
639 | 0 | isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_DEBUG(1), |
640 | 0 | "journal file %s does not exist, " |
641 | 0 | "creating it", |
642 | 0 | j->filename); |
643 | 0 | CHECK(journal_file_create(mctx, downgrade, filename)); |
644 | | /* |
645 | | * Retry. |
646 | | */ |
647 | 0 | result = isc_stdio_open(j->filename, "rb+", &fp); |
648 | 4 | } else { |
649 | 4 | FAIL(ISC_R_NOTFOUND); |
650 | 4 | } |
651 | 4 | } |
652 | 0 | if (result != ISC_R_SUCCESS) { |
653 | 0 | isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, |
654 | 0 | "%s: open: %s", j->filename, |
655 | 0 | isc_result_totext(result)); |
656 | 0 | FAIL(ISC_R_UNEXPECTED); |
657 | 0 | } |
658 | | |
659 | 0 | j->fp = fp; |
660 | | |
661 | | /* |
662 | | * Set magic early so that seek/read can succeed. |
663 | | */ |
664 | 0 | j->magic = DNS_JOURNAL_MAGIC; |
665 | |
|
666 | 0 | CHECK(journal_seek(j, 0)); |
667 | 0 | CHECK(journal_read(j, &rawheader, sizeof(rawheader))); |
668 | | |
669 | 0 | if (memcmp(rawheader.h.format, journal_header_ver1.format, |
670 | 0 | sizeof(journal_header_ver1.format)) == 0) |
671 | 0 | { |
672 | | /* |
673 | | * The file header says it's the old format, but it |
674 | | * still might have the new xhdr format because we |
675 | | * forgot to change the format string when we introduced |
676 | | * the new xhdr. When we first try to read it, we assume |
677 | | * it uses the new xhdr format. If that fails, we'll be |
678 | | * called a second time with compat set to true, in which |
679 | | * case we can lower xhdr_version to 1 if we find a |
680 | | * corrupt transaction. |
681 | | */ |
682 | 0 | j->header_ver1 = true; |
683 | 0 | } else if (memcmp(rawheader.h.format, initial_journal_header.format, |
684 | 0 | sizeof(initial_journal_header.format)) == 0) |
685 | 0 | { |
686 | | /* |
687 | | * File header says this is format version 2; all |
688 | | * transactions have to match. |
689 | | */ |
690 | 0 | j->header_ver1 = false; |
691 | 0 | } else { |
692 | 0 | isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, |
693 | 0 | "%s: journal format not recognized", j->filename); |
694 | 0 | FAIL(ISC_R_UNEXPECTED); |
695 | 0 | } |
696 | 0 | journal_header_decode(&rawheader, &j->header); |
697 | | |
698 | | /* |
699 | | * If there is an index, read the raw index into a dynamically |
700 | | * allocated buffer and then convert it into a cooked index. |
701 | | */ |
702 | 0 | if (j->header.index_size != 0) { |
703 | 0 | unsigned int i; |
704 | 0 | unsigned int rawbytes; |
705 | 0 | unsigned char *p; |
706 | |
|
707 | 0 | rawbytes = j->header.index_size * sizeof(journal_rawpos_t); |
708 | 0 | j->rawindex = isc_mem_get(mctx, rawbytes); |
709 | |
|
710 | 0 | CHECK(journal_read(j, j->rawindex, rawbytes)); |
711 | | |
712 | 0 | j->index = isc_mem_get(mctx, j->header.index_size * |
713 | 0 | sizeof(journal_pos_t)); |
714 | |
|
715 | 0 | p = j->rawindex; |
716 | 0 | for (i = 0; i < j->header.index_size; i++) { |
717 | 0 | j->index[i].serial = decode_uint32(p); |
718 | 0 | p += 4; |
719 | 0 | j->index[i].offset = decode_uint32(p); |
720 | 0 | p += 4; |
721 | 0 | } |
722 | 0 | INSIST(p == j->rawindex + rawbytes); |
723 | 0 | } |
724 | 0 | j->offset = -1; /* Invalid, must seek explicitly. */ |
725 | | |
726 | | /* |
727 | | * Initialize the iterator. |
728 | | */ |
729 | 0 | dns_name_init(&j->it.name, NULL); |
730 | 0 | dns_rdata_init(&j->it.rdata); |
731 | | |
732 | | /* |
733 | | * Set up empty initial buffers for unchecked and checked |
734 | | * wire format RR data. They will be reallocated |
735 | | * later. |
736 | | */ |
737 | 0 | isc_buffer_init(&j->it.source, NULL, 0); |
738 | 0 | isc_buffer_init(&j->it.target, NULL, 0); |
739 | 0 | j->it.dctx = DNS_DECOMPRESS_NEVER; |
740 | |
|
741 | 0 | j->state = writable ? JOURNAL_STATE_WRITE : JOURNAL_STATE_READ; |
742 | |
|
743 | 0 | *journalp = j; |
744 | 0 | return (ISC_R_SUCCESS); |
745 | | |
746 | 4 | failure: |
747 | 4 | j->magic = 0; |
748 | 4 | if (j->rawindex != NULL) { |
749 | 0 | isc_mem_put(j->mctx, j->rawindex, |
750 | 0 | j->header.index_size * sizeof(journal_rawpos_t)); |
751 | 0 | } |
752 | 4 | if (j->index != NULL) { |
753 | 0 | isc_mem_put(j->mctx, j->index, |
754 | 0 | j->header.index_size * sizeof(journal_pos_t)); |
755 | 0 | } |
756 | 4 | isc_mem_free(j->mctx, j->filename); |
757 | 4 | if (j->fp != NULL) { |
758 | 0 | (void)isc_stdio_close(j->fp); |
759 | 0 | } |
760 | 4 | isc_mem_putanddetach(&j->mctx, j, sizeof(*j)); |
761 | 4 | return (result); |
762 | 0 | } |
763 | | |
764 | | isc_result_t |
765 | | dns_journal_open(isc_mem_t *mctx, const char *filename, unsigned int mode, |
766 | 2 | dns_journal_t **journalp) { |
767 | 2 | isc_result_t result; |
768 | 2 | size_t namelen; |
769 | 2 | char backup[1024]; |
770 | 2 | bool writable, create; |
771 | | |
772 | 2 | create = ((mode & DNS_JOURNAL_CREATE) != 0); |
773 | 2 | writable = ((mode & (DNS_JOURNAL_WRITE | DNS_JOURNAL_CREATE)) != 0); |
774 | | |
775 | 2 | result = journal_open(mctx, filename, writable, create, false, |
776 | 2 | journalp); |
777 | 2 | if (result == ISC_R_NOTFOUND) { |
778 | 2 | namelen = strlen(filename); |
779 | 2 | if (namelen > 4U && strcmp(filename + namelen - 4, ".jnl") == 0) |
780 | 2 | { |
781 | 2 | namelen -= 4; |
782 | 2 | } |
783 | | |
784 | 2 | result = snprintf(backup, sizeof(backup), "%.*s.jbk", |
785 | 2 | (int)namelen, filename); |
786 | 2 | if (result >= sizeof(backup)) { |
787 | 0 | return (ISC_R_NOSPACE); |
788 | 0 | } |
789 | 2 | result = journal_open(mctx, backup, writable, writable, false, |
790 | 2 | journalp); |
791 | 2 | } |
792 | 2 | return (result); |
793 | 2 | } |
794 | | |
795 | | /* |
796 | | * A comparison function defining the sorting order for |
797 | | * entries in the IXFR-style journal file. |
798 | | * |
799 | | * The IXFR format requires that deletions are sorted before |
800 | | * additions, and within either one, SOA records are sorted |
801 | | * before others. |
802 | | * |
803 | | * Also sort the non-SOA records by type as a courtesy to the |
804 | | * server receiving the IXFR - it may help reduce the amount of |
805 | | * rdataset merging it has to do. |
806 | | */ |
807 | | static int |
808 | 0 | ixfr_order(const void *av, const void *bv) { |
809 | 0 | dns_difftuple_t const *const *ap = av; |
810 | 0 | dns_difftuple_t const *const *bp = bv; |
811 | 0 | dns_difftuple_t const *a = *ap; |
812 | 0 | dns_difftuple_t const *b = *bp; |
813 | 0 | int r; |
814 | 0 | int bop = 0, aop = 0; |
815 | |
|
816 | 0 | switch (a->op) { |
817 | 0 | case DNS_DIFFOP_DEL: |
818 | 0 | case DNS_DIFFOP_DELRESIGN: |
819 | 0 | aop = 1; |
820 | 0 | break; |
821 | 0 | case DNS_DIFFOP_ADD: |
822 | 0 | case DNS_DIFFOP_ADDRESIGN: |
823 | 0 | aop = 0; |
824 | 0 | break; |
825 | 0 | default: |
826 | 0 | UNREACHABLE(); |
827 | 0 | } |
828 | | |
829 | 0 | switch (b->op) { |
830 | 0 | case DNS_DIFFOP_DEL: |
831 | 0 | case DNS_DIFFOP_DELRESIGN: |
832 | 0 | bop = 1; |
833 | 0 | break; |
834 | 0 | case DNS_DIFFOP_ADD: |
835 | 0 | case DNS_DIFFOP_ADDRESIGN: |
836 | 0 | bop = 0; |
837 | 0 | break; |
838 | 0 | default: |
839 | 0 | UNREACHABLE(); |
840 | 0 | } |
841 | | |
842 | 0 | r = bop - aop; |
843 | 0 | if (r != 0) { |
844 | 0 | return (r); |
845 | 0 | } |
846 | | |
847 | 0 | r = (b->rdata.type == dns_rdatatype_soa) - |
848 | 0 | (a->rdata.type == dns_rdatatype_soa); |
849 | 0 | if (r != 0) { |
850 | 0 | return (r); |
851 | 0 | } |
852 | | |
853 | 0 | r = (a->rdata.type - b->rdata.type); |
854 | 0 | return (r); |
855 | 0 | } |
856 | | |
857 | | static isc_result_t |
858 | | maybe_fixup_xhdr(dns_journal_t *j, journal_xhdr_t *xhdr, uint32_t serial, |
859 | 0 | off_t offset) { |
860 | 0 | isc_result_t result = ISC_R_SUCCESS; |
861 | | |
862 | | /* |
863 | | * Handle mixture of version 1 and version 2 |
864 | | * transaction headers in a version 1 journal. |
865 | | */ |
866 | 0 | if ((xhdr->serial0 != serial || |
867 | 0 | isc_serial_le(xhdr->serial1, xhdr->serial0))) |
868 | 0 | { |
869 | 0 | if (j->xhdr_version == XHDR_VERSION1 && xhdr->serial1 == serial) |
870 | 0 | { |
871 | 0 | isc_log_write( |
872 | 0 | JOURNAL_COMMON_LOGARGS, ISC_LOG_DEBUG(3), |
873 | 0 | "%s: XHDR_VERSION1 -> XHDR_VERSION2 at %u", |
874 | 0 | j->filename, serial); |
875 | 0 | j->xhdr_version = XHDR_VERSION2; |
876 | 0 | CHECK(journal_seek(j, offset)); |
877 | 0 | CHECK(journal_read_xhdr(j, xhdr)); |
878 | 0 | j->recovered = true; |
879 | 0 | } else if (j->xhdr_version == XHDR_VERSION2 && |
880 | 0 | xhdr->count == serial) |
881 | 0 | { |
882 | 0 | isc_log_write( |
883 | 0 | JOURNAL_COMMON_LOGARGS, ISC_LOG_DEBUG(3), |
884 | 0 | "%s: XHDR_VERSION2 -> XHDR_VERSION1 at %u", |
885 | 0 | j->filename, serial); |
886 | 0 | j->xhdr_version = XHDR_VERSION1; |
887 | 0 | CHECK(journal_seek(j, offset)); |
888 | 0 | CHECK(journal_read_xhdr(j, xhdr)); |
889 | 0 | j->recovered = true; |
890 | 0 | } |
891 | 0 | } |
892 | | |
893 | | /* |
894 | | * Handle <size, serial0, serial1, 0> transaction header. |
895 | | */ |
896 | 0 | if (j->xhdr_version == XHDR_VERSION1) { |
897 | 0 | uint32_t value; |
898 | |
|
899 | 0 | CHECK(journal_read(j, &value, sizeof(value))); |
900 | 0 | if (value != 0L) { |
901 | 0 | CHECK(journal_seek(j, offset + 12)); |
902 | 0 | } else { |
903 | 0 | isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_DEBUG(3), |
904 | 0 | "%s: XHDR_VERSION1 count zero at %u", |
905 | 0 | j->filename, serial); |
906 | 0 | j->xhdr_version = XHDR_VERSION2; |
907 | 0 | j->recovered = true; |
908 | 0 | } |
909 | 0 | } else if (j->xhdr_version == XHDR_VERSION2 && xhdr->count == serial && |
910 | 0 | xhdr->serial1 == 0U && |
911 | 0 | isc_serial_gt(xhdr->serial0, xhdr->count)) |
912 | 0 | { |
913 | 0 | isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_DEBUG(3), |
914 | 0 | "%s: XHDR_VERSION2 count zero at %u", j->filename, |
915 | 0 | serial); |
916 | 0 | xhdr->serial1 = xhdr->serial0; |
917 | 0 | xhdr->serial0 = xhdr->count; |
918 | 0 | xhdr->count = 0; |
919 | 0 | j->recovered = true; |
920 | 0 | } |
921 | | |
922 | 0 | failure: |
923 | 0 | return (result); |
924 | 0 | } |
925 | | |
926 | | /* |
927 | | * Advance '*pos' to the next journal transaction. |
928 | | * |
929 | | * Requires: |
930 | | * *pos refers to a valid journal transaction. |
931 | | * |
932 | | * Ensures: |
933 | | * When ISC_R_SUCCESS is returned, |
934 | | * *pos refers to the next journal transaction. |
935 | | * |
936 | | * Returns one of: |
937 | | * |
938 | | * ISC_R_SUCCESS |
939 | | * ISC_R_NOMORE *pos pointed at the last transaction |
940 | | * Other results due to file errors are possible. |
941 | | */ |
942 | | static isc_result_t |
943 | 0 | journal_next(dns_journal_t *j, journal_pos_t *pos) { |
944 | 0 | isc_result_t result; |
945 | 0 | journal_xhdr_t xhdr; |
946 | 0 | size_t hdrsize; |
947 | |
|
948 | 0 | REQUIRE(DNS_JOURNAL_VALID(j)); |
949 | |
|
950 | 0 | result = journal_seek(j, pos->offset); |
951 | 0 | if (result != ISC_R_SUCCESS) { |
952 | 0 | return (result); |
953 | 0 | } |
954 | | |
955 | 0 | if (pos->serial == j->header.end.serial) { |
956 | 0 | return (ISC_R_NOMORE); |
957 | 0 | } |
958 | | |
959 | | /* |
960 | | * Read the header of the current transaction. |
961 | | * This will return ISC_R_NOMORE if we are at EOF. |
962 | | */ |
963 | 0 | result = journal_read_xhdr(j, &xhdr); |
964 | 0 | if (result != ISC_R_SUCCESS) { |
965 | 0 | return (result); |
966 | 0 | } |
967 | | |
968 | 0 | if (j->header_ver1) { |
969 | 0 | CHECK(maybe_fixup_xhdr(j, &xhdr, pos->serial, pos->offset)); |
970 | 0 | } |
971 | | |
972 | | /* |
973 | | * Check serial number consistency. |
974 | | */ |
975 | 0 | if (xhdr.serial0 != pos->serial || |
976 | 0 | isc_serial_le(xhdr.serial1, xhdr.serial0)) |
977 | 0 | { |
978 | 0 | isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, |
979 | 0 | "%s: journal file corrupt: " |
980 | 0 | "expected serial %u, got %u", |
981 | 0 | j->filename, pos->serial, xhdr.serial0); |
982 | 0 | return (ISC_R_UNEXPECTED); |
983 | 0 | } |
984 | | |
985 | | /* |
986 | | * Check for offset wraparound. |
987 | | */ |
988 | 0 | hdrsize = (j->xhdr_version == XHDR_VERSION2) |
989 | 0 | ? sizeof(journal_rawxhdr_t) |
990 | 0 | : sizeof(journal_rawxhdr_ver1_t); |
991 | |
|
992 | 0 | if ((off_t)(pos->offset + hdrsize + xhdr.size) < pos->offset) { |
993 | 0 | isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, |
994 | 0 | "%s: offset too large", j->filename); |
995 | 0 | return (ISC_R_UNEXPECTED); |
996 | 0 | } |
997 | | |
998 | 0 | pos->offset += hdrsize + xhdr.size; |
999 | 0 | pos->serial = xhdr.serial1; |
1000 | 0 | return (ISC_R_SUCCESS); |
1001 | | |
1002 | 0 | failure: |
1003 | 0 | return (result); |
1004 | 0 | } |
1005 | | |
1006 | | /* |
1007 | | * If the index of the journal 'j' contains an entry "better" |
1008 | | * than '*best_guess', replace '*best_guess' with it. |
1009 | | * |
1010 | | * "Better" means having a serial number closer to 'serial' |
1011 | | * but not greater than 'serial'. |
1012 | | */ |
1013 | | static void |
1014 | 0 | index_find(dns_journal_t *j, uint32_t serial, journal_pos_t *best_guess) { |
1015 | 0 | unsigned int i; |
1016 | 0 | if (j->index == NULL) { |
1017 | 0 | return; |
1018 | 0 | } |
1019 | 0 | for (i = 0; i < j->header.index_size; i++) { |
1020 | 0 | if (POS_VALID(j->index[i]) && |
1021 | 0 | DNS_SERIAL_GE(serial, j->index[i].serial) && |
1022 | 0 | DNS_SERIAL_GT(j->index[i].serial, best_guess->serial)) |
1023 | 0 | { |
1024 | 0 | *best_guess = j->index[i]; |
1025 | 0 | } |
1026 | 0 | } |
1027 | 0 | } |
1028 | | |
1029 | | /* |
1030 | | * Add a new index entry. If there is no room, make room by removing |
1031 | | * the odd-numbered entries and compacting the others into the first |
1032 | | * half of the index. This decimates old index entries exponentially |
1033 | | * over time, so that the index always contains a much larger fraction |
1034 | | * of recent serial numbers than of old ones. This is deliberate - |
1035 | | * most index searches are for outgoing IXFR, and IXFR tends to request |
1036 | | * recent versions more often than old ones. |
1037 | | */ |
1038 | | static void |
1039 | 0 | index_add(dns_journal_t *j, journal_pos_t *pos) { |
1040 | 0 | unsigned int i; |
1041 | |
|
1042 | 0 | if (j->index == NULL) { |
1043 | 0 | return; |
1044 | 0 | } |
1045 | | |
1046 | | /* |
1047 | | * Search for a vacant position. |
1048 | | */ |
1049 | 0 | for (i = 0; i < j->header.index_size; i++) { |
1050 | 0 | if (!POS_VALID(j->index[i])) { |
1051 | 0 | break; |
1052 | 0 | } |
1053 | 0 | } |
1054 | 0 | if (i == j->header.index_size) { |
1055 | 0 | unsigned int k = 0; |
1056 | | /* |
1057 | | * Found no vacant position. Make some room. |
1058 | | */ |
1059 | 0 | for (i = 0; i < j->header.index_size; i += 2) { |
1060 | 0 | j->index[k++] = j->index[i]; |
1061 | 0 | } |
1062 | 0 | i = k; /* 'i' identifies the first vacant position. */ |
1063 | 0 | while (k < j->header.index_size) { |
1064 | 0 | POS_INVALIDATE(j->index[k]); |
1065 | 0 | k++; |
1066 | 0 | } |
1067 | 0 | } |
1068 | 0 | INSIST(i < j->header.index_size); |
1069 | 0 | INSIST(!POS_VALID(j->index[i])); |
1070 | | |
1071 | | /* |
1072 | | * Store the new index entry. |
1073 | | */ |
1074 | 0 | j->index[i] = *pos; |
1075 | 0 | } |
1076 | | |
1077 | | /* |
1078 | | * Invalidate any existing index entries that could become |
1079 | | * ambiguous when a new transaction with number 'serial' is added. |
1080 | | */ |
1081 | | static void |
1082 | 0 | index_invalidate(dns_journal_t *j, uint32_t serial) { |
1083 | 0 | unsigned int i; |
1084 | 0 | if (j->index == NULL) { |
1085 | 0 | return; |
1086 | 0 | } |
1087 | 0 | for (i = 0; i < j->header.index_size; i++) { |
1088 | 0 | if (!DNS_SERIAL_GT(serial, j->index[i].serial)) { |
1089 | 0 | POS_INVALIDATE(j->index[i]); |
1090 | 0 | } |
1091 | 0 | } |
1092 | 0 | } |
1093 | | |
1094 | | /* |
1095 | | * Try to find a transaction with initial serial number 'serial' |
1096 | | * in the journal 'j'. |
1097 | | * |
1098 | | * If found, store its position at '*pos' and return ISC_R_SUCCESS. |
1099 | | * |
1100 | | * If 'serial' is current (= the ending serial number of the |
1101 | | * last transaction in the journal), set '*pos' to |
1102 | | * the position immediately following the last transaction and |
1103 | | * return ISC_R_SUCCESS. |
1104 | | * |
1105 | | * If 'serial' is within the range of addressable serial numbers |
1106 | | * covered by the journal but that particular serial number is missing |
1107 | | * (from the journal, not just from the index), return ISC_R_NOTFOUND. |
1108 | | * |
1109 | | * If 'serial' is outside the range of addressable serial numbers |
1110 | | * covered by the journal, return ISC_R_RANGE. |
1111 | | * |
1112 | | */ |
1113 | | static isc_result_t |
1114 | 0 | journal_find(dns_journal_t *j, uint32_t serial, journal_pos_t *pos) { |
1115 | 0 | isc_result_t result; |
1116 | 0 | journal_pos_t current_pos; |
1117 | |
|
1118 | 0 | REQUIRE(DNS_JOURNAL_VALID(j)); |
1119 | |
|
1120 | 0 | if (DNS_SERIAL_GT(j->header.begin.serial, serial)) { |
1121 | 0 | return (ISC_R_RANGE); |
1122 | 0 | } |
1123 | 0 | if (DNS_SERIAL_GT(serial, j->header.end.serial)) { |
1124 | 0 | return (ISC_R_RANGE); |
1125 | 0 | } |
1126 | 0 | if (serial == j->header.end.serial) { |
1127 | 0 | *pos = j->header.end; |
1128 | 0 | return (ISC_R_SUCCESS); |
1129 | 0 | } |
1130 | | |
1131 | 0 | current_pos = j->header.begin; |
1132 | 0 | index_find(j, serial, ¤t_pos); |
1133 | |
|
1134 | 0 | while (current_pos.serial != serial) { |
1135 | 0 | if (DNS_SERIAL_GT(current_pos.serial, serial)) { |
1136 | 0 | return (ISC_R_NOTFOUND); |
1137 | 0 | } |
1138 | 0 | result = journal_next(j, ¤t_pos); |
1139 | 0 | if (result != ISC_R_SUCCESS) { |
1140 | 0 | return (result); |
1141 | 0 | } |
1142 | 0 | } |
1143 | 0 | *pos = current_pos; |
1144 | 0 | return (ISC_R_SUCCESS); |
1145 | 0 | } |
1146 | | |
1147 | | isc_result_t |
1148 | 0 | dns_journal_begin_transaction(dns_journal_t *j) { |
1149 | 0 | uint32_t offset; |
1150 | 0 | isc_result_t result; |
1151 | |
|
1152 | 0 | REQUIRE(DNS_JOURNAL_VALID(j)); |
1153 | 0 | REQUIRE(j->state == JOURNAL_STATE_WRITE || |
1154 | 0 | j->state == JOURNAL_STATE_INLINE); |
1155 | | |
1156 | | /* |
1157 | | * Find the file offset where the new transaction should |
1158 | | * be written, and seek there. |
1159 | | */ |
1160 | 0 | if (JOURNAL_EMPTY(&j->header)) { |
1161 | 0 | offset = sizeof(journal_rawheader_t) + |
1162 | 0 | j->header.index_size * sizeof(journal_rawpos_t); |
1163 | 0 | } else { |
1164 | 0 | offset = j->header.end.offset; |
1165 | 0 | } |
1166 | 0 | j->x.pos[0].offset = offset; |
1167 | 0 | j->x.pos[1].offset = offset; /* Initial value, will be incremented. */ |
1168 | 0 | j->x.n_soa = 0; |
1169 | |
|
1170 | 0 | CHECK(journal_seek(j, offset)); |
1171 | | |
1172 | | /* |
1173 | | * Write a dummy transaction header of all zeroes to reserve |
1174 | | * space. It will be filled in when the transaction is |
1175 | | * finished. |
1176 | | */ |
1177 | 0 | CHECK(journal_write_xhdr(j, 0, 0, 0, 0)); |
1178 | 0 | j->x.pos[1].offset = j->offset; |
1179 | |
|
1180 | 0 | j->state = JOURNAL_STATE_TRANSACTION; |
1181 | 0 | result = ISC_R_SUCCESS; |
1182 | 0 | failure: |
1183 | 0 | return (result); |
1184 | 0 | } |
1185 | | |
1186 | | isc_result_t |
1187 | 0 | dns_journal_writediff(dns_journal_t *j, dns_diff_t *diff) { |
1188 | 0 | dns_difftuple_t *t; |
1189 | 0 | isc_buffer_t buffer; |
1190 | 0 | void *mem = NULL; |
1191 | 0 | uint64_t size = 0; |
1192 | 0 | uint32_t rrcount = 0; |
1193 | 0 | isc_result_t result; |
1194 | 0 | isc_region_t used; |
1195 | |
|
1196 | 0 | REQUIRE(DNS_DIFF_VALID(diff)); |
1197 | 0 | REQUIRE(j->state == JOURNAL_STATE_TRANSACTION); |
1198 | |
|
1199 | 0 | isc_log_write(JOURNAL_DEBUG_LOGARGS(3), "writing to journal"); |
1200 | 0 | (void)dns_diff_print(diff, NULL); |
1201 | | |
1202 | | /* |
1203 | | * Pass 1: determine the buffer size needed, and |
1204 | | * keep track of SOA serial numbers. |
1205 | | */ |
1206 | 0 | for (t = ISC_LIST_HEAD(diff->tuples); t != NULL; |
1207 | 0 | t = ISC_LIST_NEXT(t, link)) |
1208 | 0 | { |
1209 | 0 | if (t->rdata.type == dns_rdatatype_soa) { |
1210 | 0 | if (j->x.n_soa < 2) { |
1211 | 0 | j->x.pos[j->x.n_soa].serial = |
1212 | 0 | dns_soa_getserial(&t->rdata); |
1213 | 0 | } |
1214 | 0 | j->x.n_soa++; |
1215 | 0 | } |
1216 | 0 | size += sizeof(journal_rawrrhdr_t); |
1217 | 0 | size += t->name.length; /* XXX should have access macro? */ |
1218 | 0 | size += 10; |
1219 | 0 | size += t->rdata.length; |
1220 | 0 | } |
1221 | |
|
1222 | 0 | if (size >= DNS_JOURNAL_SIZE_MAX) { |
1223 | 0 | isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, |
1224 | 0 | "dns_journal_writediff: %s: journal entry " |
1225 | 0 | "too big to be stored: %" PRIu64 " bytes", |
1226 | 0 | j->filename, size); |
1227 | 0 | return (ISC_R_NOSPACE); |
1228 | 0 | } |
1229 | | |
1230 | 0 | mem = isc_mem_get(j->mctx, size); |
1231 | |
|
1232 | 0 | isc_buffer_init(&buffer, mem, size); |
1233 | | |
1234 | | /* |
1235 | | * Pass 2. Write RRs to buffer. |
1236 | | */ |
1237 | 0 | for (t = ISC_LIST_HEAD(diff->tuples); t != NULL; |
1238 | 0 | t = ISC_LIST_NEXT(t, link)) |
1239 | 0 | { |
1240 | | /* |
1241 | | * Write the RR header. |
1242 | | */ |
1243 | 0 | isc_buffer_putuint32(&buffer, |
1244 | 0 | t->name.length + 10 + t->rdata.length); |
1245 | | /* |
1246 | | * Write the owner name, RR header, and RR data. |
1247 | | */ |
1248 | 0 | isc_buffer_putmem(&buffer, t->name.ndata, t->name.length); |
1249 | 0 | isc_buffer_putuint16(&buffer, t->rdata.type); |
1250 | 0 | isc_buffer_putuint16(&buffer, t->rdata.rdclass); |
1251 | 0 | isc_buffer_putuint32(&buffer, t->ttl); |
1252 | 0 | INSIST(t->rdata.length < 65536); |
1253 | 0 | isc_buffer_putuint16(&buffer, (uint16_t)t->rdata.length); |
1254 | 0 | INSIST(isc_buffer_availablelength(&buffer) >= t->rdata.length); |
1255 | 0 | isc_buffer_putmem(&buffer, t->rdata.data, t->rdata.length); |
1256 | |
|
1257 | 0 | rrcount++; |
1258 | 0 | } |
1259 | |
|
1260 | 0 | isc_buffer_usedregion(&buffer, &used); |
1261 | 0 | INSIST(used.length == size); |
1262 | |
|
1263 | 0 | j->x.pos[1].offset += used.length; |
1264 | 0 | j->x.n_rr = rrcount; |
1265 | | |
1266 | | /* |
1267 | | * Write the buffer contents to the journal file. |
1268 | | */ |
1269 | 0 | CHECK(journal_write(j, used.base, used.length)); |
1270 | | |
1271 | 0 | result = ISC_R_SUCCESS; |
1272 | |
|
1273 | 0 | failure: |
1274 | 0 | if (mem != NULL) { |
1275 | 0 | isc_mem_put(j->mctx, mem, size); |
1276 | 0 | } |
1277 | 0 | return (result); |
1278 | 0 | } |
1279 | | |
1280 | | isc_result_t |
1281 | 0 | dns_journal_commit(dns_journal_t *j) { |
1282 | 0 | isc_result_t result; |
1283 | 0 | journal_rawheader_t rawheader; |
1284 | 0 | uint64_t total; |
1285 | |
|
1286 | 0 | REQUIRE(DNS_JOURNAL_VALID(j)); |
1287 | 0 | REQUIRE(j->state == JOURNAL_STATE_TRANSACTION || |
1288 | 0 | j->state == JOURNAL_STATE_INLINE); |
1289 | | |
1290 | | /* |
1291 | | * Just write out a updated header. |
1292 | | */ |
1293 | 0 | if (j->state == JOURNAL_STATE_INLINE) { |
1294 | 0 | CHECK(journal_fsync(j)); |
1295 | 0 | journal_header_encode(&j->header, &rawheader); |
1296 | 0 | CHECK(journal_seek(j, 0)); |
1297 | 0 | CHECK(journal_write(j, &rawheader, sizeof(rawheader))); |
1298 | 0 | CHECK(journal_fsync(j)); |
1299 | 0 | j->state = JOURNAL_STATE_WRITE; |
1300 | 0 | return (ISC_R_SUCCESS); |
1301 | 0 | } |
1302 | | |
1303 | | /* |
1304 | | * Perform some basic consistency checks. |
1305 | | */ |
1306 | 0 | if (j->x.n_soa != 2) { |
1307 | 0 | isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, |
1308 | 0 | "%s: malformed transaction: %d SOAs", j->filename, |
1309 | 0 | j->x.n_soa); |
1310 | 0 | return (ISC_R_UNEXPECTED); |
1311 | 0 | } |
1312 | 0 | if (!DNS_SERIAL_GT(j->x.pos[1].serial, j->x.pos[0].serial)) { |
1313 | 0 | isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, |
1314 | 0 | "%s: malformed transaction: serial number " |
1315 | 0 | "did not increase", |
1316 | 0 | j->filename); |
1317 | 0 | return (ISC_R_UNEXPECTED); |
1318 | 0 | } |
1319 | 0 | if (!JOURNAL_EMPTY(&j->header)) { |
1320 | 0 | if (j->x.pos[0].serial != j->header.end.serial) { |
1321 | 0 | isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, |
1322 | 0 | "malformed transaction: " |
1323 | 0 | "%s last serial %u != " |
1324 | 0 | "transaction first serial %u", |
1325 | 0 | j->filename, j->header.end.serial, |
1326 | 0 | j->x.pos[0].serial); |
1327 | 0 | return (ISC_R_UNEXPECTED); |
1328 | 0 | } |
1329 | 0 | } |
1330 | | |
1331 | | /* |
1332 | | * We currently don't support huge journal entries. |
1333 | | */ |
1334 | 0 | total = j->x.pos[1].offset - j->x.pos[0].offset; |
1335 | 0 | if (total >= DNS_JOURNAL_SIZE_MAX) { |
1336 | 0 | isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, |
1337 | 0 | "transaction too big to be stored in journal: " |
1338 | 0 | "%" PRIu64 "b (max is %" PRIu64 "b)", |
1339 | 0 | total, (uint64_t)DNS_JOURNAL_SIZE_MAX); |
1340 | 0 | return (ISC_R_UNEXPECTED); |
1341 | 0 | } |
1342 | | |
1343 | | /* |
1344 | | * Some old journal entries may become non-addressable |
1345 | | * when we increment the current serial number. Purge them |
1346 | | * by stepping header.begin forward to the first addressable |
1347 | | * transaction. Also purge them from the index. |
1348 | | */ |
1349 | 0 | if (!JOURNAL_EMPTY(&j->header)) { |
1350 | 0 | while (!DNS_SERIAL_GT(j->x.pos[1].serial, |
1351 | 0 | j->header.begin.serial)) |
1352 | 0 | { |
1353 | 0 | CHECK(journal_next(j, &j->header.begin)); |
1354 | 0 | } |
1355 | 0 | index_invalidate(j, j->x.pos[1].serial); |
1356 | 0 | } |
1357 | | #ifdef notyet |
1358 | | if (DNS_SERIAL_GT(last_dumped_serial, j->x.pos[1].serial)) { |
1359 | | force_dump(...); |
1360 | | } |
1361 | | #endif /* ifdef notyet */ |
1362 | | |
1363 | | /* |
1364 | | * Commit the transaction data to stable storage. |
1365 | | */ |
1366 | 0 | CHECK(journal_fsync(j)); |
1367 | | |
1368 | 0 | if (j->state == JOURNAL_STATE_TRANSACTION) { |
1369 | 0 | off_t offset; |
1370 | 0 | offset = (j->x.pos[1].offset - j->x.pos[0].offset) - |
1371 | 0 | (j->header_ver1 ? sizeof(journal_rawxhdr_ver1_t) |
1372 | 0 | : sizeof(journal_rawxhdr_t)); |
1373 | | /* |
1374 | | * Update the transaction header. |
1375 | | */ |
1376 | 0 | CHECK(journal_seek(j, j->x.pos[0].offset)); |
1377 | 0 | CHECK(journal_write_xhdr(j, offset, j->x.n_rr, |
1378 | 0 | j->x.pos[0].serial, |
1379 | 0 | j->x.pos[1].serial)); |
1380 | 0 | } |
1381 | | |
1382 | | /* |
1383 | | * Update the journal header. |
1384 | | */ |
1385 | 0 | if (JOURNAL_EMPTY(&j->header)) { |
1386 | 0 | j->header.begin = j->x.pos[0]; |
1387 | 0 | } |
1388 | 0 | j->header.end = j->x.pos[1]; |
1389 | 0 | journal_header_encode(&j->header, &rawheader); |
1390 | 0 | CHECK(journal_seek(j, 0)); |
1391 | 0 | CHECK(journal_write(j, &rawheader, sizeof(rawheader))); |
1392 | | |
1393 | | /* |
1394 | | * Update the index. |
1395 | | */ |
1396 | 0 | index_add(j, &j->x.pos[0]); |
1397 | | |
1398 | | /* |
1399 | | * Convert the index into on-disk format and write |
1400 | | * it to disk. |
1401 | | */ |
1402 | 0 | CHECK(index_to_disk(j)); |
1403 | | |
1404 | | /* |
1405 | | * Commit the header to stable storage. |
1406 | | */ |
1407 | 0 | CHECK(journal_fsync(j)); |
1408 | | |
1409 | | /* |
1410 | | * We no longer have a transaction open. |
1411 | | */ |
1412 | 0 | j->state = JOURNAL_STATE_WRITE; |
1413 | |
|
1414 | 0 | result = ISC_R_SUCCESS; |
1415 | |
|
1416 | 0 | failure: |
1417 | 0 | return (result); |
1418 | 0 | } |
1419 | | |
1420 | | isc_result_t |
1421 | 0 | dns_journal_write_transaction(dns_journal_t *j, dns_diff_t *diff) { |
1422 | 0 | isc_result_t result; |
1423 | |
|
1424 | 0 | CHECK(dns_diff_sort(diff, ixfr_order)); |
1425 | 0 | CHECK(dns_journal_begin_transaction(j)); |
1426 | 0 | CHECK(dns_journal_writediff(j, diff)); |
1427 | 0 | CHECK(dns_journal_commit(j)); |
1428 | 0 | result = ISC_R_SUCCESS; |
1429 | 0 | failure: |
1430 | 0 | return (result); |
1431 | 0 | } |
1432 | | |
1433 | | void |
1434 | 0 | dns_journal_destroy(dns_journal_t **journalp) { |
1435 | 0 | dns_journal_t *j = NULL; |
1436 | |
|
1437 | 0 | REQUIRE(journalp != NULL); |
1438 | 0 | REQUIRE(DNS_JOURNAL_VALID(*journalp)); |
1439 | |
|
1440 | 0 | j = *journalp; |
1441 | 0 | *journalp = NULL; |
1442 | |
|
1443 | 0 | j->it.result = ISC_R_FAILURE; |
1444 | 0 | dns_name_invalidate(&j->it.name); |
1445 | 0 | if (j->rawindex != NULL) { |
1446 | 0 | isc_mem_put(j->mctx, j->rawindex, |
1447 | 0 | j->header.index_size * sizeof(journal_rawpos_t)); |
1448 | 0 | } |
1449 | 0 | if (j->index != NULL) { |
1450 | 0 | isc_mem_put(j->mctx, j->index, |
1451 | 0 | j->header.index_size * sizeof(journal_pos_t)); |
1452 | 0 | } |
1453 | 0 | if (j->it.target.base != NULL) { |
1454 | 0 | isc_mem_put(j->mctx, j->it.target.base, j->it.target.length); |
1455 | 0 | } |
1456 | 0 | if (j->it.source.base != NULL) { |
1457 | 0 | isc_mem_put(j->mctx, j->it.source.base, j->it.source.length); |
1458 | 0 | } |
1459 | 0 | if (j->filename != NULL) { |
1460 | 0 | isc_mem_free(j->mctx, j->filename); |
1461 | 0 | } |
1462 | 0 | if (j->fp != NULL) { |
1463 | 0 | (void)isc_stdio_close(j->fp); |
1464 | 0 | } |
1465 | 0 | j->magic = 0; |
1466 | 0 | isc_mem_putanddetach(&j->mctx, j, sizeof(*j)); |
1467 | 0 | } |
1468 | | |
1469 | | /* |
1470 | | * Roll the open journal 'j' into the database 'db'. |
1471 | | * A new database version will be created. |
1472 | | */ |
1473 | | |
1474 | | /* XXX Share code with incoming IXFR? */ |
1475 | | |
1476 | | isc_result_t |
1477 | 0 | dns_journal_rollforward(dns_journal_t *j, dns_db_t *db, unsigned int options) { |
1478 | 0 | isc_buffer_t source; /* Transaction data from disk */ |
1479 | 0 | isc_buffer_t target; /* Ditto after _fromwire check */ |
1480 | 0 | uint32_t db_serial; /* Database SOA serial */ |
1481 | 0 | uint32_t end_serial; /* Last journal SOA serial */ |
1482 | 0 | isc_result_t result; |
1483 | 0 | dns_dbversion_t *ver = NULL; |
1484 | 0 | journal_pos_t pos; |
1485 | 0 | dns_diff_t diff; |
1486 | 0 | unsigned int n_soa = 0; |
1487 | 0 | unsigned int n_put = 0; |
1488 | 0 | dns_diffop_t op; |
1489 | |
|
1490 | 0 | REQUIRE(DNS_JOURNAL_VALID(j)); |
1491 | 0 | REQUIRE(DNS_DB_VALID(db)); |
1492 | |
|
1493 | 0 | dns_diff_init(j->mctx, &diff); |
1494 | | |
1495 | | /* |
1496 | | * Set up empty initial buffers for unchecked and checked |
1497 | | * wire format transaction data. They will be reallocated |
1498 | | * later. |
1499 | | */ |
1500 | 0 | isc_buffer_init(&source, NULL, 0); |
1501 | 0 | isc_buffer_init(&target, NULL, 0); |
1502 | | |
1503 | | /* |
1504 | | * Create the new database version. |
1505 | | */ |
1506 | 0 | CHECK(dns_db_newversion(db, &ver)); |
1507 | | |
1508 | | /* |
1509 | | * Get the current database SOA serial number. |
1510 | | */ |
1511 | 0 | CHECK(dns_db_getsoaserial(db, ver, &db_serial)); |
1512 | | |
1513 | | /* |
1514 | | * Locate a journal entry for the current database serial. |
1515 | | */ |
1516 | 0 | CHECK(journal_find(j, db_serial, &pos)); |
1517 | | |
1518 | 0 | end_serial = dns_journal_last_serial(j); |
1519 | | |
1520 | | /* |
1521 | | * If we're reading a version 1 file, scan all the transactions |
1522 | | * to see if the journal needs rewriting: if any outdated |
1523 | | * transaction headers are found, j->recovered will be set. |
1524 | | */ |
1525 | 0 | if (j->header_ver1) { |
1526 | 0 | uint32_t start_serial = dns_journal_first_serial(j); |
1527 | |
|
1528 | 0 | CHECK(dns_journal_iter_init(j, start_serial, db_serial, NULL)); |
1529 | 0 | for (result = dns_journal_first_rr(j); result == ISC_R_SUCCESS; |
1530 | 0 | result = dns_journal_next_rr(j)) |
1531 | 0 | { |
1532 | 0 | continue; |
1533 | 0 | } |
1534 | 0 | } |
1535 | | |
1536 | 0 | if (db_serial == end_serial) { |
1537 | 0 | CHECK(DNS_R_UPTODATE); |
1538 | 0 | } |
1539 | | |
1540 | 0 | CHECK(dns_journal_iter_init(j, db_serial, end_serial, NULL)); |
1541 | 0 | for (result = dns_journal_first_rr(j); result == ISC_R_SUCCESS; |
1542 | 0 | result = dns_journal_next_rr(j)) |
1543 | 0 | { |
1544 | 0 | dns_name_t *name = NULL; |
1545 | 0 | dns_rdata_t *rdata = NULL; |
1546 | 0 | dns_difftuple_t *tuple = NULL; |
1547 | 0 | uint32_t ttl; |
1548 | |
|
1549 | 0 | dns_journal_current_rr(j, &name, &ttl, &rdata); |
1550 | |
|
1551 | 0 | if (rdata->type == dns_rdatatype_soa) { |
1552 | 0 | n_soa++; |
1553 | 0 | if (n_soa == 2) { |
1554 | 0 | db_serial = j->it.current_serial; |
1555 | 0 | } |
1556 | 0 | } |
1557 | |
|
1558 | 0 | if (n_soa == 3) { |
1559 | 0 | n_soa = 1; |
1560 | 0 | } |
1561 | 0 | if (n_soa == 0) { |
1562 | 0 | isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, |
1563 | 0 | "%s: journal file corrupt: missing " |
1564 | 0 | "initial SOA", |
1565 | 0 | j->filename); |
1566 | 0 | FAIL(ISC_R_UNEXPECTED); |
1567 | 0 | } |
1568 | 0 | if ((options & DNS_JOURNALOPT_RESIGN) != 0) { |
1569 | 0 | op = (n_soa == 1) ? DNS_DIFFOP_DELRESIGN |
1570 | 0 | : DNS_DIFFOP_ADDRESIGN; |
1571 | 0 | } else { |
1572 | 0 | op = (n_soa == 1) ? DNS_DIFFOP_DEL : DNS_DIFFOP_ADD; |
1573 | 0 | } |
1574 | |
|
1575 | 0 | CHECK(dns_difftuple_create(diff.mctx, op, name, ttl, rdata, |
1576 | 0 | &tuple)); |
1577 | 0 | dns_diff_append(&diff, &tuple); |
1578 | |
|
1579 | 0 | if (++n_put > 100) { |
1580 | 0 | isc_log_write(JOURNAL_DEBUG_LOGARGS(3), |
1581 | 0 | "%s: applying diff to database (%u)", |
1582 | 0 | j->filename, db_serial); |
1583 | 0 | (void)dns_diff_print(&diff, NULL); |
1584 | 0 | CHECK(dns_diff_apply(&diff, db, ver)); |
1585 | 0 | dns_diff_clear(&diff); |
1586 | 0 | n_put = 0; |
1587 | 0 | } |
1588 | 0 | } |
1589 | 0 | if (result == ISC_R_NOMORE) { |
1590 | 0 | result = ISC_R_SUCCESS; |
1591 | 0 | } |
1592 | 0 | CHECK(result); |
1593 | | |
1594 | 0 | if (n_put != 0) { |
1595 | 0 | isc_log_write(JOURNAL_DEBUG_LOGARGS(3), |
1596 | 0 | "%s: applying final diff to database (%u)", |
1597 | 0 | j->filename, db_serial); |
1598 | 0 | (void)dns_diff_print(&diff, NULL); |
1599 | 0 | CHECK(dns_diff_apply(&diff, db, ver)); |
1600 | 0 | dns_diff_clear(&diff); |
1601 | 0 | } |
1602 | | |
1603 | 0 | failure: |
1604 | 0 | if (ver != NULL) { |
1605 | 0 | dns_db_closeversion(db, &ver, |
1606 | 0 | result == ISC_R_SUCCESS ? true : false); |
1607 | 0 | } |
1608 | |
|
1609 | 0 | if (source.base != NULL) { |
1610 | 0 | isc_mem_put(j->mctx, source.base, source.length); |
1611 | 0 | } |
1612 | 0 | if (target.base != NULL) { |
1613 | 0 | isc_mem_put(j->mctx, target.base, target.length); |
1614 | 0 | } |
1615 | |
|
1616 | 0 | dns_diff_clear(&diff); |
1617 | |
|
1618 | 0 | INSIST(ver == NULL); |
1619 | |
|
1620 | 0 | return (result); |
1621 | 0 | } |
1622 | | |
1623 | | isc_result_t |
1624 | | dns_journal_print(isc_mem_t *mctx, uint32_t flags, const char *filename, |
1625 | 0 | FILE *file) { |
1626 | 0 | dns_journal_t *j = NULL; |
1627 | 0 | isc_buffer_t source; /* Transaction data from disk */ |
1628 | 0 | isc_buffer_t target; /* Ditto after _fromwire check */ |
1629 | 0 | uint32_t start_serial; /* Database SOA serial */ |
1630 | 0 | uint32_t end_serial; /* Last journal SOA serial */ |
1631 | 0 | isc_result_t result; |
1632 | 0 | dns_diff_t diff; |
1633 | 0 | unsigned int n_soa = 0; |
1634 | 0 | unsigned int n_put = 0; |
1635 | 0 | bool printxhdr = ((flags & DNS_JOURNAL_PRINTXHDR) != 0); |
1636 | |
|
1637 | 0 | REQUIRE(filename != NULL); |
1638 | |
|
1639 | 0 | result = dns_journal_open(mctx, filename, DNS_JOURNAL_READ, &j); |
1640 | 0 | if (result == ISC_R_NOTFOUND) { |
1641 | 0 | isc_log_write(JOURNAL_DEBUG_LOGARGS(3), "no journal file"); |
1642 | 0 | return (DNS_R_NOJOURNAL); |
1643 | 0 | } else if (result != ISC_R_SUCCESS) { |
1644 | 0 | isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, |
1645 | 0 | "journal open failure: %s: %s", |
1646 | 0 | isc_result_totext(result), filename); |
1647 | 0 | return (result); |
1648 | 0 | } |
1649 | | |
1650 | 0 | if (printxhdr) { |
1651 | 0 | fprintf(file, "Journal format = %sHeader version = %d\n", |
1652 | 0 | j->header.format + 1, j->header_ver1 ? 1 : 2); |
1653 | 0 | fprintf(file, "Start serial = %u\n", j->header.begin.serial); |
1654 | 0 | fprintf(file, "End serial = %u\n", j->header.end.serial); |
1655 | 0 | fprintf(file, "Index (size = %u):\n", j->header.index_size); |
1656 | 0 | for (uint32_t i = 0; i < j->header.index_size; i++) { |
1657 | 0 | if (j->index[i].offset == 0) { |
1658 | 0 | fputc('\n', file); |
1659 | 0 | break; |
1660 | 0 | } |
1661 | 0 | fprintf(file, "%lld", (long long)j->index[i].offset); |
1662 | 0 | fputc((i + 1) % 8 == 0 ? '\n' : ' ', file); |
1663 | 0 | } |
1664 | 0 | } |
1665 | 0 | if (j->header.serialset) { |
1666 | 0 | fprintf(file, "Source serial = %u\n", j->header.sourceserial); |
1667 | 0 | } |
1668 | 0 | dns_diff_init(j->mctx, &diff); |
1669 | | |
1670 | | /* |
1671 | | * Set up empty initial buffers for unchecked and checked |
1672 | | * wire format transaction data. They will be reallocated |
1673 | | * later. |
1674 | | */ |
1675 | 0 | isc_buffer_init(&source, NULL, 0); |
1676 | 0 | isc_buffer_init(&target, NULL, 0); |
1677 | |
|
1678 | 0 | start_serial = dns_journal_first_serial(j); |
1679 | 0 | end_serial = dns_journal_last_serial(j); |
1680 | |
|
1681 | 0 | CHECK(dns_journal_iter_init(j, start_serial, end_serial, NULL)); |
1682 | | |
1683 | 0 | for (result = dns_journal_first_rr(j); result == ISC_R_SUCCESS; |
1684 | 0 | result = dns_journal_next_rr(j)) |
1685 | 0 | { |
1686 | 0 | dns_name_t *name = NULL; |
1687 | 0 | dns_rdata_t *rdata = NULL; |
1688 | 0 | dns_difftuple_t *tuple = NULL; |
1689 | 0 | static uint32_t i = 0; |
1690 | 0 | bool print = false; |
1691 | 0 | uint32_t ttl; |
1692 | |
|
1693 | 0 | dns_journal_current_rr(j, &name, &ttl, &rdata); |
1694 | |
|
1695 | 0 | if (rdata->type == dns_rdatatype_soa) { |
1696 | 0 | n_soa++; |
1697 | 0 | if (n_soa == 3) { |
1698 | 0 | n_soa = 1; |
1699 | 0 | } |
1700 | 0 | if (n_soa == 1) { |
1701 | 0 | print = printxhdr; |
1702 | 0 | } |
1703 | 0 | } |
1704 | 0 | if (n_soa == 0) { |
1705 | 0 | isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, |
1706 | 0 | "%s: journal file corrupt: missing " |
1707 | 0 | "initial SOA", |
1708 | 0 | j->filename); |
1709 | 0 | FAIL(ISC_R_UNEXPECTED); |
1710 | 0 | } |
1711 | | |
1712 | 0 | if (print) { |
1713 | 0 | fprintf(file, |
1714 | 0 | "Transaction: version %d offset %lld size %u " |
1715 | 0 | "rrcount %u start %u end %u\n", |
1716 | 0 | j->xhdr_version, (long long)j->it.cpos.offset, |
1717 | 0 | j->curxhdr.size, j->curxhdr.count, |
1718 | 0 | j->curxhdr.serial0, j->curxhdr.serial1); |
1719 | 0 | if (j->it.cpos.offset > j->index[i].offset) { |
1720 | 0 | fprintf(file, |
1721 | 0 | "ERROR: Offset mismatch, " |
1722 | 0 | "expected %lld\n", |
1723 | 0 | (long long)j->index[i].offset); |
1724 | 0 | } else if (j->it.cpos.offset == j->index[i].offset) { |
1725 | 0 | i++; |
1726 | 0 | } |
1727 | 0 | } |
1728 | 0 | CHECK(dns_difftuple_create( |
1729 | 0 | diff.mctx, n_soa == 1 ? DNS_DIFFOP_DEL : DNS_DIFFOP_ADD, |
1730 | 0 | name, ttl, rdata, &tuple)); |
1731 | 0 | dns_diff_append(&diff, &tuple); |
1732 | |
|
1733 | 0 | if (++n_put > 100 || printxhdr) { |
1734 | 0 | result = dns_diff_print(&diff, file); |
1735 | 0 | dns_diff_clear(&diff); |
1736 | 0 | n_put = 0; |
1737 | 0 | if (result != ISC_R_SUCCESS) { |
1738 | 0 | break; |
1739 | 0 | } |
1740 | 0 | } |
1741 | 0 | } |
1742 | 0 | if (result == ISC_R_NOMORE) { |
1743 | 0 | result = ISC_R_SUCCESS; |
1744 | 0 | } |
1745 | 0 | CHECK(result); |
1746 | | |
1747 | 0 | if (n_put != 0) { |
1748 | 0 | result = dns_diff_print(&diff, file); |
1749 | 0 | dns_diff_clear(&diff); |
1750 | 0 | } |
1751 | 0 | goto cleanup; |
1752 | | |
1753 | 0 | failure: |
1754 | 0 | isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, |
1755 | 0 | "%s: cannot print: journal file corrupt", j->filename); |
1756 | |
|
1757 | 0 | cleanup: |
1758 | 0 | if (source.base != NULL) { |
1759 | 0 | isc_mem_put(j->mctx, source.base, source.length); |
1760 | 0 | } |
1761 | 0 | if (target.base != NULL) { |
1762 | 0 | isc_mem_put(j->mctx, target.base, target.length); |
1763 | 0 | } |
1764 | |
|
1765 | 0 | dns_diff_clear(&diff); |
1766 | 0 | dns_journal_destroy(&j); |
1767 | |
|
1768 | 0 | return (result); |
1769 | 0 | } |
1770 | | |
1771 | | /**************************************************************************/ |
1772 | | /* |
1773 | | * Miscellaneous accessors. |
1774 | | */ |
1775 | | bool |
1776 | 0 | dns_journal_empty(dns_journal_t *j) { |
1777 | 0 | return (JOURNAL_EMPTY(&j->header)); |
1778 | 0 | } |
1779 | | |
1780 | | bool |
1781 | 0 | dns_journal_recovered(dns_journal_t *j) { |
1782 | 0 | return (j->recovered); |
1783 | 0 | } |
1784 | | |
1785 | | uint32_t |
1786 | 0 | dns_journal_first_serial(dns_journal_t *j) { |
1787 | 0 | return (j->header.begin.serial); |
1788 | 0 | } |
1789 | | |
1790 | | uint32_t |
1791 | 0 | dns_journal_last_serial(dns_journal_t *j) { |
1792 | 0 | return (j->header.end.serial); |
1793 | 0 | } |
1794 | | |
1795 | | void |
1796 | 0 | dns_journal_set_sourceserial(dns_journal_t *j, uint32_t sourceserial) { |
1797 | 0 | REQUIRE(j->state == JOURNAL_STATE_WRITE || |
1798 | 0 | j->state == JOURNAL_STATE_INLINE || |
1799 | 0 | j->state == JOURNAL_STATE_TRANSACTION); |
1800 | |
|
1801 | 0 | j->header.sourceserial = sourceserial; |
1802 | 0 | j->header.serialset = true; |
1803 | 0 | if (j->state == JOURNAL_STATE_WRITE) { |
1804 | 0 | j->state = JOURNAL_STATE_INLINE; |
1805 | 0 | } |
1806 | 0 | } |
1807 | | |
1808 | | bool |
1809 | 0 | dns_journal_get_sourceserial(dns_journal_t *j, uint32_t *sourceserial) { |
1810 | 0 | REQUIRE(sourceserial != NULL); |
1811 | |
|
1812 | 0 | if (!j->header.serialset) { |
1813 | 0 | return (false); |
1814 | 0 | } |
1815 | 0 | *sourceserial = j->header.sourceserial; |
1816 | 0 | return (true); |
1817 | 0 | } |
1818 | | |
1819 | | /**************************************************************************/ |
1820 | | /* |
1821 | | * Iteration support. |
1822 | | * |
1823 | | * When serving an outgoing IXFR, we transmit a part the journal starting |
1824 | | * at the serial number in the IXFR request and ending at the serial |
1825 | | * number that is current when the IXFR request arrives. The ending |
1826 | | * serial number is not necessarily at the end of the journal: |
1827 | | * the journal may grow while the IXFR is in progress, but we stop |
1828 | | * when we reach the serial number that was current when the IXFR started. |
1829 | | */ |
1830 | | |
1831 | | static isc_result_t |
1832 | | read_one_rr(dns_journal_t *j); |
1833 | | |
1834 | | /* |
1835 | | * Make sure the buffer 'b' is has at least 'size' bytes |
1836 | | * allocated, and clear it. |
1837 | | * |
1838 | | * Requires: |
1839 | | * Either b->base is NULL, or it points to b->length bytes of memory |
1840 | | * previously allocated by isc_mem_get(). |
1841 | | */ |
1842 | | |
1843 | | static isc_result_t |
1844 | 0 | size_buffer(isc_mem_t *mctx, isc_buffer_t *b, unsigned int size) { |
1845 | 0 | if (b->length < size) { |
1846 | 0 | void *mem = isc_mem_get(mctx, size); |
1847 | 0 | if (mem == NULL) { |
1848 | 0 | return (ISC_R_NOMEMORY); |
1849 | 0 | } |
1850 | 0 | if (b->base != NULL) { |
1851 | 0 | isc_mem_put(mctx, b->base, b->length); |
1852 | 0 | } |
1853 | 0 | b->base = mem; |
1854 | 0 | b->length = size; |
1855 | 0 | } |
1856 | 0 | isc_buffer_clear(b); |
1857 | 0 | return (ISC_R_SUCCESS); |
1858 | 0 | } |
1859 | | |
1860 | | isc_result_t |
1861 | | dns_journal_iter_init(dns_journal_t *j, uint32_t begin_serial, |
1862 | 0 | uint32_t end_serial, size_t *xfrsizep) { |
1863 | 0 | isc_result_t result; |
1864 | |
|
1865 | 0 | CHECK(journal_find(j, begin_serial, &j->it.bpos)); |
1866 | 0 | INSIST(j->it.bpos.serial == begin_serial); |
1867 | |
|
1868 | 0 | CHECK(journal_find(j, end_serial, &j->it.epos)); |
1869 | 0 | INSIST(j->it.epos.serial == end_serial); |
1870 | |
|
1871 | 0 | if (xfrsizep != NULL) { |
1872 | 0 | journal_pos_t pos = j->it.bpos; |
1873 | 0 | journal_xhdr_t xhdr; |
1874 | 0 | uint64_t size = 0; |
1875 | 0 | uint32_t count = 0; |
1876 | | |
1877 | | /* |
1878 | | * We already know the beginning and ending serial |
1879 | | * numbers are in the journal. Scan through them, |
1880 | | * adding up sizes and RR counts so we can calculate |
1881 | | * the IXFR size. |
1882 | | */ |
1883 | 0 | do { |
1884 | 0 | CHECK(journal_seek(j, pos.offset)); |
1885 | 0 | CHECK(journal_read_xhdr(j, &xhdr)); |
1886 | | |
1887 | 0 | if (j->header_ver1) { |
1888 | 0 | CHECK(maybe_fixup_xhdr(j, &xhdr, pos.serial, |
1889 | 0 | pos.offset)); |
1890 | 0 | } |
1891 | | |
1892 | | /* |
1893 | | * Check that xhdr is consistent. |
1894 | | */ |
1895 | 0 | if (xhdr.serial0 != pos.serial || |
1896 | 0 | isc_serial_le(xhdr.serial1, xhdr.serial0)) |
1897 | 0 | { |
1898 | 0 | CHECK(ISC_R_UNEXPECTED); |
1899 | 0 | } |
1900 | | |
1901 | 0 | size += xhdr.size; |
1902 | 0 | count += xhdr.count; |
1903 | |
|
1904 | 0 | result = journal_next(j, &pos); |
1905 | 0 | if (result == ISC_R_NOMORE) { |
1906 | 0 | result = ISC_R_SUCCESS; |
1907 | 0 | } |
1908 | 0 | CHECK(result); |
1909 | 0 | } while (pos.serial != end_serial); |
1910 | | |
1911 | | /* |
1912 | | * For each RR, subtract the length of the RR header, |
1913 | | * as this would not be present in IXFR messages. |
1914 | | * (We don't need to worry about the transaction header |
1915 | | * because that was already excluded from xdr.size.) |
1916 | | */ |
1917 | 0 | *xfrsizep = size - (count * sizeof(journal_rawrrhdr_t)); |
1918 | 0 | } |
1919 | | |
1920 | 0 | result = ISC_R_SUCCESS; |
1921 | 0 | failure: |
1922 | 0 | j->it.result = result; |
1923 | 0 | return (j->it.result); |
1924 | 0 | } |
1925 | | |
1926 | | isc_result_t |
1927 | 0 | dns_journal_first_rr(dns_journal_t *j) { |
1928 | 0 | isc_result_t result; |
1929 | | |
1930 | | /* |
1931 | | * Seek to the beginning of the first transaction we are |
1932 | | * interested in. |
1933 | | */ |
1934 | 0 | CHECK(journal_seek(j, j->it.bpos.offset)); |
1935 | 0 | j->it.current_serial = j->it.bpos.serial; |
1936 | |
|
1937 | 0 | j->it.xsize = 0; /* We have no transaction data yet... */ |
1938 | 0 | j->it.xpos = 0; /* ...and haven't used any of it. */ |
1939 | |
|
1940 | 0 | return (read_one_rr(j)); |
1941 | | |
1942 | 0 | failure: |
1943 | 0 | return (result); |
1944 | 0 | } |
1945 | | |
1946 | | static isc_result_t |
1947 | 0 | read_one_rr(dns_journal_t *j) { |
1948 | 0 | isc_result_t result; |
1949 | 0 | dns_rdatatype_t rdtype; |
1950 | 0 | dns_rdataclass_t rdclass; |
1951 | 0 | unsigned int rdlen; |
1952 | 0 | uint32_t ttl; |
1953 | 0 | journal_xhdr_t xhdr; |
1954 | 0 | journal_rrhdr_t rrhdr; |
1955 | 0 | dns_journal_t save = *j; |
1956 | |
|
1957 | 0 | if (j->offset > j->it.epos.offset) { |
1958 | 0 | isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, |
1959 | 0 | "%s: journal corrupt: possible integer overflow", |
1960 | 0 | j->filename); |
1961 | 0 | return (ISC_R_UNEXPECTED); |
1962 | 0 | } |
1963 | 0 | if (j->offset == j->it.epos.offset) { |
1964 | 0 | return (ISC_R_NOMORE); |
1965 | 0 | } |
1966 | 0 | if (j->it.xpos == j->it.xsize) { |
1967 | | /* |
1968 | | * We are at a transaction boundary. |
1969 | | * Read another transaction header. |
1970 | | */ |
1971 | 0 | CHECK(journal_read_xhdr(j, &xhdr)); |
1972 | 0 | if (xhdr.size == 0) { |
1973 | 0 | isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, |
1974 | 0 | "%s: journal corrupt: empty transaction", |
1975 | 0 | j->filename); |
1976 | 0 | FAIL(ISC_R_UNEXPECTED); |
1977 | 0 | } |
1978 | | |
1979 | 0 | if (j->header_ver1) { |
1980 | 0 | CHECK(maybe_fixup_xhdr(j, &xhdr, j->it.current_serial, |
1981 | 0 | save.offset)); |
1982 | 0 | } |
1983 | | |
1984 | 0 | if (xhdr.serial0 != j->it.current_serial || |
1985 | 0 | isc_serial_le(xhdr.serial1, xhdr.serial0)) |
1986 | 0 | { |
1987 | 0 | isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, |
1988 | 0 | "%s: journal file corrupt: " |
1989 | 0 | "expected serial %u, got %u", |
1990 | 0 | j->filename, j->it.current_serial, |
1991 | 0 | xhdr.serial0); |
1992 | 0 | FAIL(ISC_R_UNEXPECTED); |
1993 | 0 | } |
1994 | | |
1995 | 0 | j->it.xsize = xhdr.size; |
1996 | 0 | j->it.xpos = 0; |
1997 | 0 | } |
1998 | | /* |
1999 | | * Read an RR. |
2000 | | */ |
2001 | 0 | CHECK(journal_read_rrhdr(j, &rrhdr)); |
2002 | | /* |
2003 | | * Perform a sanity check on the journal RR size. |
2004 | | * The smallest possible RR has a 1-byte owner name |
2005 | | * and a 10-byte header. The largest possible |
2006 | | * RR has 65535 bytes of data, a header, and a maximum- |
2007 | | * size owner name, well below 70 k total. |
2008 | | */ |
2009 | 0 | if (rrhdr.size < 1 + 10 || rrhdr.size > 70000) { |
2010 | 0 | isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, |
2011 | 0 | "%s: journal corrupt: impossible RR size " |
2012 | 0 | "(%d bytes)", |
2013 | 0 | j->filename, rrhdr.size); |
2014 | 0 | FAIL(ISC_R_UNEXPECTED); |
2015 | 0 | } |
2016 | | |
2017 | 0 | CHECK(size_buffer(j->mctx, &j->it.source, rrhdr.size)); |
2018 | 0 | CHECK(journal_read(j, j->it.source.base, rrhdr.size)); |
2019 | 0 | isc_buffer_add(&j->it.source, rrhdr.size); |
2020 | | |
2021 | | /* |
2022 | | * The target buffer is made the same size |
2023 | | * as the source buffer, with the assumption that when |
2024 | | * no compression in present, the output of dns_*_fromwire() |
2025 | | * is no larger than the input. |
2026 | | */ |
2027 | 0 | CHECK(size_buffer(j->mctx, &j->it.target, rrhdr.size)); |
2028 | | |
2029 | | /* |
2030 | | * Parse the owner name. We don't know where it |
2031 | | * ends yet, so we make the entire "remaining" |
2032 | | * part of the buffer "active". |
2033 | | */ |
2034 | 0 | isc_buffer_setactive(&j->it.source, |
2035 | 0 | j->it.source.used - j->it.source.current); |
2036 | 0 | CHECK(dns_name_fromwire(&j->it.name, &j->it.source, j->it.dctx, |
2037 | 0 | &j->it.target)); |
2038 | | |
2039 | | /* |
2040 | | * Check that the RR header is there, and parse it. |
2041 | | */ |
2042 | 0 | if (isc_buffer_remaininglength(&j->it.source) < 10) { |
2043 | 0 | FAIL(DNS_R_FORMERR); |
2044 | 0 | } |
2045 | | |
2046 | 0 | rdtype = isc_buffer_getuint16(&j->it.source); |
2047 | 0 | rdclass = isc_buffer_getuint16(&j->it.source); |
2048 | 0 | ttl = isc_buffer_getuint32(&j->it.source); |
2049 | 0 | rdlen = isc_buffer_getuint16(&j->it.source); |
2050 | |
|
2051 | 0 | if (rdlen > DNS_RDATA_MAXLENGTH) { |
2052 | 0 | isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, |
2053 | 0 | "%s: journal corrupt: impossible rdlen " |
2054 | 0 | "(%u bytes)", |
2055 | 0 | j->filename, rdlen); |
2056 | 0 | FAIL(ISC_R_FAILURE); |
2057 | 0 | } |
2058 | | |
2059 | | /* |
2060 | | * Parse the rdata. |
2061 | | */ |
2062 | 0 | if (isc_buffer_remaininglength(&j->it.source) != rdlen) { |
2063 | 0 | FAIL(DNS_R_FORMERR); |
2064 | 0 | } |
2065 | 0 | isc_buffer_setactive(&j->it.source, rdlen); |
2066 | 0 | dns_rdata_reset(&j->it.rdata); |
2067 | 0 | CHECK(dns_rdata_fromwire(&j->it.rdata, rdclass, rdtype, &j->it.source, |
2068 | 0 | j->it.dctx, &j->it.target)); |
2069 | 0 | j->it.ttl = ttl; |
2070 | |
|
2071 | 0 | j->it.xpos += sizeof(journal_rawrrhdr_t) + rrhdr.size; |
2072 | 0 | if (rdtype == dns_rdatatype_soa) { |
2073 | | /* XXX could do additional consistency checks here */ |
2074 | 0 | j->it.current_serial = dns_soa_getserial(&j->it.rdata); |
2075 | 0 | } |
2076 | |
|
2077 | 0 | result = ISC_R_SUCCESS; |
2078 | |
|
2079 | 0 | failure: |
2080 | 0 | j->it.result = result; |
2081 | 0 | return (result); |
2082 | 0 | } |
2083 | | |
2084 | | isc_result_t |
2085 | 0 | dns_journal_next_rr(dns_journal_t *j) { |
2086 | 0 | j->it.result = read_one_rr(j); |
2087 | 0 | return (j->it.result); |
2088 | 0 | } |
2089 | | |
2090 | | void |
2091 | | dns_journal_current_rr(dns_journal_t *j, dns_name_t **name, uint32_t *ttl, |
2092 | 0 | dns_rdata_t **rdata) { |
2093 | 0 | REQUIRE(j->it.result == ISC_R_SUCCESS); |
2094 | 0 | *name = &j->it.name; |
2095 | 0 | *ttl = j->it.ttl; |
2096 | 0 | *rdata = &j->it.rdata; |
2097 | 0 | } |
2098 | | |
2099 | | /**************************************************************************/ |
2100 | | /* |
2101 | | * Generating diffs from databases |
2102 | | */ |
2103 | | |
2104 | | /* |
2105 | | * Construct a diff containing all the RRs at the current name of the |
2106 | | * database iterator 'dbit' in database 'db', version 'ver'. |
2107 | | * Set '*name' to the current name, and append the diff to 'diff'. |
2108 | | * All new tuples will have the operation 'op'. |
2109 | | * |
2110 | | * Requires: 'name' must have buffer large enough to hold the name. |
2111 | | * Typically, a dns_fixedname_t would be used. |
2112 | | */ |
2113 | | static isc_result_t |
2114 | | get_name_diff(dns_db_t *db, dns_dbversion_t *ver, isc_stdtime_t now, |
2115 | | dns_dbiterator_t *dbit, dns_name_t *name, dns_diffop_t op, |
2116 | 0 | dns_diff_t *diff) { |
2117 | 0 | isc_result_t result; |
2118 | 0 | dns_dbnode_t *node = NULL; |
2119 | 0 | dns_rdatasetiter_t *rdsiter = NULL; |
2120 | 0 | dns_difftuple_t *tuple = NULL; |
2121 | |
|
2122 | 0 | result = dns_dbiterator_current(dbit, &node, name); |
2123 | 0 | if (result != ISC_R_SUCCESS) { |
2124 | 0 | return (result); |
2125 | 0 | } |
2126 | | |
2127 | 0 | result = dns_db_allrdatasets(db, node, ver, 0, now, &rdsiter); |
2128 | 0 | if (result != ISC_R_SUCCESS) { |
2129 | 0 | goto cleanup_node; |
2130 | 0 | } |
2131 | | |
2132 | 0 | for (result = dns_rdatasetiter_first(rdsiter); result == ISC_R_SUCCESS; |
2133 | 0 | result = dns_rdatasetiter_next(rdsiter)) |
2134 | 0 | { |
2135 | 0 | dns_rdataset_t rdataset; |
2136 | |
|
2137 | 0 | dns_rdataset_init(&rdataset); |
2138 | 0 | dns_rdatasetiter_current(rdsiter, &rdataset); |
2139 | |
|
2140 | 0 | for (result = dns_rdataset_first(&rdataset); |
2141 | 0 | result == ISC_R_SUCCESS; |
2142 | 0 | result = dns_rdataset_next(&rdataset)) |
2143 | 0 | { |
2144 | 0 | dns_rdata_t rdata = DNS_RDATA_INIT; |
2145 | 0 | dns_rdataset_current(&rdataset, &rdata); |
2146 | 0 | result = dns_difftuple_create(diff->mctx, op, name, |
2147 | 0 | rdataset.ttl, &rdata, |
2148 | 0 | &tuple); |
2149 | 0 | if (result != ISC_R_SUCCESS) { |
2150 | 0 | dns_rdataset_disassociate(&rdataset); |
2151 | 0 | goto cleanup_iterator; |
2152 | 0 | } |
2153 | 0 | dns_diff_append(diff, &tuple); |
2154 | 0 | } |
2155 | 0 | dns_rdataset_disassociate(&rdataset); |
2156 | 0 | if (result != ISC_R_NOMORE) { |
2157 | 0 | goto cleanup_iterator; |
2158 | 0 | } |
2159 | 0 | } |
2160 | 0 | if (result != ISC_R_NOMORE) { |
2161 | 0 | goto cleanup_iterator; |
2162 | 0 | } |
2163 | | |
2164 | 0 | result = ISC_R_SUCCESS; |
2165 | |
|
2166 | 0 | cleanup_iterator: |
2167 | 0 | dns_rdatasetiter_destroy(&rdsiter); |
2168 | |
|
2169 | 0 | cleanup_node: |
2170 | 0 | dns_db_detachnode(db, &node); |
2171 | |
|
2172 | 0 | return (result); |
2173 | 0 | } |
2174 | | |
2175 | | /* |
2176 | | * Comparison function for use by dns_diff_subtract when sorting |
2177 | | * the diffs to be subtracted. The sort keys are the rdata type |
2178 | | * and the rdata itself. The owner name is ignored, because |
2179 | | * it is known to be the same for all tuples. |
2180 | | */ |
2181 | | static int |
2182 | 0 | rdata_order(const void *av, const void *bv) { |
2183 | 0 | dns_difftuple_t const *const *ap = av; |
2184 | 0 | dns_difftuple_t const *const *bp = bv; |
2185 | 0 | dns_difftuple_t const *a = *ap; |
2186 | 0 | dns_difftuple_t const *b = *bp; |
2187 | 0 | int r; |
2188 | 0 | r = (b->rdata.type - a->rdata.type); |
2189 | 0 | if (r != 0) { |
2190 | 0 | return (r); |
2191 | 0 | } |
2192 | 0 | r = dns_rdata_compare(&a->rdata, &b->rdata); |
2193 | 0 | return (r); |
2194 | 0 | } |
2195 | | |
2196 | | static isc_result_t |
2197 | 0 | dns_diff_subtract(dns_diff_t diff[2], dns_diff_t *r) { |
2198 | 0 | isc_result_t result; |
2199 | 0 | dns_difftuple_t *p[2]; |
2200 | 0 | int i, t; |
2201 | 0 | bool append; |
2202 | 0 | dns_difftuplelist_t add, del; |
2203 | |
|
2204 | 0 | CHECK(dns_diff_sort(&diff[0], rdata_order)); |
2205 | 0 | CHECK(dns_diff_sort(&diff[1], rdata_order)); |
2206 | 0 | ISC_LIST_INIT(add); |
2207 | 0 | ISC_LIST_INIT(del); |
2208 | |
|
2209 | 0 | for (;;) { |
2210 | 0 | p[0] = ISC_LIST_HEAD(diff[0].tuples); |
2211 | 0 | p[1] = ISC_LIST_HEAD(diff[1].tuples); |
2212 | 0 | if (p[0] == NULL && p[1] == NULL) { |
2213 | 0 | break; |
2214 | 0 | } |
2215 | | |
2216 | 0 | for (i = 0; i < 2; i++) { |
2217 | 0 | if (p[!i] == NULL) { |
2218 | 0 | dns_difftuplelist_t *l = (i == 0) ? &add : &del; |
2219 | 0 | ISC_LIST_UNLINK(diff[i].tuples, p[i], link); |
2220 | 0 | ISC_LIST_APPEND(*l, p[i], link); |
2221 | 0 | goto next; |
2222 | 0 | } |
2223 | 0 | } |
2224 | 0 | t = rdata_order(&p[0], &p[1]); |
2225 | 0 | if (t < 0) { |
2226 | 0 | ISC_LIST_UNLINK(diff[0].tuples, p[0], link); |
2227 | 0 | ISC_LIST_APPEND(add, p[0], link); |
2228 | 0 | goto next; |
2229 | 0 | } |
2230 | 0 | if (t > 0) { |
2231 | 0 | ISC_LIST_UNLINK(diff[1].tuples, p[1], link); |
2232 | 0 | ISC_LIST_APPEND(del, p[1], link); |
2233 | 0 | goto next; |
2234 | 0 | } |
2235 | 0 | INSIST(t == 0); |
2236 | | /* |
2237 | | * Identical RRs in both databases; skip them both |
2238 | | * if the ttl differs. |
2239 | | */ |
2240 | 0 | append = (p[0]->ttl != p[1]->ttl); |
2241 | 0 | for (i = 0; i < 2; i++) { |
2242 | 0 | ISC_LIST_UNLINK(diff[i].tuples, p[i], link); |
2243 | 0 | if (append) { |
2244 | 0 | dns_difftuplelist_t *l = (i == 0) ? &add : &del; |
2245 | 0 | ISC_LIST_APPEND(*l, p[i], link); |
2246 | 0 | } else { |
2247 | 0 | dns_difftuple_free(&p[i]); |
2248 | 0 | } |
2249 | 0 | } |
2250 | 0 | next:; |
2251 | 0 | } |
2252 | 0 | ISC_LIST_APPENDLIST(r->tuples, del, link); |
2253 | 0 | ISC_LIST_APPENDLIST(r->tuples, add, link); |
2254 | 0 | result = ISC_R_SUCCESS; |
2255 | 0 | failure: |
2256 | 0 | return (result); |
2257 | 0 | } |
2258 | | |
2259 | | static isc_result_t |
2260 | | diff_namespace(dns_db_t *dba, dns_dbversion_t *dbvera, dns_db_t *dbb, |
2261 | | dns_dbversion_t *dbverb, unsigned int options, |
2262 | 0 | dns_diff_t *resultdiff) { |
2263 | 0 | dns_db_t *db[2]; |
2264 | 0 | dns_dbversion_t *ver[2]; |
2265 | 0 | dns_dbiterator_t *dbit[2] = { NULL, NULL }; |
2266 | 0 | bool have[2] = { false, false }; |
2267 | 0 | dns_fixedname_t fixname[2]; |
2268 | 0 | isc_result_t result, itresult[2]; |
2269 | 0 | dns_diff_t diff[2]; |
2270 | 0 | int i, t; |
2271 | |
|
2272 | 0 | db[0] = dba, db[1] = dbb; |
2273 | 0 | ver[0] = dbvera, ver[1] = dbverb; |
2274 | |
|
2275 | 0 | dns_diff_init(resultdiff->mctx, &diff[0]); |
2276 | 0 | dns_diff_init(resultdiff->mctx, &diff[1]); |
2277 | |
|
2278 | 0 | dns_fixedname_init(&fixname[0]); |
2279 | 0 | dns_fixedname_init(&fixname[1]); |
2280 | |
|
2281 | 0 | result = dns_db_createiterator(db[0], options, &dbit[0]); |
2282 | 0 | if (result != ISC_R_SUCCESS) { |
2283 | 0 | return (result); |
2284 | 0 | } |
2285 | 0 | result = dns_db_createiterator(db[1], options, &dbit[1]); |
2286 | 0 | if (result != ISC_R_SUCCESS) { |
2287 | 0 | goto cleanup_iterator; |
2288 | 0 | } |
2289 | | |
2290 | 0 | itresult[0] = dns_dbiterator_first(dbit[0]); |
2291 | 0 | itresult[1] = dns_dbiterator_first(dbit[1]); |
2292 | |
|
2293 | 0 | for (;;) { |
2294 | 0 | for (i = 0; i < 2; i++) { |
2295 | 0 | if (!have[i] && itresult[i] == ISC_R_SUCCESS) { |
2296 | 0 | CHECK(get_name_diff( |
2297 | 0 | db[i], ver[i], 0, dbit[i], |
2298 | 0 | dns_fixedname_name(&fixname[i]), |
2299 | 0 | i == 0 ? DNS_DIFFOP_ADD |
2300 | 0 | : DNS_DIFFOP_DEL, |
2301 | 0 | &diff[i])); |
2302 | 0 | itresult[i] = dns_dbiterator_next(dbit[i]); |
2303 | 0 | have[i] = true; |
2304 | 0 | } |
2305 | 0 | } |
2306 | | |
2307 | 0 | if (!have[0] && !have[1]) { |
2308 | 0 | INSIST(ISC_LIST_EMPTY(diff[0].tuples)); |
2309 | 0 | INSIST(ISC_LIST_EMPTY(diff[1].tuples)); |
2310 | 0 | break; |
2311 | 0 | } |
2312 | | |
2313 | 0 | for (i = 0; i < 2; i++) { |
2314 | 0 | if (!have[!i]) { |
2315 | 0 | ISC_LIST_APPENDLIST(resultdiff->tuples, |
2316 | 0 | diff[i].tuples, link); |
2317 | 0 | INSIST(ISC_LIST_EMPTY(diff[i].tuples)); |
2318 | 0 | have[i] = false; |
2319 | 0 | goto next; |
2320 | 0 | } |
2321 | 0 | } |
2322 | | |
2323 | 0 | t = dns_name_compare(dns_fixedname_name(&fixname[0]), |
2324 | 0 | dns_fixedname_name(&fixname[1])); |
2325 | 0 | if (t < 0) { |
2326 | 0 | ISC_LIST_APPENDLIST(resultdiff->tuples, diff[0].tuples, |
2327 | 0 | link); |
2328 | 0 | INSIST(ISC_LIST_EMPTY(diff[0].tuples)); |
2329 | 0 | have[0] = false; |
2330 | 0 | continue; |
2331 | 0 | } |
2332 | 0 | if (t > 0) { |
2333 | 0 | ISC_LIST_APPENDLIST(resultdiff->tuples, diff[1].tuples, |
2334 | 0 | link); |
2335 | 0 | INSIST(ISC_LIST_EMPTY(diff[1].tuples)); |
2336 | 0 | have[1] = false; |
2337 | 0 | continue; |
2338 | 0 | } |
2339 | 0 | INSIST(t == 0); |
2340 | 0 | CHECK(dns_diff_subtract(diff, resultdiff)); |
2341 | 0 | INSIST(ISC_LIST_EMPTY(diff[0].tuples)); |
2342 | 0 | INSIST(ISC_LIST_EMPTY(diff[1].tuples)); |
2343 | 0 | have[0] = have[1] = false; |
2344 | 0 | next:; |
2345 | 0 | } |
2346 | 0 | if (itresult[0] != ISC_R_NOMORE) { |
2347 | 0 | FAIL(itresult[0]); |
2348 | 0 | } |
2349 | 0 | if (itresult[1] != ISC_R_NOMORE) { |
2350 | 0 | FAIL(itresult[1]); |
2351 | 0 | } |
2352 | | |
2353 | 0 | INSIST(ISC_LIST_EMPTY(diff[0].tuples)); |
2354 | 0 | INSIST(ISC_LIST_EMPTY(diff[1].tuples)); |
2355 | |
|
2356 | 0 | failure: |
2357 | 0 | dns_dbiterator_destroy(&dbit[1]); |
2358 | |
|
2359 | 0 | cleanup_iterator: |
2360 | 0 | dns_dbiterator_destroy(&dbit[0]); |
2361 | 0 | dns_diff_clear(&diff[0]); |
2362 | 0 | dns_diff_clear(&diff[1]); |
2363 | 0 | return (result); |
2364 | 0 | } |
2365 | | |
2366 | | /* |
2367 | | * Compare the databases 'dba' and 'dbb' and generate a journal |
2368 | | * entry containing the changes to make 'dba' from 'dbb' (note |
2369 | | * the order). This journal entry will consist of a single, |
2370 | | * possibly very large transaction. |
2371 | | */ |
2372 | | isc_result_t |
2373 | | dns_db_diff(isc_mem_t *mctx, dns_db_t *dba, dns_dbversion_t *dbvera, |
2374 | 0 | dns_db_t *dbb, dns_dbversion_t *dbverb, const char *filename) { |
2375 | 0 | isc_result_t result; |
2376 | 0 | dns_diff_t diff; |
2377 | |
|
2378 | 0 | dns_diff_init(mctx, &diff); |
2379 | |
|
2380 | 0 | result = dns_db_diffx(&diff, dba, dbvera, dbb, dbverb, filename); |
2381 | |
|
2382 | 0 | dns_diff_clear(&diff); |
2383 | |
|
2384 | 0 | return (result); |
2385 | 0 | } |
2386 | | |
2387 | | isc_result_t |
2388 | | dns_db_diffx(dns_diff_t *diff, dns_db_t *dba, dns_dbversion_t *dbvera, |
2389 | 0 | dns_db_t *dbb, dns_dbversion_t *dbverb, const char *filename) { |
2390 | 0 | isc_result_t result; |
2391 | 0 | dns_journal_t *journal = NULL; |
2392 | |
|
2393 | 0 | if (filename != NULL) { |
2394 | 0 | result = dns_journal_open(diff->mctx, filename, |
2395 | 0 | DNS_JOURNAL_CREATE, &journal); |
2396 | 0 | if (result != ISC_R_SUCCESS) { |
2397 | 0 | return (result); |
2398 | 0 | } |
2399 | 0 | } |
2400 | | |
2401 | 0 | CHECK(diff_namespace(dba, dbvera, dbb, dbverb, DNS_DB_NONSEC3, diff)); |
2402 | 0 | CHECK(diff_namespace(dba, dbvera, dbb, dbverb, DNS_DB_NSEC3ONLY, diff)); |
2403 | | |
2404 | 0 | if (journal != NULL) { |
2405 | 0 | if (ISC_LIST_EMPTY(diff->tuples)) { |
2406 | 0 | isc_log_write(JOURNAL_DEBUG_LOGARGS(3), "no changes"); |
2407 | 0 | } else { |
2408 | 0 | CHECK(dns_journal_write_transaction(journal, diff)); |
2409 | 0 | } |
2410 | 0 | } |
2411 | | |
2412 | 0 | failure: |
2413 | 0 | if (journal != NULL) { |
2414 | 0 | dns_journal_destroy(&journal); |
2415 | 0 | } |
2416 | 0 | return (result); |
2417 | 0 | } |
2418 | | |
2419 | | static uint32_t |
2420 | 0 | rrcount(unsigned char *buf, unsigned int size) { |
2421 | 0 | isc_buffer_t b; |
2422 | 0 | uint32_t rrsize, count = 0; |
2423 | |
|
2424 | 0 | isc_buffer_init(&b, buf, size); |
2425 | 0 | isc_buffer_add(&b, size); |
2426 | 0 | while (isc_buffer_remaininglength(&b) > 0) { |
2427 | 0 | rrsize = isc_buffer_getuint32(&b); |
2428 | 0 | INSIST(isc_buffer_remaininglength(&b) >= rrsize); |
2429 | 0 | isc_buffer_forward(&b, rrsize); |
2430 | 0 | count++; |
2431 | 0 | } |
2432 | |
|
2433 | 0 | return (count); |
2434 | 0 | } |
2435 | | |
2436 | | static bool |
2437 | 0 | check_delta(unsigned char *buf, size_t size) { |
2438 | 0 | isc_buffer_t b; |
2439 | 0 | uint32_t rrsize; |
2440 | |
|
2441 | 0 | isc_buffer_init(&b, buf, size); |
2442 | 0 | isc_buffer_add(&b, size); |
2443 | 0 | while (isc_buffer_remaininglength(&b) > 0) { |
2444 | 0 | if (isc_buffer_remaininglength(&b) < 4) { |
2445 | 0 | return (false); |
2446 | 0 | } |
2447 | 0 | rrsize = isc_buffer_getuint32(&b); |
2448 | | /* "." + type + class + ttl + rdlen => 11U */ |
2449 | 0 | if (rrsize < 11U || isc_buffer_remaininglength(&b) < rrsize) { |
2450 | 0 | return (false); |
2451 | 0 | } |
2452 | 0 | isc_buffer_forward(&b, rrsize); |
2453 | 0 | } |
2454 | | |
2455 | 0 | return (true); |
2456 | 0 | } |
2457 | | |
2458 | | isc_result_t |
2459 | | dns_journal_compact(isc_mem_t *mctx, char *filename, uint32_t serial, |
2460 | 0 | uint32_t flags, uint32_t target_size) { |
2461 | 0 | unsigned int i; |
2462 | 0 | journal_pos_t best_guess; |
2463 | 0 | journal_pos_t current_pos; |
2464 | 0 | dns_journal_t *j1 = NULL; |
2465 | 0 | dns_journal_t *j2 = NULL; |
2466 | 0 | journal_rawheader_t rawheader; |
2467 | 0 | unsigned int len; |
2468 | 0 | size_t namelen; |
2469 | 0 | unsigned char *buf = NULL; |
2470 | 0 | unsigned int size = 0; |
2471 | 0 | isc_result_t result; |
2472 | 0 | unsigned int indexend; |
2473 | 0 | char newname[PATH_MAX]; |
2474 | 0 | char backup[PATH_MAX]; |
2475 | 0 | bool is_backup = false; |
2476 | 0 | bool rewrite = false; |
2477 | 0 | bool downgrade = false; |
2478 | |
|
2479 | 0 | REQUIRE(filename != NULL); |
2480 | |
|
2481 | 0 | namelen = strlen(filename); |
2482 | 0 | if (namelen > 4U && strcmp(filename + namelen - 4, ".jnl") == 0) { |
2483 | 0 | namelen -= 4; |
2484 | 0 | } |
2485 | |
|
2486 | 0 | result = snprintf(newname, sizeof(newname), "%.*s.jnw", (int)namelen, |
2487 | 0 | filename); |
2488 | 0 | RUNTIME_CHECK(result < sizeof(newname)); |
2489 | |
|
2490 | 0 | result = snprintf(backup, sizeof(backup), "%.*s.jbk", (int)namelen, |
2491 | 0 | filename); |
2492 | 0 | RUNTIME_CHECK(result < sizeof(backup)); |
2493 | |
|
2494 | 0 | result = journal_open(mctx, filename, false, false, false, &j1); |
2495 | 0 | if (result == ISC_R_NOTFOUND) { |
2496 | 0 | is_backup = true; |
2497 | 0 | result = journal_open(mctx, backup, false, false, false, &j1); |
2498 | 0 | } |
2499 | 0 | if (result != ISC_R_SUCCESS) { |
2500 | 0 | return (result); |
2501 | 0 | } |
2502 | | |
2503 | | /* |
2504 | | * Always perform a re-write when processing a version 1 journal. |
2505 | | */ |
2506 | 0 | rewrite = j1->header_ver1; |
2507 | | |
2508 | | /* |
2509 | | * Check whether we need to rewrite the whole journal |
2510 | | * file (for example, to upversion it). |
2511 | | */ |
2512 | 0 | if ((flags & DNS_JOURNAL_COMPACTALL) != 0) { |
2513 | 0 | if ((flags & DNS_JOURNAL_VERSION1) != 0) { |
2514 | 0 | downgrade = true; |
2515 | 0 | } |
2516 | 0 | rewrite = true; |
2517 | 0 | serial = dns_journal_first_serial(j1); |
2518 | 0 | } else if (JOURNAL_EMPTY(&j1->header)) { |
2519 | 0 | dns_journal_destroy(&j1); |
2520 | 0 | return (ISC_R_SUCCESS); |
2521 | 0 | } |
2522 | | |
2523 | 0 | if (DNS_SERIAL_GT(j1->header.begin.serial, serial) || |
2524 | 0 | DNS_SERIAL_GT(serial, j1->header.end.serial)) |
2525 | 0 | { |
2526 | 0 | dns_journal_destroy(&j1); |
2527 | 0 | return (ISC_R_RANGE); |
2528 | 0 | } |
2529 | | |
2530 | | /* |
2531 | | * Cope with very small target sizes. |
2532 | | */ |
2533 | 0 | indexend = sizeof(journal_rawheader_t) + |
2534 | 0 | j1->header.index_size * sizeof(journal_rawpos_t); |
2535 | 0 | if (target_size < DNS_JOURNAL_SIZE_MIN) { |
2536 | 0 | target_size = DNS_JOURNAL_SIZE_MIN; |
2537 | 0 | } |
2538 | 0 | if (target_size < indexend * 2) { |
2539 | 0 | target_size = target_size / 2 + indexend; |
2540 | 0 | } |
2541 | | |
2542 | | /* |
2543 | | * See if there is any work to do. |
2544 | | */ |
2545 | 0 | if (!rewrite && (uint32_t)j1->header.end.offset < target_size) { |
2546 | 0 | dns_journal_destroy(&j1); |
2547 | 0 | return (ISC_R_SUCCESS); |
2548 | 0 | } |
2549 | | |
2550 | 0 | CHECK(journal_open(mctx, newname, true, true, downgrade, &j2)); |
2551 | 0 | CHECK(journal_seek(j2, indexend)); |
2552 | | |
2553 | | /* |
2554 | | * Remove overhead so space test below can succeed. |
2555 | | */ |
2556 | 0 | if (target_size >= indexend) { |
2557 | 0 | target_size -= indexend; |
2558 | 0 | } |
2559 | | |
2560 | | /* |
2561 | | * Find if we can create enough free space. |
2562 | | */ |
2563 | 0 | best_guess = j1->header.begin; |
2564 | 0 | for (i = 0; i < j1->header.index_size; i++) { |
2565 | 0 | if (POS_VALID(j1->index[i]) && |
2566 | 0 | DNS_SERIAL_GE(serial, j1->index[i].serial) && |
2567 | 0 | ((uint32_t)(j1->header.end.offset - j1->index[i].offset) >= |
2568 | 0 | target_size / 2) && |
2569 | 0 | j1->index[i].offset > best_guess.offset) |
2570 | 0 | { |
2571 | 0 | best_guess = j1->index[i]; |
2572 | 0 | } |
2573 | 0 | } |
2574 | |
|
2575 | 0 | current_pos = best_guess; |
2576 | 0 | while (current_pos.serial != serial) { |
2577 | 0 | CHECK(journal_next(j1, ¤t_pos)); |
2578 | 0 | if (current_pos.serial == j1->header.end.serial) { |
2579 | 0 | break; |
2580 | 0 | } |
2581 | | |
2582 | 0 | if (DNS_SERIAL_GE(serial, current_pos.serial) && |
2583 | 0 | ((uint32_t)(j1->header.end.offset - current_pos.offset) >= |
2584 | 0 | (target_size / 2)) && |
2585 | 0 | current_pos.offset > best_guess.offset) |
2586 | 0 | { |
2587 | 0 | best_guess = current_pos; |
2588 | 0 | } else { |
2589 | 0 | break; |
2590 | 0 | } |
2591 | 0 | } |
2592 | | |
2593 | 0 | INSIST(best_guess.serial != j1->header.end.serial); |
2594 | 0 | if (best_guess.serial != serial) { |
2595 | 0 | CHECK(journal_next(j1, &best_guess)); |
2596 | 0 | serial = best_guess.serial; |
2597 | 0 | } |
2598 | | |
2599 | | /* |
2600 | | * We should now be roughly half target_size provided |
2601 | | * we did not reach 'serial'. If not we will just copy |
2602 | | * all uncommitted deltas regardless of the size. |
2603 | | */ |
2604 | 0 | len = j1->header.end.offset - best_guess.offset; |
2605 | 0 | if (len != 0) { |
2606 | 0 | CHECK(journal_seek(j1, best_guess.offset)); |
2607 | | |
2608 | | /* Prepare new header */ |
2609 | 0 | j2->header.begin.serial = best_guess.serial; |
2610 | 0 | j2->header.begin.offset = indexend; |
2611 | 0 | j2->header.sourceserial = j1->header.sourceserial; |
2612 | 0 | j2->header.serialset = j1->header.serialset; |
2613 | 0 | j2->header.end.serial = j1->header.end.serial; |
2614 | | |
2615 | | /* |
2616 | | * Only use this method if we're rewriting the |
2617 | | * journal to fix outdated transaction headers; |
2618 | | * otherwise we'll copy the whole journal without |
2619 | | * parsing individual deltas below. |
2620 | | */ |
2621 | 0 | while (rewrite && len > 0) { |
2622 | 0 | journal_xhdr_t xhdr; |
2623 | 0 | off_t offset = j1->offset; |
2624 | 0 | uint32_t count; |
2625 | |
|
2626 | 0 | result = journal_read_xhdr(j1, &xhdr); |
2627 | 0 | if (rewrite && result == ISC_R_NOMORE) { |
2628 | 0 | break; |
2629 | 0 | } |
2630 | 0 | CHECK(result); |
2631 | | |
2632 | 0 | size = xhdr.size; |
2633 | 0 | if (size > len) { |
2634 | 0 | isc_log_write(JOURNAL_COMMON_LOGARGS, |
2635 | 0 | ISC_LOG_ERROR, |
2636 | 0 | "%s: journal file corrupt, " |
2637 | 0 | "transaction too large", |
2638 | 0 | j1->filename); |
2639 | 0 | CHECK(ISC_R_FAILURE); |
2640 | 0 | } |
2641 | 0 | buf = isc_mem_get(mctx, size); |
2642 | 0 | result = journal_read(j1, buf, size); |
2643 | | |
2644 | | /* |
2645 | | * If we're repairing an outdated journal, the |
2646 | | * xhdr format may be wrong. |
2647 | | */ |
2648 | 0 | if (rewrite && (result != ISC_R_SUCCESS || |
2649 | 0 | !check_delta(buf, size))) |
2650 | 0 | { |
2651 | 0 | if (j1->xhdr_version == XHDR_VERSION2) { |
2652 | | /* XHDR_VERSION2 -> XHDR_VERSION1 */ |
2653 | 0 | j1->xhdr_version = XHDR_VERSION1; |
2654 | 0 | CHECK(journal_seek(j1, offset)); |
2655 | 0 | CHECK(journal_read_xhdr(j1, &xhdr)); |
2656 | 0 | } else if (j1->xhdr_version == XHDR_VERSION1) { |
2657 | | /* XHDR_VERSION1 -> XHDR_VERSION2 */ |
2658 | 0 | j1->xhdr_version = XHDR_VERSION2; |
2659 | 0 | CHECK(journal_seek(j1, offset)); |
2660 | 0 | CHECK(journal_read_xhdr(j1, &xhdr)); |
2661 | 0 | } |
2662 | | |
2663 | | /* Check again */ |
2664 | 0 | isc_mem_put(mctx, buf, size); |
2665 | 0 | size = xhdr.size; |
2666 | 0 | if (size > len) { |
2667 | 0 | isc_log_write( |
2668 | 0 | JOURNAL_COMMON_LOGARGS, |
2669 | 0 | ISC_LOG_ERROR, |
2670 | 0 | "%s: journal file corrupt, " |
2671 | 0 | "transaction too large", |
2672 | 0 | j1->filename); |
2673 | 0 | CHECK(ISC_R_FAILURE); |
2674 | 0 | } |
2675 | 0 | buf = isc_mem_get(mctx, size); |
2676 | 0 | CHECK(journal_read(j1, buf, size)); |
2677 | | |
2678 | 0 | if (!check_delta(buf, size)) { |
2679 | 0 | CHECK(ISC_R_UNEXPECTED); |
2680 | 0 | } |
2681 | 0 | } else { |
2682 | 0 | CHECK(result); |
2683 | 0 | } |
2684 | | |
2685 | | /* |
2686 | | * Recover from incorrectly written transaction header. |
2687 | | * The incorrect header was written as size, serial0, |
2688 | | * serial1, and 0. XHDR_VERSION2 is expecting size, |
2689 | | * count, serial0, and serial1. |
2690 | | */ |
2691 | 0 | if (j1->xhdr_version == XHDR_VERSION2 && |
2692 | 0 | xhdr.count == serial && xhdr.serial1 == 0U && |
2693 | 0 | isc_serial_gt(xhdr.serial0, xhdr.count)) |
2694 | 0 | { |
2695 | 0 | xhdr.serial1 = xhdr.serial0; |
2696 | 0 | xhdr.serial0 = xhdr.count; |
2697 | 0 | xhdr.count = 0; |
2698 | 0 | } |
2699 | | |
2700 | | /* |
2701 | | * Check that xhdr is consistent. |
2702 | | */ |
2703 | 0 | if (xhdr.serial0 != serial || |
2704 | 0 | isc_serial_le(xhdr.serial1, xhdr.serial0)) |
2705 | 0 | { |
2706 | 0 | CHECK(ISC_R_UNEXPECTED); |
2707 | 0 | } |
2708 | | |
2709 | | /* |
2710 | | * Extract record count from the transaction. This |
2711 | | * is needed when converting from XHDR_VERSION1 to |
2712 | | * XHDR_VERSION2, and when recovering from an |
2713 | | * incorrectly written XHDR_VERSION2. |
2714 | | */ |
2715 | 0 | count = rrcount(buf, size); |
2716 | 0 | CHECK(journal_write_xhdr(j2, xhdr.size, count, |
2717 | 0 | xhdr.serial0, xhdr.serial1)); |
2718 | 0 | CHECK(journal_write(j2, buf, size)); |
2719 | | |
2720 | 0 | j2->header.end.offset = j2->offset; |
2721 | |
|
2722 | 0 | serial = xhdr.serial1; |
2723 | |
|
2724 | 0 | len = j1->header.end.offset - j1->offset; |
2725 | 0 | isc_mem_put(mctx, buf, size); |
2726 | 0 | } |
2727 | | |
2728 | | /* |
2729 | | * If we're not rewriting transaction headers, we can use |
2730 | | * this faster method instead. |
2731 | | */ |
2732 | 0 | if (!rewrite) { |
2733 | 0 | size = ISC_MIN(64 * 1024, len); |
2734 | 0 | buf = isc_mem_get(mctx, size); |
2735 | 0 | for (i = 0; i < len; i += size) { |
2736 | 0 | unsigned int blob = ISC_MIN(size, len - i); |
2737 | 0 | CHECK(journal_read(j1, buf, blob)); |
2738 | 0 | CHECK(journal_write(j2, buf, blob)); |
2739 | 0 | } |
2740 | | |
2741 | 0 | j2->header.end.offset = indexend + len; |
2742 | 0 | } |
2743 | | |
2744 | 0 | CHECK(journal_fsync(j2)); |
2745 | | |
2746 | | /* |
2747 | | * Update the journal header. |
2748 | | */ |
2749 | 0 | journal_header_encode(&j2->header, &rawheader); |
2750 | 0 | CHECK(journal_seek(j2, 0)); |
2751 | 0 | CHECK(journal_write(j2, &rawheader, sizeof(rawheader))); |
2752 | 0 | CHECK(journal_fsync(j2)); |
2753 | | |
2754 | | /* |
2755 | | * Build new index. |
2756 | | */ |
2757 | 0 | current_pos = j2->header.begin; |
2758 | 0 | while (current_pos.serial != j2->header.end.serial) { |
2759 | 0 | index_add(j2, ¤t_pos); |
2760 | 0 | CHECK(journal_next(j2, ¤t_pos)); |
2761 | 0 | } |
2762 | | |
2763 | | /* |
2764 | | * Write index. |
2765 | | */ |
2766 | 0 | CHECK(index_to_disk(j2)); |
2767 | 0 | CHECK(journal_fsync(j2)); |
2768 | | |
2769 | 0 | indexend = j2->header.end.offset; |
2770 | 0 | POST(indexend); |
2771 | 0 | } |
2772 | | |
2773 | | /* |
2774 | | * Close both journals before trying to rename files. |
2775 | | */ |
2776 | 0 | dns_journal_destroy(&j1); |
2777 | 0 | dns_journal_destroy(&j2); |
2778 | | |
2779 | | /* |
2780 | | * With a UFS file system this should just succeed and be atomic. |
2781 | | * Any IXFR outs will just continue and the old journal will be |
2782 | | * removed on final close. |
2783 | | * |
2784 | | * With MSDOS / NTFS we need to do a two stage rename, triggered |
2785 | | * by EEXIST. (If any IXFR's are running in other threads, however, |
2786 | | * this will fail, and the journal will not be compacted. But |
2787 | | * if so, hopefully they'll be finished by the next time we |
2788 | | * compact.) |
2789 | | */ |
2790 | 0 | if (rename(newname, filename) == -1) { |
2791 | 0 | if (errno == EEXIST && !is_backup) { |
2792 | 0 | result = isc_file_remove(backup); |
2793 | 0 | if (result != ISC_R_SUCCESS && |
2794 | 0 | result != ISC_R_FILENOTFOUND) |
2795 | 0 | { |
2796 | 0 | goto failure; |
2797 | 0 | } |
2798 | 0 | if (rename(filename, backup) == -1) { |
2799 | 0 | goto maperrno; |
2800 | 0 | } |
2801 | 0 | if (rename(newname, filename) == -1) { |
2802 | 0 | goto maperrno; |
2803 | 0 | } |
2804 | 0 | (void)isc_file_remove(backup); |
2805 | 0 | } else { |
2806 | 0 | maperrno: |
2807 | 0 | result = ISC_R_FAILURE; |
2808 | 0 | goto failure; |
2809 | 0 | } |
2810 | 0 | } |
2811 | | |
2812 | 0 | result = ISC_R_SUCCESS; |
2813 | |
|
2814 | 0 | failure: |
2815 | 0 | (void)isc_file_remove(newname); |
2816 | 0 | if (buf != NULL) { |
2817 | 0 | isc_mem_put(mctx, buf, size); |
2818 | 0 | } |
2819 | 0 | if (j1 != NULL) { |
2820 | 0 | dns_journal_destroy(&j1); |
2821 | 0 | } |
2822 | 0 | if (j2 != NULL) { |
2823 | 0 | dns_journal_destroy(&j2); |
2824 | 0 | } |
2825 | 0 | return (result); |
2826 | 0 | } |
2827 | | |
2828 | | static isc_result_t |
2829 | 0 | index_to_disk(dns_journal_t *j) { |
2830 | 0 | isc_result_t result = ISC_R_SUCCESS; |
2831 | |
|
2832 | 0 | if (j->header.index_size != 0) { |
2833 | 0 | unsigned int i; |
2834 | 0 | unsigned char *p; |
2835 | 0 | unsigned int rawbytes; |
2836 | |
|
2837 | 0 | rawbytes = j->header.index_size * sizeof(journal_rawpos_t); |
2838 | |
|
2839 | 0 | p = j->rawindex; |
2840 | 0 | for (i = 0; i < j->header.index_size; i++) { |
2841 | 0 | encode_uint32(j->index[i].serial, p); |
2842 | 0 | p += 4; |
2843 | 0 | encode_uint32(j->index[i].offset, p); |
2844 | 0 | p += 4; |
2845 | 0 | } |
2846 | 0 | INSIST(p == j->rawindex + rawbytes); |
2847 | |
|
2848 | 0 | CHECK(journal_seek(j, sizeof(journal_rawheader_t))); |
2849 | 0 | CHECK(journal_write(j, j->rawindex, rawbytes)); |
2850 | 0 | } |
2851 | 0 | failure: |
2852 | 0 | return (result); |
2853 | 0 | } |