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