1135446Strhodes/* 2262706Serwin * Copyright (C) 2004, 2005, 2007-2011, 2013, 2014 Internet Systems Consortium, Inc. ("ISC") 3135446Strhodes * Copyright (C) 1999-2002 Internet Software Consortium. 4135446Strhodes * 5174187Sdougb * Permission to use, copy, modify, and/or distribute this software for any 6135446Strhodes * purpose with or without fee is hereby granted, provided that the above 7135446Strhodes * copyright notice and this permission notice appear in all copies. 8135446Strhodes * 9135446Strhodes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10135446Strhodes * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11135446Strhodes * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12135446Strhodes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13135446Strhodes * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14135446Strhodes * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15135446Strhodes * PERFORMANCE OF THIS SOFTWARE. 16135446Strhodes */ 17135446Strhodes 18254897Serwin/* $Id: journal.c,v 1.120 2011/12/22 07:32:41 each Exp $ */ 19135446Strhodes 20135446Strhodes#include <config.h> 21135446Strhodes 22135446Strhodes#include <stdlib.h> 23153816Sdougb#include <unistd.h> 24174187Sdougb#include <errno.h> 25135446Strhodes 26135446Strhodes#include <isc/file.h> 27135446Strhodes#include <isc/mem.h> 28135446Strhodes#include <isc/stdio.h> 29135446Strhodes#include <isc/string.h> 30135446Strhodes#include <isc/util.h> 31135446Strhodes 32135446Strhodes#include <dns/compress.h> 33135446Strhodes#include <dns/db.h> 34135446Strhodes#include <dns/dbiterator.h> 35135446Strhodes#include <dns/diff.h> 36135446Strhodes#include <dns/fixedname.h> 37135446Strhodes#include <dns/journal.h> 38135446Strhodes#include <dns/log.h> 39135446Strhodes#include <dns/rdataset.h> 40135446Strhodes#include <dns/rdatasetiter.h> 41135446Strhodes#include <dns/result.h> 42135446Strhodes#include <dns/soa.h> 43135446Strhodes 44186462Sdougb/*! \file 45193149Sdougb * \brief Journaling. 46170222Sdougb * 47170222Sdougb * A journal file consists of 48170222Sdougb * 49170222Sdougb * \li A fixed-size header of type journal_rawheader_t. 50170222Sdougb * 51170222Sdougb * \li The index. This is an unordered array of index entries 52170222Sdougb * of type journal_rawpos_t giving the locations 53170222Sdougb * of some arbitrary subset of the journal's addressable 54170222Sdougb * transactions. The index entries are used as hints to 55170222Sdougb * speed up the process of locating a transaction with a given 56170222Sdougb * serial number. Unused index entries have an "offset" 57170222Sdougb * field of zero. The size of the index can vary between 58170222Sdougb * journal files, but does not change during the lifetime 59170222Sdougb * of a file. The size can be zero. 60170222Sdougb * 61170222Sdougb * \li The journal data. This consists of one or more transactions. 62170222Sdougb * Each transaction begins with a transaction header of type 63170222Sdougb * journal_rawxhdr_t. The transaction header is followed by a 64170222Sdougb * sequence of RRs, similar in structure to an IXFR difference 65170222Sdougb * sequence (RFC1995). That is, the pre-transaction SOA, 66170222Sdougb * zero or more other deleted RRs, the post-transaction SOA, 67170222Sdougb * and zero or more other added RRs. Unlike in IXFR, each RR 68170222Sdougb * is prefixed with a 32-bit length. 69170222Sdougb * 70170222Sdougb * The journal data part grows as new transactions are 71170222Sdougb * appended to the file. Only those transactions 72170222Sdougb * whose serial number is current-(2^31-1) to current 73170222Sdougb * are considered "addressable" and may be pointed 74170222Sdougb * to from the header or index. They may be preceded 75170222Sdougb * by old transactions that are no longer addressable, 76170222Sdougb * and they may be followed by transactions that were 77170222Sdougb * appended to the journal but never committed by updating 78170222Sdougb * the "end" position in the header. The latter will 79170222Sdougb * be overwritten when new transactions are added. 80170222Sdougb */ 81170222Sdougb/*% 82135446Strhodes * When true, accept IXFR difference sequences where the 83135446Strhodes * SOA serial number does not change (BIND 8 sends such 84135446Strhodes * sequences). 85135446Strhodes */ 86135446Strhodesstatic isc_boolean_t bind8_compat = ISC_TRUE; /* XXX config */ 87135446Strhodes 88135446Strhodes/**************************************************************************/ 89135446Strhodes/* 90135446Strhodes * Miscellaneous utilities. 91135446Strhodes */ 92135446Strhodes 93135446Strhodes#define JOURNAL_COMMON_LOGARGS \ 94135446Strhodes dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_JOURNAL 95135446Strhodes 96135446Strhodes#define JOURNAL_DEBUG_LOGARGS(n) \ 97135446Strhodes JOURNAL_COMMON_LOGARGS, ISC_LOG_DEBUG(n) 98135446Strhodes 99170222Sdougb/*% 100135446Strhodes * It would be non-sensical (or at least obtuse) to use FAIL() with an 101135446Strhodes * ISC_R_SUCCESS code, but the test is there to keep the Solaris compiler 102135446Strhodes * from complaining about "end-of-loop code not reached". 103135446Strhodes */ 104135446Strhodes#define FAIL(code) \ 105135446Strhodes do { result = (code); \ 106135446Strhodes if (result != ISC_R_SUCCESS) goto failure; \ 107135446Strhodes } while (0) 108135446Strhodes 109135446Strhodes#define CHECK(op) \ 110186462Sdougb do { result = (op); \ 111135446Strhodes if (result != ISC_R_SUCCESS) goto failure; \ 112135446Strhodes } while (0) 113135446Strhodes 114254897Serwin#define JOURNAL_SERIALSET 0x01U 115254897Serwin 116135446Strhodesstatic isc_result_t index_to_disk(dns_journal_t *); 117135446Strhodes 118135446Strhodesstatic inline isc_uint32_t 119135446Strhodesdecode_uint32(unsigned char *p) { 120135446Strhodes return ((p[0] << 24) + 121135446Strhodes (p[1] << 16) + 122135446Strhodes (p[2] << 8) + 123135446Strhodes (p[3] << 0)); 124135446Strhodes} 125135446Strhodes 126135446Strhodesstatic inline void 127135446Strhodesencode_uint32(isc_uint32_t val, unsigned char *p) { 128135446Strhodes p[0] = (isc_uint8_t)(val >> 24); 129135446Strhodes p[1] = (isc_uint8_t)(val >> 16); 130135446Strhodes p[2] = (isc_uint8_t)(val >> 8); 131135446Strhodes p[3] = (isc_uint8_t)(val >> 0); 132135446Strhodes} 133135446Strhodes 134135446Strhodesisc_result_t 135135446Strhodesdns_db_createsoatuple(dns_db_t *db, dns_dbversion_t *ver, isc_mem_t *mctx, 136135446Strhodes dns_diffop_t op, dns_difftuple_t **tp) 137135446Strhodes{ 138135446Strhodes isc_result_t result; 139135446Strhodes dns_dbnode_t *node; 140135446Strhodes dns_rdataset_t rdataset; 141135446Strhodes dns_rdata_t rdata = DNS_RDATA_INIT; 142135446Strhodes dns_name_t *zonename; 143135446Strhodes 144135446Strhodes zonename = dns_db_origin(db); 145135446Strhodes 146135446Strhodes node = NULL; 147135446Strhodes result = dns_db_findnode(db, zonename, ISC_FALSE, &node); 148135446Strhodes if (result != ISC_R_SUCCESS) 149135446Strhodes goto nonode; 150135446Strhodes 151135446Strhodes dns_rdataset_init(&rdataset); 152135446Strhodes result = dns_db_findrdataset(db, node, ver, dns_rdatatype_soa, 0, 153135446Strhodes (isc_stdtime_t)0, &rdataset, NULL); 154186462Sdougb if (result != ISC_R_SUCCESS) 155135446Strhodes goto freenode; 156135446Strhodes 157135446Strhodes result = dns_rdataset_first(&rdataset); 158186462Sdougb if (result != ISC_R_SUCCESS) 159135446Strhodes goto freenode; 160135446Strhodes 161135446Strhodes dns_rdataset_current(&rdataset, &rdata); 162135446Strhodes 163135446Strhodes result = dns_difftuple_create(mctx, op, zonename, rdataset.ttl, 164135446Strhodes &rdata, tp); 165135446Strhodes 166135446Strhodes dns_rdataset_disassociate(&rdataset); 167135446Strhodes dns_db_detachnode(db, &node); 168225361Sdougb return (result); 169135446Strhodes 170135446Strhodes freenode: 171135446Strhodes dns_db_detachnode(db, &node); 172135446Strhodes nonode: 173135446Strhodes UNEXPECTED_ERROR(__FILE__, __LINE__, "missing SOA"); 174135446Strhodes return (result); 175135446Strhodes} 176135446Strhodes 177193149Sdougb/* Journaling */ 178135446Strhodes 179170222Sdougb/*% 180135446Strhodes * On-disk representation of a "pointer" to a journal entry. 181135446Strhodes * These are used in the journal header to locate the beginning 182135446Strhodes * and end of the journal, and in the journal index to locate 183135446Strhodes * other transactions. 184135446Strhodes */ 185135446Strhodestypedef struct { 186170222Sdougb unsigned char serial[4]; /*%< SOA serial before update. */ 187135446Strhodes /* 188135446Strhodes * XXXRTH Should offset be 8 bytes? 189135446Strhodes * XXXDCL ... probably, since isc_offset_t is 8 bytes on many OSs. 190135446Strhodes * XXXAG ... but we will not be able to seek >2G anyway on many 191135446Strhodes * platforms as long as we are using fseek() rather 192135446Strhodes * than lseek(). 193135446Strhodes */ 194170222Sdougb unsigned char offset[4]; /*%< Offset from beginning of file. */ 195135446Strhodes} journal_rawpos_t; 196135446Strhodes 197135446Strhodes 198170222Sdougb/*% 199135446Strhodes * The header is of a fixed size, with some spare room for future 200135446Strhodes * extensions. 201135446Strhodes */ 202135446Strhodes#define JOURNAL_HEADER_SIZE 64 /* Bytes. */ 203135446Strhodes 204170222Sdougb/*% 205170222Sdougb * The on-disk representation of the journal header. 206170222Sdougb * All numbers are stored in big-endian order. 207170222Sdougb */ 208135446Strhodestypedef union { 209135446Strhodes struct { 210170222Sdougb /*% File format version ID. */ 211135446Strhodes unsigned char format[16]; 212170222Sdougb /*% Position of the first addressable transaction */ 213135446Strhodes journal_rawpos_t begin; 214170222Sdougb /*% Position of the next (yet nonexistent) transaction. */ 215135446Strhodes journal_rawpos_t end; 216170222Sdougb /*% Number of index entries following the header. */ 217135446Strhodes unsigned char index_size[4]; 218254897Serwin /*% Source serial number. */ 219254897Serwin unsigned char sourceserial[4]; 220254897Serwin unsigned char flags; 221135446Strhodes } h; 222135446Strhodes /* Pad the header to a fixed size. */ 223135446Strhodes unsigned char pad[JOURNAL_HEADER_SIZE]; 224135446Strhodes} journal_rawheader_t; 225135446Strhodes 226170222Sdougb/*% 227135446Strhodes * The on-disk representation of the transaction header. 228135446Strhodes * There is one of these at the beginning of each transaction. 229135446Strhodes */ 230135446Strhodestypedef struct { 231170222Sdougb unsigned char size[4]; /*%< In bytes, excluding header. */ 232170222Sdougb unsigned char serial0[4]; /*%< SOA serial before update. */ 233170222Sdougb unsigned char serial1[4]; /*%< SOA serial after update. */ 234135446Strhodes} journal_rawxhdr_t; 235135446Strhodes 236170222Sdougb/*% 237135446Strhodes * The on-disk representation of the RR header. 238135446Strhodes * There is one of these at the beginning of each RR. 239135446Strhodes */ 240135446Strhodestypedef struct { 241170222Sdougb unsigned char size[4]; /*%< In bytes, excluding header. */ 242135446Strhodes} journal_rawrrhdr_t; 243135446Strhodes 244170222Sdougb/*% 245135446Strhodes * The in-core representation of the journal header. 246135446Strhodes */ 247135446Strhodestypedef struct { 248135446Strhodes isc_uint32_t serial; 249135446Strhodes isc_offset_t offset; 250135446Strhodes} journal_pos_t; 251135446Strhodes 252135446Strhodes#define POS_VALID(pos) ((pos).offset != 0) 253135446Strhodes#define POS_INVALIDATE(pos) ((pos).offset = 0, (pos).serial = 0) 254135446Strhodes 255135446Strhodestypedef struct { 256135446Strhodes unsigned char format[16]; 257135446Strhodes journal_pos_t begin; 258135446Strhodes journal_pos_t end; 259135446Strhodes isc_uint32_t index_size; 260254897Serwin isc_uint32_t sourceserial; 261254897Serwin isc_boolean_t serialset; 262135446Strhodes} journal_header_t; 263135446Strhodes 264170222Sdougb/*% 265135446Strhodes * The in-core representation of the transaction header. 266135446Strhodes */ 267135446Strhodes 268135446Strhodestypedef struct { 269135446Strhodes isc_uint32_t size; 270135446Strhodes isc_uint32_t serial0; 271135446Strhodes isc_uint32_t serial1; 272135446Strhodes} journal_xhdr_t; 273135446Strhodes 274170222Sdougb/*% 275135446Strhodes * The in-core representation of the RR header. 276135446Strhodes */ 277135446Strhodestypedef struct { 278135446Strhodes isc_uint32_t size; 279135446Strhodes} journal_rrhdr_t; 280135446Strhodes 281135446Strhodes 282170222Sdougb/*% 283135446Strhodes * Initial contents to store in the header of a newly created 284135446Strhodes * journal file. 285135446Strhodes * 286135446Strhodes * The header starts with the magic string ";BIND LOG V9\n" 287135446Strhodes * to identify the file as a BIND 9 journal file. An ASCII 288135446Strhodes * identification string is used rather than a binary magic 289135446Strhodes * number to be consistent with BIND 8 (BIND 8 journal files 290135446Strhodes * are ASCII text files). 291135446Strhodes */ 292135446Strhodes 293135446Strhodesstatic journal_header_t 294254897Serwininitial_journal_header = { ";BIND LOG V9\n", { 0, 0 }, { 0, 0 }, 0, 0, 0 }; 295135446Strhodes 296135446Strhodes#define JOURNAL_EMPTY(h) ((h)->begin.offset == (h)->end.offset) 297135446Strhodes 298135446Strhodestypedef enum { 299135446Strhodes JOURNAL_STATE_INVALID, 300135446Strhodes JOURNAL_STATE_READ, 301135446Strhodes JOURNAL_STATE_WRITE, 302254897Serwin JOURNAL_STATE_TRANSACTION, 303254897Serwin JOURNAL_STATE_INLINE 304135446Strhodes} journal_state_t; 305135446Strhodes 306135446Strhodesstruct dns_journal { 307170222Sdougb unsigned int magic; /*%< JOUR */ 308170222Sdougb isc_mem_t *mctx; /*%< Memory context */ 309135446Strhodes journal_state_t state; 310262706Serwin char *filename; /*%< Journal file name */ 311170222Sdougb FILE * fp; /*%< File handle */ 312170222Sdougb isc_offset_t offset; /*%< Current file offset */ 313170222Sdougb journal_header_t header; /*%< In-core journal header */ 314170222Sdougb unsigned char *rawindex; /*%< In-core buffer for journal index in on-disk format */ 315170222Sdougb journal_pos_t *index; /*%< In-core journal index */ 316135446Strhodes 317170222Sdougb /*% Current transaction state (when writing). */ 318135446Strhodes struct { 319170222Sdougb unsigned int n_soa; /*%< Number of SOAs seen */ 320170222Sdougb journal_pos_t pos[2]; /*%< Begin/end position */ 321135446Strhodes } x; 322135446Strhodes 323170222Sdougb /*% Iteration state (when reading). */ 324135446Strhodes struct { 325135446Strhodes /* These define the part of the journal we iterate over. */ 326170222Sdougb journal_pos_t bpos; /*%< Position before first, */ 327170222Sdougb journal_pos_t epos; /*%< and after last transaction */ 328135446Strhodes /* The rest is iterator state. */ 329170222Sdougb isc_uint32_t current_serial; /*%< Current SOA serial */ 330170222Sdougb isc_buffer_t source; /*%< Data from disk */ 331170222Sdougb isc_buffer_t target; /*%< Data from _fromwire check */ 332170222Sdougb dns_decompress_t dctx; /*%< Dummy decompression ctx */ 333170222Sdougb dns_name_t name; /*%< Current domain name */ 334170222Sdougb dns_rdata_t rdata; /*%< Current rdata */ 335170222Sdougb isc_uint32_t ttl; /*%< Current TTL */ 336170222Sdougb unsigned int xsize; /*%< Size of transaction data */ 337170222Sdougb unsigned int xpos; /*%< Current position in it */ 338170222Sdougb isc_result_t result; /*%< Result of last call */ 339135446Strhodes } it; 340135446Strhodes}; 341135446Strhodes 342135446Strhodes#define DNS_JOURNAL_MAGIC ISC_MAGIC('J', 'O', 'U', 'R') 343135446Strhodes#define DNS_JOURNAL_VALID(t) ISC_MAGIC_VALID(t, DNS_JOURNAL_MAGIC) 344135446Strhodes 345135446Strhodesstatic void 346135446Strhodesjournal_pos_decode(journal_rawpos_t *raw, journal_pos_t *cooked) { 347135446Strhodes cooked->serial = decode_uint32(raw->serial); 348135446Strhodes cooked->offset = decode_uint32(raw->offset); 349135446Strhodes} 350135446Strhodes 351135446Strhodesstatic void 352135446Strhodesjournal_pos_encode(journal_rawpos_t *raw, journal_pos_t *cooked) { 353135446Strhodes encode_uint32(cooked->serial, raw->serial); 354135446Strhodes encode_uint32(cooked->offset, raw->offset); 355135446Strhodes} 356135446Strhodes 357135446Strhodesstatic void 358135446Strhodesjournal_header_decode(journal_rawheader_t *raw, journal_header_t *cooked) { 359135446Strhodes INSIST(sizeof(cooked->format) == sizeof(raw->h.format)); 360262706Serwin memmove(cooked->format, raw->h.format, sizeof(cooked->format)); 361135446Strhodes journal_pos_decode(&raw->h.begin, &cooked->begin); 362135446Strhodes journal_pos_decode(&raw->h.end, &cooked->end); 363135446Strhodes cooked->index_size = decode_uint32(raw->h.index_size); 364254897Serwin cooked->sourceserial = decode_uint32(raw->h.sourceserial); 365254897Serwin cooked->serialset = ISC_TF(raw->h.flags & JOURNAL_SERIALSET); 366135446Strhodes} 367135446Strhodes 368135446Strhodesstatic void 369135446Strhodesjournal_header_encode(journal_header_t *cooked, journal_rawheader_t *raw) { 370254897Serwin unsigned char flags = 0; 371254897Serwin 372135446Strhodes INSIST(sizeof(cooked->format) == sizeof(raw->h.format)); 373135446Strhodes memset(raw->pad, 0, sizeof(raw->pad)); 374262706Serwin memmove(raw->h.format, cooked->format, sizeof(raw->h.format)); 375135446Strhodes journal_pos_encode(&raw->h.begin, &cooked->begin); 376135446Strhodes journal_pos_encode(&raw->h.end, &cooked->end); 377135446Strhodes encode_uint32(cooked->index_size, raw->h.index_size); 378254897Serwin encode_uint32(cooked->sourceserial, raw->h.sourceserial); 379254897Serwin if (cooked->serialset) 380254897Serwin flags |= JOURNAL_SERIALSET; 381254897Serwin raw->h.flags = flags; 382135446Strhodes} 383135446Strhodes 384135446Strhodes/* 385135446Strhodes * Journal file I/O subroutines, with error checking and reporting. 386135446Strhodes */ 387135446Strhodesstatic isc_result_t 388135446Strhodesjournal_seek(dns_journal_t *j, isc_uint32_t offset) { 389135446Strhodes isc_result_t result; 390135446Strhodes result = isc_stdio_seek(j->fp, (long)offset, SEEK_SET); 391135446Strhodes if (result != ISC_R_SUCCESS) { 392135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 393135446Strhodes "%s: seek: %s", j->filename, 394135446Strhodes isc_result_totext(result)); 395135446Strhodes return (ISC_R_UNEXPECTED); 396135446Strhodes } 397135446Strhodes j->offset = offset; 398135446Strhodes return (ISC_R_SUCCESS); 399135446Strhodes} 400135446Strhodes 401135446Strhodesstatic isc_result_t 402135446Strhodesjournal_read(dns_journal_t *j, void *mem, size_t nbytes) { 403135446Strhodes isc_result_t result; 404135446Strhodes 405135446Strhodes result = isc_stdio_read(mem, 1, nbytes, j->fp, NULL); 406135446Strhodes if (result != ISC_R_SUCCESS) { 407135446Strhodes if (result == ISC_R_EOF) 408135446Strhodes return (ISC_R_NOMORE); 409135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 410135446Strhodes "%s: read: %s", 411135446Strhodes j->filename, isc_result_totext(result)); 412135446Strhodes return (ISC_R_UNEXPECTED); 413135446Strhodes } 414262706Serwin j->offset += (isc_offset_t)nbytes; 415135446Strhodes return (ISC_R_SUCCESS); 416135446Strhodes} 417135446Strhodes 418135446Strhodesstatic isc_result_t 419135446Strhodesjournal_write(dns_journal_t *j, void *mem, size_t nbytes) { 420135446Strhodes isc_result_t result; 421135446Strhodes 422135446Strhodes result = isc_stdio_write(mem, 1, nbytes, j->fp, NULL); 423135446Strhodes if (result != ISC_R_SUCCESS) { 424135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 425135446Strhodes "%s: write: %s", 426135446Strhodes j->filename, isc_result_totext(result)); 427135446Strhodes return (ISC_R_UNEXPECTED); 428135446Strhodes } 429262706Serwin j->offset += (isc_offset_t)nbytes; 430135446Strhodes return (ISC_R_SUCCESS); 431135446Strhodes} 432135446Strhodes 433135446Strhodesstatic isc_result_t 434135446Strhodesjournal_fsync(dns_journal_t *j) { 435135446Strhodes isc_result_t result; 436135446Strhodes result = isc_stdio_flush(j->fp); 437135446Strhodes if (result != ISC_R_SUCCESS) { 438135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 439135446Strhodes "%s: flush: %s", 440135446Strhodes j->filename, isc_result_totext(result)); 441135446Strhodes return (ISC_R_UNEXPECTED); 442135446Strhodes } 443135446Strhodes result = isc_stdio_sync(j->fp); 444135446Strhodes if (result != ISC_R_SUCCESS) { 445135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 446135446Strhodes "%s: fsync: %s", 447135446Strhodes j->filename, isc_result_totext(result)); 448135446Strhodes return (ISC_R_UNEXPECTED); 449135446Strhodes } 450135446Strhodes return (ISC_R_SUCCESS); 451135446Strhodes} 452135446Strhodes 453135446Strhodes/* 454135446Strhodes * Read/write a transaction header at the current file position. 455135446Strhodes */ 456135446Strhodes 457135446Strhodesstatic isc_result_t 458135446Strhodesjournal_read_xhdr(dns_journal_t *j, journal_xhdr_t *xhdr) { 459135446Strhodes journal_rawxhdr_t raw; 460135446Strhodes isc_result_t result; 461135446Strhodes result = journal_read(j, &raw, sizeof(raw)); 462135446Strhodes if (result != ISC_R_SUCCESS) 463135446Strhodes return (result); 464135446Strhodes xhdr->size = decode_uint32(raw.size); 465135446Strhodes xhdr->serial0 = decode_uint32(raw.serial0); 466135446Strhodes xhdr->serial1 = decode_uint32(raw.serial1); 467135446Strhodes return (ISC_R_SUCCESS); 468135446Strhodes} 469135446Strhodes 470135446Strhodesstatic isc_result_t 471135446Strhodesjournal_write_xhdr(dns_journal_t *j, isc_uint32_t size, 472135446Strhodes isc_uint32_t serial0, isc_uint32_t serial1) 473135446Strhodes{ 474135446Strhodes journal_rawxhdr_t raw; 475135446Strhodes encode_uint32(size, raw.size); 476135446Strhodes encode_uint32(serial0, raw.serial0); 477135446Strhodes encode_uint32(serial1, raw.serial1); 478135446Strhodes return (journal_write(j, &raw, sizeof(raw))); 479135446Strhodes} 480135446Strhodes 481135446Strhodes 482135446Strhodes/* 483135446Strhodes * Read an RR header at the current file position. 484135446Strhodes */ 485135446Strhodes 486135446Strhodesstatic isc_result_t 487135446Strhodesjournal_read_rrhdr(dns_journal_t *j, journal_rrhdr_t *rrhdr) { 488135446Strhodes journal_rawrrhdr_t raw; 489135446Strhodes isc_result_t result; 490135446Strhodes result = journal_read(j, &raw, sizeof(raw)); 491135446Strhodes if (result != ISC_R_SUCCESS) 492135446Strhodes return (result); 493135446Strhodes rrhdr->size = decode_uint32(raw.size); 494135446Strhodes return (ISC_R_SUCCESS); 495135446Strhodes} 496135446Strhodes 497135446Strhodesstatic isc_result_t 498135446Strhodesjournal_file_create(isc_mem_t *mctx, const char *filename) { 499135446Strhodes FILE *fp = NULL; 500135446Strhodes isc_result_t result; 501135446Strhodes journal_header_t header; 502135446Strhodes journal_rawheader_t rawheader; 503135446Strhodes int index_size = 56; /* XXX configurable */ 504135446Strhodes int size; 505135446Strhodes void *mem; /* Memory for temporary index image. */ 506135446Strhodes 507135446Strhodes INSIST(sizeof(journal_rawheader_t) == JOURNAL_HEADER_SIZE); 508135446Strhodes 509135446Strhodes result = isc_stdio_open(filename, "wb", &fp); 510135446Strhodes if (result != ISC_R_SUCCESS) { 511135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 512135446Strhodes "%s: create: %s", 513135446Strhodes filename, isc_result_totext(result)); 514135446Strhodes return (ISC_R_UNEXPECTED); 515135446Strhodes } 516135446Strhodes 517135446Strhodes header = initial_journal_header; 518135446Strhodes header.index_size = index_size; 519135446Strhodes journal_header_encode(&header, &rawheader); 520135446Strhodes 521135446Strhodes size = sizeof(journal_rawheader_t) + 522135446Strhodes index_size * sizeof(journal_rawpos_t); 523135446Strhodes 524135446Strhodes mem = isc_mem_get(mctx, size); 525135446Strhodes if (mem == NULL) { 526135446Strhodes (void)isc_stdio_close(fp); 527135446Strhodes (void)isc_file_remove(filename); 528135446Strhodes return (ISC_R_NOMEMORY); 529135446Strhodes } 530135446Strhodes memset(mem, 0, size); 531262706Serwin memmove(mem, &rawheader, sizeof(rawheader)); 532135446Strhodes 533135446Strhodes result = isc_stdio_write(mem, 1, (size_t) size, fp, NULL); 534135446Strhodes if (result != ISC_R_SUCCESS) { 535135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 536135446Strhodes "%s: write: %s", 537135446Strhodes filename, isc_result_totext(result)); 538135446Strhodes (void)isc_stdio_close(fp); 539135446Strhodes (void)isc_file_remove(filename); 540135446Strhodes isc_mem_put(mctx, mem, size); 541135446Strhodes return (ISC_R_UNEXPECTED); 542135446Strhodes } 543135446Strhodes isc_mem_put(mctx, mem, size); 544135446Strhodes 545135446Strhodes result = isc_stdio_close(fp); 546135446Strhodes if (result != ISC_R_SUCCESS) { 547135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 548135446Strhodes "%s: close: %s", 549135446Strhodes filename, isc_result_totext(result)); 550135446Strhodes (void)isc_file_remove(filename); 551135446Strhodes return (ISC_R_UNEXPECTED); 552135446Strhodes } 553135446Strhodes 554135446Strhodes return (ISC_R_SUCCESS); 555135446Strhodes} 556135446Strhodes 557135446Strhodesstatic isc_result_t 558135446Strhodesjournal_open(isc_mem_t *mctx, const char *filename, isc_boolean_t write, 559254897Serwin isc_boolean_t create, dns_journal_t **journalp) 560254897Serwin{ 561135446Strhodes FILE *fp = NULL; 562135446Strhodes isc_result_t result; 563135446Strhodes journal_rawheader_t rawheader; 564135446Strhodes dns_journal_t *j; 565135446Strhodes 566135446Strhodes INSIST(journalp != NULL && *journalp == NULL); 567135446Strhodes j = isc_mem_get(mctx, sizeof(*j)); 568135446Strhodes if (j == NULL) 569135446Strhodes return (ISC_R_NOMEMORY); 570135446Strhodes 571254897Serwin j->mctx = NULL; 572254897Serwin isc_mem_attach(mctx, &j->mctx); 573135446Strhodes j->state = JOURNAL_STATE_INVALID; 574135446Strhodes j->fp = NULL; 575262706Serwin j->filename = isc_mem_strdup(mctx, filename); 576135446Strhodes j->index = NULL; 577135446Strhodes j->rawindex = NULL; 578135446Strhodes 579262706Serwin if (j->filename == NULL) 580262706Serwin FAIL(ISC_R_NOMEMORY); 581262706Serwin 582135446Strhodes result = isc_stdio_open(j->filename, write ? "rb+" : "rb", &fp); 583135446Strhodes 584135446Strhodes if (result == ISC_R_FILENOTFOUND) { 585135446Strhodes if (create) { 586224092Sdougb isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_DEBUG(1), 587135446Strhodes "journal file %s does not exist, " 588224092Sdougb "creating it", j->filename); 589135446Strhodes CHECK(journal_file_create(mctx, filename)); 590135446Strhodes /* 591135446Strhodes * Retry. 592135446Strhodes */ 593135446Strhodes result = isc_stdio_open(j->filename, "rb+", &fp); 594135446Strhodes } else { 595135446Strhodes FAIL(ISC_R_NOTFOUND); 596135446Strhodes } 597135446Strhodes } 598135446Strhodes if (result != ISC_R_SUCCESS) { 599135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 600135446Strhodes "%s: open: %s", 601135446Strhodes j->filename, isc_result_totext(result)); 602135446Strhodes FAIL(ISC_R_UNEXPECTED); 603135446Strhodes } 604135446Strhodes 605135446Strhodes j->fp = fp; 606135446Strhodes 607135446Strhodes /* 608135446Strhodes * Set magic early so that seek/read can succeed. 609135446Strhodes */ 610135446Strhodes j->magic = DNS_JOURNAL_MAGIC; 611135446Strhodes 612135446Strhodes CHECK(journal_seek(j, 0)); 613135446Strhodes CHECK(journal_read(j, &rawheader, sizeof(rawheader))); 614135446Strhodes 615135446Strhodes if (memcmp(rawheader.h.format, initial_journal_header.format, 616135446Strhodes sizeof(initial_journal_header.format)) != 0) { 617135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 618135446Strhodes "%s: journal format not recognized", 619135446Strhodes j->filename); 620135446Strhodes FAIL(ISC_R_UNEXPECTED); 621135446Strhodes } 622135446Strhodes journal_header_decode(&rawheader, &j->header); 623135446Strhodes 624135446Strhodes /* 625135446Strhodes * If there is an index, read the raw index into a dynamically 626135446Strhodes * allocated buffer and then convert it into a cooked index. 627135446Strhodes */ 628135446Strhodes if (j->header.index_size != 0) { 629135446Strhodes unsigned int i; 630135446Strhodes unsigned int rawbytes; 631135446Strhodes unsigned char *p; 632135446Strhodes 633135446Strhodes rawbytes = j->header.index_size * sizeof(journal_rawpos_t); 634135446Strhodes j->rawindex = isc_mem_get(mctx, rawbytes); 635135446Strhodes if (j->rawindex == NULL) 636135446Strhodes FAIL(ISC_R_NOMEMORY); 637135446Strhodes 638135446Strhodes CHECK(journal_read(j, j->rawindex, rawbytes)); 639135446Strhodes 640135446Strhodes j->index = isc_mem_get(mctx, j->header.index_size * 641135446Strhodes sizeof(journal_pos_t)); 642135446Strhodes if (j->index == NULL) 643135446Strhodes FAIL(ISC_R_NOMEMORY); 644135446Strhodes 645135446Strhodes p = j->rawindex; 646135446Strhodes for (i = 0; i < j->header.index_size; i++) { 647135446Strhodes j->index[i].serial = decode_uint32(p); 648135446Strhodes p += 4; 649135446Strhodes j->index[i].offset = decode_uint32(p); 650135446Strhodes p += 4; 651135446Strhodes } 652135446Strhodes INSIST(p == j->rawindex + rawbytes); 653135446Strhodes } 654135446Strhodes j->offset = -1; /* Invalid, must seek explicitly. */ 655135446Strhodes 656135446Strhodes /* 657135446Strhodes * Initialize the iterator. 658135446Strhodes */ 659135446Strhodes dns_name_init(&j->it.name, NULL); 660135446Strhodes dns_rdata_init(&j->it.rdata); 661135446Strhodes 662135446Strhodes /* 663193149Sdougb * Set up empty initial buffers for unchecked and checked 664135446Strhodes * wire format RR data. They will be reallocated 665135446Strhodes * later. 666135446Strhodes */ 667135446Strhodes isc_buffer_init(&j->it.source, NULL, 0); 668135446Strhodes isc_buffer_init(&j->it.target, NULL, 0); 669135446Strhodes dns_decompress_init(&j->it.dctx, -1, DNS_DECOMPRESS_NONE); 670135446Strhodes 671135446Strhodes j->state = 672135446Strhodes write ? JOURNAL_STATE_WRITE : JOURNAL_STATE_READ; 673135446Strhodes 674135446Strhodes *journalp = j; 675135446Strhodes return (ISC_R_SUCCESS); 676135446Strhodes 677135446Strhodes failure: 678135446Strhodes j->magic = 0; 679135446Strhodes if (j->index != NULL) { 680135446Strhodes isc_mem_put(j->mctx, j->index, j->header.index_size * 681135446Strhodes sizeof(journal_rawpos_t)); 682135446Strhodes j->index = NULL; 683135446Strhodes } 684262706Serwin if (j->filename != NULL) 685262706Serwin isc_mem_free(j->mctx, j->filename); 686135446Strhodes if (j->fp != NULL) 687135446Strhodes (void)isc_stdio_close(j->fp); 688254897Serwin isc_mem_putanddetach(&j->mctx, j, sizeof(*j)); 689135446Strhodes return (result); 690135446Strhodes} 691135446Strhodes 692135446Strhodesisc_result_t 693254897Serwindns_journal_open(isc_mem_t *mctx, const char *filename, unsigned int mode, 694254897Serwin dns_journal_t **journalp) 695254897Serwin{ 696174187Sdougb isc_result_t result; 697262706Serwin size_t namelen; 698174187Sdougb char backup[1024]; 699254897Serwin isc_boolean_t write, create; 700186462Sdougb 701254897Serwin create = ISC_TF(mode & DNS_JOURNAL_CREATE); 702254897Serwin write = ISC_TF(mode & (DNS_JOURNAL_WRITE|DNS_JOURNAL_CREATE)); 703254897Serwin 704254897Serwin result = journal_open(mctx, filename, write, create, journalp); 705174187Sdougb if (result == ISC_R_NOTFOUND) { 706174187Sdougb namelen = strlen(filename); 707262706Serwin if (namelen > 4U && strcmp(filename + namelen - 4, ".jnl") == 0) 708174187Sdougb namelen -= 4; 709174187Sdougb 710174187Sdougb result = isc_string_printf(backup, sizeof(backup), "%.*s.jbk", 711262706Serwin (int)namelen, filename); 712174187Sdougb if (result != ISC_R_SUCCESS) 713174187Sdougb return (result); 714174187Sdougb result = journal_open(mctx, backup, write, write, journalp); 715174187Sdougb } 716174187Sdougb return (result); 717135446Strhodes} 718135446Strhodes 719135446Strhodes/* 720135446Strhodes * A comparison function defining the sorting order for 721135446Strhodes * entries in the IXFR-style journal file. 722135446Strhodes * 723135446Strhodes * The IXFR format requires that deletions are sorted before 724135446Strhodes * additions, and within either one, SOA records are sorted 725135446Strhodes * before others. 726135446Strhodes * 727135446Strhodes * Also sort the non-SOA records by type as a courtesy to the 728135446Strhodes * server receiving the IXFR - it may help reduce the amount of 729135446Strhodes * rdataset merging it has to do. 730135446Strhodes */ 731135446Strhodesstatic int 732135446Strhodesixfr_order(const void *av, const void *bv) { 733135446Strhodes dns_difftuple_t const * const *ap = av; 734135446Strhodes dns_difftuple_t const * const *bp = bv; 735135446Strhodes dns_difftuple_t const *a = *ap; 736135446Strhodes dns_difftuple_t const *b = *bp; 737135446Strhodes int r; 738193149Sdougb int bop = 0, aop = 0; 739135446Strhodes 740193149Sdougb switch (a->op) { 741193149Sdougb case DNS_DIFFOP_DEL: 742193149Sdougb case DNS_DIFFOP_DELRESIGN: 743193149Sdougb aop = 1; 744193149Sdougb break; 745193149Sdougb case DNS_DIFFOP_ADD: 746193149Sdougb case DNS_DIFFOP_ADDRESIGN: 747193149Sdougb aop = 0; 748193149Sdougb break; 749193149Sdougb default: 750193149Sdougb INSIST(0); 751193149Sdougb } 752193149Sdougb 753193149Sdougb switch (b->op) { 754193149Sdougb case DNS_DIFFOP_DEL: 755193149Sdougb case DNS_DIFFOP_DELRESIGN: 756193149Sdougb bop = 1; 757193149Sdougb break; 758193149Sdougb case DNS_DIFFOP_ADD: 759193149Sdougb case DNS_DIFFOP_ADDRESIGN: 760193149Sdougb bop = 0; 761193149Sdougb break; 762193149Sdougb default: 763193149Sdougb INSIST(0); 764193149Sdougb } 765193149Sdougb 766193149Sdougb r = bop - aop; 767135446Strhodes if (r != 0) 768135446Strhodes return (r); 769135446Strhodes 770135446Strhodes r = (b->rdata.type == dns_rdatatype_soa) - 771135446Strhodes (a->rdata.type == dns_rdatatype_soa); 772135446Strhodes if (r != 0) 773135446Strhodes return (r); 774135446Strhodes 775135446Strhodes r = (a->rdata.type - b->rdata.type); 776135446Strhodes return (r); 777135446Strhodes} 778135446Strhodes 779135446Strhodes/* 780135446Strhodes * Advance '*pos' to the next journal transaction. 781135446Strhodes * 782135446Strhodes * Requires: 783135446Strhodes * *pos refers to a valid journal transaction. 784135446Strhodes * 785135446Strhodes * Ensures: 786135446Strhodes * When ISC_R_SUCCESS is returned, 787135446Strhodes * *pos refers to the next journal transaction. 788135446Strhodes * 789135446Strhodes * Returns one of: 790135446Strhodes * 791135446Strhodes * ISC_R_SUCCESS 792135446Strhodes * ISC_R_NOMORE *pos pointed at the last transaction 793135446Strhodes * Other results due to file errors are possible. 794135446Strhodes */ 795135446Strhodesstatic isc_result_t 796135446Strhodesjournal_next(dns_journal_t *j, journal_pos_t *pos) { 797135446Strhodes isc_result_t result; 798135446Strhodes journal_xhdr_t xhdr; 799135446Strhodes REQUIRE(DNS_JOURNAL_VALID(j)); 800135446Strhodes 801135446Strhodes result = journal_seek(j, pos->offset); 802135446Strhodes if (result != ISC_R_SUCCESS) 803135446Strhodes return (result); 804135446Strhodes 805135446Strhodes if (pos->serial == j->header.end.serial) 806135446Strhodes return (ISC_R_NOMORE); 807135446Strhodes /* 808135446Strhodes * Read the header of the current transaction. 809135446Strhodes * This will return ISC_R_NOMORE if we are at EOF. 810135446Strhodes */ 811135446Strhodes result = journal_read_xhdr(j, &xhdr); 812135446Strhodes if (result != ISC_R_SUCCESS) 813135446Strhodes return (result); 814135446Strhodes 815135446Strhodes /* 816135446Strhodes * Check serial number consistency. 817135446Strhodes */ 818135446Strhodes if (xhdr.serial0 != pos->serial) { 819135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 820135446Strhodes "%s: journal file corrupt: " 821135446Strhodes "expected serial %u, got %u", 822135446Strhodes j->filename, pos->serial, xhdr.serial0); 823135446Strhodes return (ISC_R_UNEXPECTED); 824135446Strhodes } 825135446Strhodes 826135446Strhodes /* 827135446Strhodes * Check for offset wraparound. 828135446Strhodes */ 829135446Strhodes if ((isc_offset_t)(pos->offset + sizeof(journal_rawxhdr_t) + xhdr.size) 830135446Strhodes < pos->offset) { 831135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 832135446Strhodes "%s: offset too large", j->filename); 833135446Strhodes return (ISC_R_UNEXPECTED); 834135446Strhodes } 835135446Strhodes 836135446Strhodes pos->offset += sizeof(journal_rawxhdr_t) + xhdr.size; 837135446Strhodes pos->serial = xhdr.serial1; 838135446Strhodes return (ISC_R_SUCCESS); 839135446Strhodes} 840135446Strhodes 841135446Strhodes/* 842135446Strhodes * If the index of the journal 'j' contains an entry "better" 843135446Strhodes * than '*best_guess', replace '*best_guess' with it. 844135446Strhodes * 845135446Strhodes * "Better" means having a serial number closer to 'serial' 846135446Strhodes * but not greater than 'serial'. 847135446Strhodes */ 848135446Strhodesstatic void 849135446Strhodesindex_find(dns_journal_t *j, isc_uint32_t serial, journal_pos_t *best_guess) { 850135446Strhodes unsigned int i; 851135446Strhodes if (j->index == NULL) 852135446Strhodes return; 853135446Strhodes for (i = 0; i < j->header.index_size; i++) { 854135446Strhodes if (POS_VALID(j->index[i]) && 855135446Strhodes DNS_SERIAL_GE(serial, j->index[i].serial) && 856135446Strhodes DNS_SERIAL_GT(j->index[i].serial, best_guess->serial)) 857135446Strhodes *best_guess = j->index[i]; 858135446Strhodes } 859135446Strhodes} 860135446Strhodes 861135446Strhodes/* 862135446Strhodes * Add a new index entry. If there is no room, make room by removing 863135446Strhodes * the odd-numbered entries and compacting the others into the first 864135446Strhodes * half of the index. This decimates old index entries exponentially 865135446Strhodes * over time, so that the index always contains a much larger fraction 866135446Strhodes * of recent serial numbers than of old ones. This is deliberate - 867135446Strhodes * most index searches are for outgoing IXFR, and IXFR tends to request 868135446Strhodes * recent versions more often than old ones. 869135446Strhodes */ 870135446Strhodesstatic void 871135446Strhodesindex_add(dns_journal_t *j, journal_pos_t *pos) { 872135446Strhodes unsigned int i; 873135446Strhodes if (j->index == NULL) 874135446Strhodes return; 875135446Strhodes /* 876135446Strhodes * Search for a vacant position. 877135446Strhodes */ 878135446Strhodes for (i = 0; i < j->header.index_size; i++) { 879135446Strhodes if (! POS_VALID(j->index[i])) 880135446Strhodes break; 881135446Strhodes } 882135446Strhodes if (i == j->header.index_size) { 883135446Strhodes unsigned int k = 0; 884135446Strhodes /* 885135446Strhodes * Found no vacant position. Make some room. 886135446Strhodes */ 887135446Strhodes for (i = 0; i < j->header.index_size; i += 2) { 888135446Strhodes j->index[k++] = j->index[i]; 889135446Strhodes } 890135446Strhodes i = k; /* 'i' identifies the first vacant position. */ 891135446Strhodes while (k < j->header.index_size) { 892135446Strhodes POS_INVALIDATE(j->index[k]); 893135446Strhodes k++; 894135446Strhodes } 895135446Strhodes } 896135446Strhodes INSIST(i < j->header.index_size); 897135446Strhodes INSIST(! POS_VALID(j->index[i])); 898135446Strhodes 899135446Strhodes /* 900135446Strhodes * Store the new index entry. 901135446Strhodes */ 902135446Strhodes j->index[i] = *pos; 903135446Strhodes} 904135446Strhodes 905135446Strhodes/* 906135446Strhodes * Invalidate any existing index entries that could become 907135446Strhodes * ambiguous when a new transaction with number 'serial' is added. 908135446Strhodes */ 909135446Strhodesstatic void 910135446Strhodesindex_invalidate(dns_journal_t *j, isc_uint32_t serial) { 911135446Strhodes unsigned int i; 912135446Strhodes if (j->index == NULL) 913135446Strhodes return; 914135446Strhodes for (i = 0; i < j->header.index_size; i++) { 915135446Strhodes if (! DNS_SERIAL_GT(serial, j->index[i].serial)) 916135446Strhodes POS_INVALIDATE(j->index[i]); 917135446Strhodes } 918135446Strhodes} 919135446Strhodes 920135446Strhodes/* 921135446Strhodes * Try to find a transaction with initial serial number 'serial' 922135446Strhodes * in the journal 'j'. 923135446Strhodes * 924135446Strhodes * If found, store its position at '*pos' and return ISC_R_SUCCESS. 925135446Strhodes * 926135446Strhodes * If 'serial' is current (= the ending serial number of the 927135446Strhodes * last transaction in the journal), set '*pos' to 928135446Strhodes * the position immediately following the last transaction and 929135446Strhodes * return ISC_R_SUCCESS. 930135446Strhodes * 931135446Strhodes * If 'serial' is within the range of addressable serial numbers 932135446Strhodes * covered by the journal but that particular serial number is missing 933135446Strhodes * (from the journal, not just from the index), return ISC_R_NOTFOUND. 934135446Strhodes * 935135446Strhodes * If 'serial' is outside the range of addressable serial numbers 936135446Strhodes * covered by the journal, return ISC_R_RANGE. 937135446Strhodes * 938135446Strhodes */ 939135446Strhodesstatic isc_result_t 940135446Strhodesjournal_find(dns_journal_t *j, isc_uint32_t serial, journal_pos_t *pos) { 941135446Strhodes isc_result_t result; 942135446Strhodes journal_pos_t current_pos; 943135446Strhodes REQUIRE(DNS_JOURNAL_VALID(j)); 944135446Strhodes 945135446Strhodes if (DNS_SERIAL_GT(j->header.begin.serial, serial)) 946135446Strhodes return (ISC_R_RANGE); 947135446Strhodes if (DNS_SERIAL_GT(serial, j->header.end.serial)) 948135446Strhodes return (ISC_R_RANGE); 949135446Strhodes if (serial == j->header.end.serial) { 950135446Strhodes *pos = j->header.end; 951135446Strhodes return (ISC_R_SUCCESS); 952135446Strhodes } 953135446Strhodes 954135446Strhodes current_pos = j->header.begin; 955135446Strhodes index_find(j, serial, ¤t_pos); 956135446Strhodes 957135446Strhodes while (current_pos.serial != serial) { 958135446Strhodes if (DNS_SERIAL_GT(current_pos.serial, serial)) 959135446Strhodes return (ISC_R_NOTFOUND); 960135446Strhodes result = journal_next(j, ¤t_pos); 961135446Strhodes if (result != ISC_R_SUCCESS) 962135446Strhodes return (result); 963135446Strhodes } 964135446Strhodes *pos = current_pos; 965135446Strhodes return (ISC_R_SUCCESS); 966135446Strhodes} 967135446Strhodes 968135446Strhodesisc_result_t 969135446Strhodesdns_journal_begin_transaction(dns_journal_t *j) { 970135446Strhodes isc_uint32_t offset; 971135446Strhodes isc_result_t result; 972135446Strhodes journal_rawxhdr_t hdr; 973135446Strhodes 974135446Strhodes REQUIRE(DNS_JOURNAL_VALID(j)); 975254897Serwin REQUIRE(j->state == JOURNAL_STATE_WRITE || 976254897Serwin j->state == JOURNAL_STATE_INLINE); 977135446Strhodes 978135446Strhodes /* 979135446Strhodes * Find the file offset where the new transaction should 980135446Strhodes * be written, and seek there. 981135446Strhodes */ 982135446Strhodes if (JOURNAL_EMPTY(&j->header)) { 983135446Strhodes offset = sizeof(journal_rawheader_t) + 984135446Strhodes j->header.index_size * sizeof(journal_rawpos_t); 985135446Strhodes } else { 986135446Strhodes offset = j->header.end.offset; 987135446Strhodes } 988135446Strhodes j->x.pos[0].offset = offset; 989135446Strhodes j->x.pos[1].offset = offset; /* Initial value, will be incremented. */ 990135446Strhodes j->x.n_soa = 0; 991135446Strhodes 992135446Strhodes CHECK(journal_seek(j, offset)); 993135446Strhodes 994135446Strhodes /* 995135446Strhodes * Write a dummy transaction header of all zeroes to reserve 996135446Strhodes * space. It will be filled in when the transaction is 997135446Strhodes * finished. 998135446Strhodes */ 999135446Strhodes memset(&hdr, 0, sizeof(hdr)); 1000135446Strhodes CHECK(journal_write(j, &hdr, sizeof(hdr))); 1001135446Strhodes j->x.pos[1].offset = j->offset; 1002135446Strhodes 1003135446Strhodes j->state = JOURNAL_STATE_TRANSACTION; 1004135446Strhodes result = ISC_R_SUCCESS; 1005135446Strhodes failure: 1006135446Strhodes return (result); 1007135446Strhodes} 1008135446Strhodes 1009135446Strhodesisc_result_t 1010135446Strhodesdns_journal_writediff(dns_journal_t *j, dns_diff_t *diff) { 1011135446Strhodes dns_difftuple_t *t; 1012135446Strhodes isc_buffer_t buffer; 1013135446Strhodes void *mem = NULL; 1014135446Strhodes unsigned int size; 1015135446Strhodes isc_result_t result; 1016135446Strhodes isc_region_t used; 1017135446Strhodes 1018135446Strhodes REQUIRE(DNS_DIFF_VALID(diff)); 1019135446Strhodes REQUIRE(j->state == JOURNAL_STATE_TRANSACTION); 1020135446Strhodes 1021135446Strhodes isc_log_write(JOURNAL_DEBUG_LOGARGS(3), "writing to journal"); 1022135446Strhodes (void)dns_diff_print(diff, NULL); 1023135446Strhodes 1024135446Strhodes /* 1025135446Strhodes * Pass 1: determine the buffer size needed, and 1026135446Strhodes * keep track of SOA serial numbers. 1027135446Strhodes */ 1028135446Strhodes size = 0; 1029135446Strhodes for (t = ISC_LIST_HEAD(diff->tuples); t != NULL; 1030135446Strhodes t = ISC_LIST_NEXT(t, link)) 1031135446Strhodes { 1032135446Strhodes if (t->rdata.type == dns_rdatatype_soa) { 1033135446Strhodes if (j->x.n_soa < 2) 1034135446Strhodes j->x.pos[j->x.n_soa].serial = 1035135446Strhodes dns_soa_getserial(&t->rdata); 1036135446Strhodes j->x.n_soa++; 1037135446Strhodes } 1038135446Strhodes size += sizeof(journal_rawrrhdr_t); 1039135446Strhodes size += t->name.length; /* XXX should have access macro? */ 1040135446Strhodes size += 10; 1041135446Strhodes size += t->rdata.length; 1042135446Strhodes } 1043135446Strhodes 1044135446Strhodes mem = isc_mem_get(j->mctx, size); 1045135446Strhodes if (mem == NULL) 1046135446Strhodes return (ISC_R_NOMEMORY); 1047135446Strhodes 1048135446Strhodes isc_buffer_init(&buffer, mem, size); 1049135446Strhodes 1050135446Strhodes /* 1051135446Strhodes * Pass 2. Write RRs to buffer. 1052135446Strhodes */ 1053135446Strhodes for (t = ISC_LIST_HEAD(diff->tuples); t != NULL; 1054135446Strhodes t = ISC_LIST_NEXT(t, link)) 1055135446Strhodes { 1056135446Strhodes /* 1057135446Strhodes * Write the RR header. 1058135446Strhodes */ 1059135446Strhodes isc_buffer_putuint32(&buffer, t->name.length + 10 + 1060135446Strhodes t->rdata.length); 1061135446Strhodes /* 1062135446Strhodes * Write the owner name, RR header, and RR data. 1063135446Strhodes */ 1064135446Strhodes isc_buffer_putmem(&buffer, t->name.ndata, t->name.length); 1065135446Strhodes isc_buffer_putuint16(&buffer, t->rdata.type); 1066135446Strhodes isc_buffer_putuint16(&buffer, t->rdata.rdclass); 1067135446Strhodes isc_buffer_putuint32(&buffer, t->ttl); 1068135446Strhodes INSIST(t->rdata.length < 65536); 1069135446Strhodes isc_buffer_putuint16(&buffer, (isc_uint16_t)t->rdata.length); 1070135446Strhodes INSIST(isc_buffer_availablelength(&buffer) >= t->rdata.length); 1071135446Strhodes isc_buffer_putmem(&buffer, t->rdata.data, t->rdata.length); 1072135446Strhodes } 1073135446Strhodes 1074135446Strhodes isc_buffer_usedregion(&buffer, &used); 1075135446Strhodes INSIST(used.length == size); 1076135446Strhodes 1077135446Strhodes j->x.pos[1].offset += used.length; 1078135446Strhodes 1079135446Strhodes /* 1080135446Strhodes * Write the buffer contents to the journal file. 1081135446Strhodes */ 1082135446Strhodes CHECK(journal_write(j, used.base, used.length)); 1083135446Strhodes 1084135446Strhodes result = ISC_R_SUCCESS; 1085135446Strhodes 1086135446Strhodes failure: 1087135446Strhodes if (mem != NULL) 1088135446Strhodes isc_mem_put(j->mctx, mem, size); 1089135446Strhodes return (result); 1090135446Strhodes 1091135446Strhodes} 1092135446Strhodes 1093135446Strhodesisc_result_t 1094135446Strhodesdns_journal_commit(dns_journal_t *j) { 1095135446Strhodes isc_result_t result; 1096135446Strhodes journal_rawheader_t rawheader; 1097135446Strhodes 1098135446Strhodes REQUIRE(DNS_JOURNAL_VALID(j)); 1099254897Serwin REQUIRE(j->state == JOURNAL_STATE_TRANSACTION || 1100254897Serwin j->state == JOURNAL_STATE_INLINE); 1101135446Strhodes 1102135446Strhodes /* 1103254897Serwin * Just write out a updated header. 1104254897Serwin */ 1105254897Serwin if (j->state == JOURNAL_STATE_INLINE) { 1106254897Serwin CHECK(journal_fsync(j)); 1107254897Serwin journal_header_encode(&j->header, &rawheader); 1108254897Serwin CHECK(journal_seek(j, 0)); 1109254897Serwin CHECK(journal_write(j, &rawheader, sizeof(rawheader))); 1110254897Serwin CHECK(journal_fsync(j)); 1111254897Serwin j->state = JOURNAL_STATE_WRITE; 1112254897Serwin return (ISC_R_SUCCESS); 1113254897Serwin } 1114254897Serwin 1115254897Serwin /* 1116135446Strhodes * Perform some basic consistency checks. 1117135446Strhodes */ 1118135446Strhodes if (j->x.n_soa != 2) { 1119135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 1120143731Sdougb "%s: malformed transaction: %d SOAs", 1121143731Sdougb j->filename, j->x.n_soa); 1122135446Strhodes return (ISC_R_UNEXPECTED); 1123135446Strhodes } 1124135446Strhodes if (! (DNS_SERIAL_GT(j->x.pos[1].serial, j->x.pos[0].serial) || 1125135446Strhodes (bind8_compat && 1126135446Strhodes j->x.pos[1].serial == j->x.pos[0].serial))) 1127135446Strhodes { 1128135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 1129143731Sdougb "%s: malformed transaction: serial number " 1130143731Sdougb "would decrease", j->filename); 1131135446Strhodes return (ISC_R_UNEXPECTED); 1132135446Strhodes } 1133135446Strhodes if (! JOURNAL_EMPTY(&j->header)) { 1134135446Strhodes if (j->x.pos[0].serial != j->header.end.serial) { 1135135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 1136135446Strhodes "malformed transaction: " 1137135446Strhodes "%s last serial %u != " 1138135446Strhodes "transaction first serial %u", 1139135446Strhodes j->filename, 1140135446Strhodes j->header.end.serial, 1141135446Strhodes j->x.pos[0].serial); 1142135446Strhodes return (ISC_R_UNEXPECTED); 1143135446Strhodes } 1144135446Strhodes } 1145135446Strhodes 1146135446Strhodes /* 1147135446Strhodes * Some old journal entries may become non-addressable 1148135446Strhodes * when we increment the current serial number. Purge them 1149135446Strhodes * by stepping header.begin forward to the first addressable 1150135446Strhodes * transaction. Also purge them from the index. 1151135446Strhodes */ 1152135446Strhodes if (! JOURNAL_EMPTY(&j->header)) { 1153135446Strhodes while (! DNS_SERIAL_GT(j->x.pos[1].serial, 1154135446Strhodes j->header.begin.serial)) { 1155135446Strhodes CHECK(journal_next(j, &j->header.begin)); 1156135446Strhodes } 1157135446Strhodes index_invalidate(j, j->x.pos[1].serial); 1158135446Strhodes } 1159135446Strhodes#ifdef notyet 1160135446Strhodes if (DNS_SERIAL_GT(last_dumped_serial, j->x.pos[1].serial)) { 1161135446Strhodes force_dump(...); 1162135446Strhodes } 1163135446Strhodes#endif 1164135446Strhodes 1165135446Strhodes /* 1166135446Strhodes * Commit the transaction data to stable storage. 1167135446Strhodes */ 1168135446Strhodes CHECK(journal_fsync(j)); 1169135446Strhodes 1170254897Serwin if (j->state == JOURNAL_STATE_TRANSACTION) { 1171254897Serwin isc_offset_t offset; 1172254897Serwin offset = (j->x.pos[1].offset - j->x.pos[0].offset) - 1173254897Serwin sizeof(journal_rawxhdr_t); 1174254897Serwin /* 1175254897Serwin * Update the transaction header. 1176254897Serwin */ 1177254897Serwin CHECK(journal_seek(j, j->x.pos[0].offset)); 1178254897Serwin CHECK(journal_write_xhdr(j, offset, j->x.pos[0].serial, 1179254897Serwin j->x.pos[1].serial)); 1180254897Serwin } 1181135446Strhodes 1182135446Strhodes /* 1183135446Strhodes * Update the journal header. 1184135446Strhodes */ 1185254897Serwin if (JOURNAL_EMPTY(&j->header)) 1186135446Strhodes j->header.begin = j->x.pos[0]; 1187135446Strhodes j->header.end = j->x.pos[1]; 1188135446Strhodes journal_header_encode(&j->header, &rawheader); 1189135446Strhodes CHECK(journal_seek(j, 0)); 1190135446Strhodes CHECK(journal_write(j, &rawheader, sizeof(rawheader))); 1191135446Strhodes 1192135446Strhodes /* 1193135446Strhodes * Update the index. 1194135446Strhodes */ 1195135446Strhodes index_add(j, &j->x.pos[0]); 1196135446Strhodes 1197135446Strhodes /* 1198135446Strhodes * Convert the index into on-disk format and write 1199135446Strhodes * it to disk. 1200135446Strhodes */ 1201135446Strhodes CHECK(index_to_disk(j)); 1202135446Strhodes 1203135446Strhodes /* 1204135446Strhodes * Commit the header to stable storage. 1205135446Strhodes */ 1206135446Strhodes CHECK(journal_fsync(j)); 1207135446Strhodes 1208135446Strhodes /* 1209135446Strhodes * We no longer have a transaction open. 1210135446Strhodes */ 1211135446Strhodes j->state = JOURNAL_STATE_WRITE; 1212135446Strhodes 1213135446Strhodes result = ISC_R_SUCCESS; 1214135446Strhodes 1215135446Strhodes failure: 1216135446Strhodes return (result); 1217135446Strhodes} 1218135446Strhodes 1219135446Strhodesisc_result_t 1220135446Strhodesdns_journal_write_transaction(dns_journal_t *j, dns_diff_t *diff) { 1221135446Strhodes isc_result_t result; 1222135446Strhodes CHECK(dns_diff_sort(diff, ixfr_order)); 1223135446Strhodes CHECK(dns_journal_begin_transaction(j)); 1224135446Strhodes CHECK(dns_journal_writediff(j, diff)); 1225135446Strhodes CHECK(dns_journal_commit(j)); 1226135446Strhodes result = ISC_R_SUCCESS; 1227135446Strhodes failure: 1228135446Strhodes return (result); 1229135446Strhodes} 1230135446Strhodes 1231135446Strhodesvoid 1232135446Strhodesdns_journal_destroy(dns_journal_t **journalp) { 1233135446Strhodes dns_journal_t *j = *journalp; 1234135446Strhodes REQUIRE(DNS_JOURNAL_VALID(j)); 1235135446Strhodes 1236135446Strhodes j->it.result = ISC_R_FAILURE; 1237135446Strhodes dns_name_invalidate(&j->it.name); 1238135446Strhodes dns_decompress_invalidate(&j->it.dctx); 1239135446Strhodes if (j->rawindex != NULL) 1240135446Strhodes isc_mem_put(j->mctx, j->rawindex, j->header.index_size * 1241135446Strhodes sizeof(journal_rawpos_t)); 1242135446Strhodes if (j->index != NULL) 1243135446Strhodes isc_mem_put(j->mctx, j->index, j->header.index_size * 1244135446Strhodes sizeof(journal_pos_t)); 1245135446Strhodes if (j->it.target.base != NULL) 1246135446Strhodes isc_mem_put(j->mctx, j->it.target.base, j->it.target.length); 1247135446Strhodes if (j->it.source.base != NULL) 1248135446Strhodes isc_mem_put(j->mctx, j->it.source.base, j->it.source.length); 1249262706Serwin if (j->filename != NULL) 1250262706Serwin isc_mem_free(j->mctx, j->filename); 1251135446Strhodes if (j->fp != NULL) 1252135446Strhodes (void)isc_stdio_close(j->fp); 1253135446Strhodes j->magic = 0; 1254254897Serwin isc_mem_putanddetach(&j->mctx, j, sizeof(*j)); 1255135446Strhodes *journalp = NULL; 1256135446Strhodes} 1257135446Strhodes 1258135446Strhodes/* 1259135446Strhodes * Roll the open journal 'j' into the database 'db'. 1260135446Strhodes * A new database version will be created. 1261135446Strhodes */ 1262135446Strhodes 1263135446Strhodes/* XXX Share code with incoming IXFR? */ 1264135446Strhodes 1265135446Strhodesstatic isc_result_t 1266262706Serwinroll_forward(dns_journal_t *j, dns_db_t *db, unsigned int options) { 1267135446Strhodes isc_buffer_t source; /* Transaction data from disk */ 1268135446Strhodes isc_buffer_t target; /* Ditto after _fromwire check */ 1269135446Strhodes isc_uint32_t db_serial; /* Database SOA serial */ 1270135446Strhodes isc_uint32_t end_serial; /* Last journal SOA serial */ 1271135446Strhodes isc_result_t result; 1272135446Strhodes dns_dbversion_t *ver = NULL; 1273135446Strhodes journal_pos_t pos; 1274135446Strhodes dns_diff_t diff; 1275135446Strhodes unsigned int n_soa = 0; 1276135446Strhodes unsigned int n_put = 0; 1277193149Sdougb dns_diffop_t op; 1278135446Strhodes 1279135446Strhodes REQUIRE(DNS_JOURNAL_VALID(j)); 1280135446Strhodes REQUIRE(DNS_DB_VALID(db)); 1281135446Strhodes 1282135446Strhodes dns_diff_init(j->mctx, &diff); 1283135446Strhodes 1284135446Strhodes /* 1285193149Sdougb * Set up empty initial buffers for unchecked and checked 1286135446Strhodes * wire format transaction data. They will be reallocated 1287135446Strhodes * later. 1288135446Strhodes */ 1289135446Strhodes isc_buffer_init(&source, NULL, 0); 1290135446Strhodes isc_buffer_init(&target, NULL, 0); 1291135446Strhodes 1292135446Strhodes /* 1293135446Strhodes * Create the new database version. 1294135446Strhodes */ 1295135446Strhodes CHECK(dns_db_newversion(db, &ver)); 1296135446Strhodes 1297135446Strhodes /* 1298135446Strhodes * Get the current database SOA serial number. 1299135446Strhodes */ 1300135446Strhodes CHECK(dns_db_getsoaserial(db, ver, &db_serial)); 1301135446Strhodes 1302135446Strhodes /* 1303135446Strhodes * Locate a journal entry for the current database serial. 1304135446Strhodes */ 1305135446Strhodes CHECK(journal_find(j, db_serial, &pos)); 1306135446Strhodes /* 1307135446Strhodes * XXX do more drastic things, like marking zone stale, 1308135446Strhodes * if this fails? 1309135446Strhodes */ 1310135446Strhodes /* 1311135446Strhodes * XXXRTH The zone code should probably mark the zone as bad and 1312135446Strhodes * scream loudly into the log if this is a dynamic update 1313135446Strhodes * log reply that failed. 1314135446Strhodes */ 1315135446Strhodes 1316135446Strhodes end_serial = dns_journal_last_serial(j); 1317135446Strhodes if (db_serial == end_serial) 1318135446Strhodes CHECK(DNS_R_UPTODATE); 1319135446Strhodes 1320135446Strhodes CHECK(dns_journal_iter_init(j, db_serial, end_serial)); 1321135446Strhodes 1322135446Strhodes for (result = dns_journal_first_rr(j); 1323135446Strhodes result == ISC_R_SUCCESS; 1324135446Strhodes result = dns_journal_next_rr(j)) 1325135446Strhodes { 1326135446Strhodes dns_name_t *name; 1327135446Strhodes isc_uint32_t ttl; 1328135446Strhodes dns_rdata_t *rdata; 1329135446Strhodes dns_difftuple_t *tuple = NULL; 1330135446Strhodes 1331135446Strhodes name = NULL; 1332135446Strhodes rdata = NULL; 1333135446Strhodes dns_journal_current_rr(j, &name, &ttl, &rdata); 1334135446Strhodes 1335135446Strhodes if (rdata->type == dns_rdatatype_soa) { 1336135446Strhodes n_soa++; 1337135446Strhodes if (n_soa == 2) 1338135446Strhodes db_serial = j->it.current_serial; 1339135446Strhodes } 1340135446Strhodes 1341135446Strhodes if (n_soa == 3) 1342135446Strhodes n_soa = 1; 1343135446Strhodes if (n_soa == 0) { 1344135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 1345135446Strhodes "%s: journal file corrupt: missing " 1346135446Strhodes "initial SOA", j->filename); 1347135446Strhodes FAIL(ISC_R_UNEXPECTED); 1348135446Strhodes } 1349193149Sdougb if ((options & DNS_JOURNALOPT_RESIGN) != 0) 1350193149Sdougb op = (n_soa == 1) ? DNS_DIFFOP_DELRESIGN : 1351193149Sdougb DNS_DIFFOP_ADDRESIGN; 1352193149Sdougb else 1353193149Sdougb op = (n_soa == 1) ? DNS_DIFFOP_DEL : DNS_DIFFOP_ADD; 1354193149Sdougb 1355193149Sdougb CHECK(dns_difftuple_create(diff.mctx, op, name, ttl, rdata, 1356193149Sdougb &tuple)); 1357135446Strhodes dns_diff_append(&diff, &tuple); 1358135446Strhodes 1359135446Strhodes if (++n_put > 100) { 1360135446Strhodes isc_log_write(JOURNAL_DEBUG_LOGARGS(3), 1361143731Sdougb "%s: applying diff to database (%u)", 1362143731Sdougb j->filename, db_serial); 1363135446Strhodes (void)dns_diff_print(&diff, NULL); 1364135446Strhodes CHECK(dns_diff_apply(&diff, db, ver)); 1365135446Strhodes dns_diff_clear(&diff); 1366135446Strhodes n_put = 0; 1367135446Strhodes } 1368135446Strhodes } 1369135446Strhodes if (result == ISC_R_NOMORE) 1370135446Strhodes result = ISC_R_SUCCESS; 1371135446Strhodes CHECK(result); 1372135446Strhodes 1373135446Strhodes if (n_put != 0) { 1374135446Strhodes isc_log_write(JOURNAL_DEBUG_LOGARGS(3), 1375143731Sdougb "%s: applying final diff to database (%u)", 1376143731Sdougb j->filename, db_serial); 1377135446Strhodes (void)dns_diff_print(&diff, NULL); 1378135446Strhodes CHECK(dns_diff_apply(&diff, db, ver)); 1379135446Strhodes dns_diff_clear(&diff); 1380135446Strhodes } 1381135446Strhodes 1382135446Strhodes failure: 1383135446Strhodes if (ver != NULL) 1384135446Strhodes dns_db_closeversion(db, &ver, result == ISC_R_SUCCESS ? 1385135446Strhodes ISC_TRUE : ISC_FALSE); 1386135446Strhodes 1387135446Strhodes if (source.base != NULL) 1388135446Strhodes isc_mem_put(j->mctx, source.base, source.length); 1389135446Strhodes if (target.base != NULL) 1390135446Strhodes isc_mem_put(j->mctx, target.base, target.length); 1391135446Strhodes 1392135446Strhodes dns_diff_clear(&diff); 1393135446Strhodes 1394135446Strhodes return (result); 1395135446Strhodes} 1396135446Strhodes 1397135446Strhodesisc_result_t 1398193149Sdougbdns_journal_rollforward(isc_mem_t *mctx, dns_db_t *db, 1399193149Sdougb unsigned int options, const char *filename) 1400193149Sdougb{ 1401204619Sdougb REQUIRE((options & DNS_JOURNALOPT_RESIGN) == 0); 1402204619Sdougb return (dns_journal_rollforward2(mctx, db, options, 0, filename)); 1403204619Sdougb} 1404204619Sdougb 1405204619Sdougbisc_result_t 1406204619Sdougbdns_journal_rollforward2(isc_mem_t *mctx, dns_db_t *db, unsigned int options, 1407204619Sdougb isc_uint32_t resign, const char *filename) 1408204619Sdougb{ 1409135446Strhodes dns_journal_t *j; 1410135446Strhodes isc_result_t result; 1411135446Strhodes 1412135446Strhodes REQUIRE(DNS_DB_VALID(db)); 1413135446Strhodes REQUIRE(filename != NULL); 1414135446Strhodes 1415262706Serwin UNUSED(resign); 1416262706Serwin 1417135446Strhodes j = NULL; 1418254897Serwin result = dns_journal_open(mctx, filename, DNS_JOURNAL_READ, &j); 1419135446Strhodes if (result == ISC_R_NOTFOUND) { 1420135446Strhodes isc_log_write(JOURNAL_DEBUG_LOGARGS(3), 1421135446Strhodes "no journal file, but that's OK"); 1422135446Strhodes return (DNS_R_NOJOURNAL); 1423135446Strhodes } 1424135446Strhodes if (result != ISC_R_SUCCESS) 1425135446Strhodes return (result); 1426135446Strhodes if (JOURNAL_EMPTY(&j->header)) 1427135446Strhodes result = DNS_R_UPTODATE; 1428135446Strhodes else 1429262706Serwin result = roll_forward(j, db, options); 1430135446Strhodes 1431135446Strhodes dns_journal_destroy(&j); 1432135446Strhodes 1433135446Strhodes return (result); 1434135446Strhodes} 1435135446Strhodes 1436135446Strhodesisc_result_t 1437135446Strhodesdns_journal_print(isc_mem_t *mctx, const char *filename, FILE *file) { 1438135446Strhodes dns_journal_t *j; 1439135446Strhodes isc_buffer_t source; /* Transaction data from disk */ 1440135446Strhodes isc_buffer_t target; /* Ditto after _fromwire check */ 1441135446Strhodes isc_uint32_t start_serial; /* Database SOA serial */ 1442135446Strhodes isc_uint32_t end_serial; /* Last journal SOA serial */ 1443135446Strhodes isc_result_t result; 1444135446Strhodes dns_diff_t diff; 1445135446Strhodes unsigned int n_soa = 0; 1446135446Strhodes unsigned int n_put = 0; 1447135446Strhodes 1448135446Strhodes REQUIRE(filename != NULL); 1449135446Strhodes 1450135446Strhodes j = NULL; 1451254897Serwin result = dns_journal_open(mctx, filename, DNS_JOURNAL_READ, &j); 1452135446Strhodes if (result == ISC_R_NOTFOUND) { 1453135446Strhodes isc_log_write(JOURNAL_DEBUG_LOGARGS(3), "no journal file"); 1454135446Strhodes return (DNS_R_NOJOURNAL); 1455135446Strhodes } 1456135446Strhodes 1457135446Strhodes if (result != ISC_R_SUCCESS) { 1458135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 1459143731Sdougb "journal open failure: %s: %s", 1460186462Sdougb isc_result_totext(result), filename); 1461135446Strhodes return (result); 1462135446Strhodes } 1463135446Strhodes 1464254897Serwin if (j->header.serialset) 1465254897Serwin fprintf(file, "Source serial = %u\n", j->header.sourceserial); 1466135446Strhodes dns_diff_init(j->mctx, &diff); 1467135446Strhodes 1468135446Strhodes /* 1469193149Sdougb * Set up empty initial buffers for unchecked and checked 1470135446Strhodes * wire format transaction data. They will be reallocated 1471135446Strhodes * later. 1472135446Strhodes */ 1473135446Strhodes isc_buffer_init(&source, NULL, 0); 1474135446Strhodes isc_buffer_init(&target, NULL, 0); 1475135446Strhodes 1476135446Strhodes start_serial = dns_journal_first_serial(j); 1477135446Strhodes end_serial = dns_journal_last_serial(j); 1478135446Strhodes 1479135446Strhodes CHECK(dns_journal_iter_init(j, start_serial, end_serial)); 1480135446Strhodes 1481135446Strhodes for (result = dns_journal_first_rr(j); 1482135446Strhodes result == ISC_R_SUCCESS; 1483135446Strhodes result = dns_journal_next_rr(j)) 1484135446Strhodes { 1485135446Strhodes dns_name_t *name; 1486135446Strhodes isc_uint32_t ttl; 1487135446Strhodes dns_rdata_t *rdata; 1488135446Strhodes dns_difftuple_t *tuple = NULL; 1489135446Strhodes 1490135446Strhodes name = NULL; 1491135446Strhodes rdata = NULL; 1492135446Strhodes dns_journal_current_rr(j, &name, &ttl, &rdata); 1493135446Strhodes 1494135446Strhodes if (rdata->type == dns_rdatatype_soa) 1495135446Strhodes n_soa++; 1496135446Strhodes 1497135446Strhodes if (n_soa == 3) 1498135446Strhodes n_soa = 1; 1499135446Strhodes if (n_soa == 0) { 1500186462Sdougb isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 1501186462Sdougb "%s: journal file corrupt: missing " 1502186462Sdougb "initial SOA", j->filename); 1503135446Strhodes FAIL(ISC_R_UNEXPECTED); 1504135446Strhodes } 1505135446Strhodes CHECK(dns_difftuple_create(diff.mctx, n_soa == 1 ? 1506135446Strhodes DNS_DIFFOP_DEL : DNS_DIFFOP_ADD, 1507135446Strhodes name, ttl, rdata, &tuple)); 1508135446Strhodes dns_diff_append(&diff, &tuple); 1509135446Strhodes 1510135446Strhodes if (++n_put > 100) { 1511135446Strhodes result = dns_diff_print(&diff, file); 1512135446Strhodes dns_diff_clear(&diff); 1513135446Strhodes n_put = 0; 1514135446Strhodes if (result != ISC_R_SUCCESS) 1515135446Strhodes break; 1516135446Strhodes } 1517135446Strhodes } 1518135446Strhodes if (result == ISC_R_NOMORE) 1519135446Strhodes result = ISC_R_SUCCESS; 1520135446Strhodes CHECK(result); 1521135446Strhodes 1522135446Strhodes if (n_put != 0) { 1523135446Strhodes result = dns_diff_print(&diff, file); 1524135446Strhodes dns_diff_clear(&diff); 1525135446Strhodes } 1526135446Strhodes goto cleanup; 1527135446Strhodes 1528135446Strhodes failure: 1529135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 1530135446Strhodes "%s: cannot print: journal file corrupt", j->filename); 1531135446Strhodes 1532135446Strhodes cleanup: 1533135446Strhodes if (source.base != NULL) 1534135446Strhodes isc_mem_put(j->mctx, source.base, source.length); 1535135446Strhodes if (target.base != NULL) 1536135446Strhodes isc_mem_put(j->mctx, target.base, target.length); 1537135446Strhodes 1538135446Strhodes dns_diff_clear(&diff); 1539135446Strhodes dns_journal_destroy(&j); 1540135446Strhodes 1541135446Strhodes return (result); 1542135446Strhodes} 1543135446Strhodes 1544135446Strhodes/**************************************************************************/ 1545135446Strhodes/* 1546135446Strhodes * Miscellaneous accessors. 1547135446Strhodes */ 1548254897Serwinisc_uint32_t 1549254897Serwindns_journal_first_serial(dns_journal_t *j) { 1550135446Strhodes return (j->header.begin.serial); 1551135446Strhodes} 1552135446Strhodes 1553254897Serwinisc_uint32_t 1554254897Serwindns_journal_last_serial(dns_journal_t *j) { 1555135446Strhodes return (j->header.end.serial); 1556135446Strhodes} 1557135446Strhodes 1558254897Serwinvoid 1559254897Serwindns_journal_set_sourceserial(dns_journal_t *j, isc_uint32_t sourceserial) { 1560254897Serwin 1561254897Serwin REQUIRE(j->state == JOURNAL_STATE_WRITE || 1562254897Serwin j->state == JOURNAL_STATE_INLINE || 1563254897Serwin j->state == JOURNAL_STATE_TRANSACTION); 1564254897Serwin 1565254897Serwin j->header.sourceserial = sourceserial; 1566254897Serwin j->header.serialset = ISC_TRUE; 1567254897Serwin if (j->state == JOURNAL_STATE_WRITE) 1568254897Serwin j->state = JOURNAL_STATE_INLINE; 1569254897Serwin} 1570254897Serwin 1571254897Serwinisc_boolean_t 1572254897Serwindns_journal_get_sourceserial(dns_journal_t *j, isc_uint32_t *sourceserial) { 1573254897Serwin REQUIRE(sourceserial != NULL); 1574254897Serwin 1575254897Serwin if (!j->header.serialset) 1576254897Serwin return (ISC_FALSE); 1577254897Serwin *sourceserial = j->header.sourceserial; 1578254897Serwin return (ISC_TRUE); 1579254897Serwin} 1580254897Serwin 1581135446Strhodes/**************************************************************************/ 1582135446Strhodes/* 1583135446Strhodes * Iteration support. 1584135446Strhodes * 1585135446Strhodes * When serving an outgoing IXFR, we transmit a part the journal starting 1586135446Strhodes * at the serial number in the IXFR request and ending at the serial 1587135446Strhodes * number that is current when the IXFR request arrives. The ending 1588135446Strhodes * serial number is not necessarily at the end of the journal: 1589135446Strhodes * the journal may grow while the IXFR is in progress, but we stop 1590135446Strhodes * when we reach the serial number that was current when the IXFR started. 1591135446Strhodes */ 1592135446Strhodes 1593135446Strhodesstatic isc_result_t read_one_rr(dns_journal_t *j); 1594135446Strhodes 1595135446Strhodes/* 1596135446Strhodes * Make sure the buffer 'b' is has at least 'size' bytes 1597135446Strhodes * allocated, and clear it. 1598135446Strhodes * 1599135446Strhodes * Requires: 1600135446Strhodes * Either b->base is NULL, or it points to b->length bytes of memory 1601135446Strhodes * previously allocated by isc_mem_get(). 1602135446Strhodes */ 1603135446Strhodes 1604135446Strhodesstatic isc_result_t 1605135446Strhodessize_buffer(isc_mem_t *mctx, isc_buffer_t *b, unsigned size) { 1606135446Strhodes if (b->length < size) { 1607135446Strhodes void *mem = isc_mem_get(mctx, size); 1608135446Strhodes if (mem == NULL) 1609135446Strhodes return (ISC_R_NOMEMORY); 1610135446Strhodes if (b->base != NULL) 1611135446Strhodes isc_mem_put(mctx, b->base, b->length); 1612135446Strhodes b->base = mem; 1613135446Strhodes b->length = size; 1614135446Strhodes } 1615135446Strhodes isc_buffer_clear(b); 1616135446Strhodes return (ISC_R_SUCCESS); 1617135446Strhodes} 1618135446Strhodes 1619135446Strhodesisc_result_t 1620135446Strhodesdns_journal_iter_init(dns_journal_t *j, 1621135446Strhodes isc_uint32_t begin_serial, isc_uint32_t end_serial) 1622135446Strhodes{ 1623135446Strhodes isc_result_t result; 1624135446Strhodes 1625135446Strhodes CHECK(journal_find(j, begin_serial, &j->it.bpos)); 1626135446Strhodes INSIST(j->it.bpos.serial == begin_serial); 1627135446Strhodes 1628135446Strhodes CHECK(journal_find(j, end_serial, &j->it.epos)); 1629135446Strhodes INSIST(j->it.epos.serial == end_serial); 1630135446Strhodes 1631135446Strhodes result = ISC_R_SUCCESS; 1632135446Strhodes failure: 1633135446Strhodes j->it.result = result; 1634135446Strhodes return (j->it.result); 1635135446Strhodes} 1636135446Strhodes 1637135446Strhodes 1638135446Strhodesisc_result_t 1639135446Strhodesdns_journal_first_rr(dns_journal_t *j) { 1640135446Strhodes isc_result_t result; 1641135446Strhodes 1642135446Strhodes /* 1643135446Strhodes * Seek to the beginning of the first transaction we are 1644135446Strhodes * interested in. 1645135446Strhodes */ 1646135446Strhodes CHECK(journal_seek(j, j->it.bpos.offset)); 1647135446Strhodes j->it.current_serial = j->it.bpos.serial; 1648135446Strhodes 1649135446Strhodes j->it.xsize = 0; /* We have no transaction data yet... */ 1650135446Strhodes j->it.xpos = 0; /* ...and haven't used any of it. */ 1651135446Strhodes 1652135446Strhodes return (read_one_rr(j)); 1653135446Strhodes 1654135446Strhodes failure: 1655135446Strhodes return (result); 1656135446Strhodes} 1657135446Strhodes 1658135446Strhodesstatic isc_result_t 1659135446Strhodesread_one_rr(dns_journal_t *j) { 1660135446Strhodes isc_result_t result; 1661135446Strhodes 1662135446Strhodes dns_rdatatype_t rdtype; 1663135446Strhodes dns_rdataclass_t rdclass; 1664135446Strhodes unsigned int rdlen; 1665135446Strhodes isc_uint32_t ttl; 1666135446Strhodes journal_xhdr_t xhdr; 1667135446Strhodes journal_rrhdr_t rrhdr; 1668135446Strhodes 1669135446Strhodes INSIST(j->offset <= j->it.epos.offset); 1670135446Strhodes if (j->offset == j->it.epos.offset) 1671135446Strhodes return (ISC_R_NOMORE); 1672135446Strhodes if (j->it.xpos == j->it.xsize) { 1673135446Strhodes /* 1674135446Strhodes * We are at a transaction boundary. 1675135446Strhodes * Read another transaction header. 1676135446Strhodes */ 1677135446Strhodes CHECK(journal_read_xhdr(j, &xhdr)); 1678135446Strhodes if (xhdr.size == 0) { 1679135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 1680143731Sdougb "%s: journal corrupt: empty transaction", 1681143731Sdougb j->filename); 1682135446Strhodes FAIL(ISC_R_UNEXPECTED); 1683135446Strhodes } 1684135446Strhodes if (xhdr.serial0 != j->it.current_serial) { 1685135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 1686135446Strhodes "%s: journal file corrupt: " 1687135446Strhodes "expected serial %u, got %u", 1688135446Strhodes j->filename, 1689135446Strhodes j->it.current_serial, xhdr.serial0); 1690135446Strhodes FAIL(ISC_R_UNEXPECTED); 1691135446Strhodes } 1692135446Strhodes j->it.xsize = xhdr.size; 1693135446Strhodes j->it.xpos = 0; 1694135446Strhodes } 1695135446Strhodes /* 1696135446Strhodes * Read an RR. 1697135446Strhodes */ 1698153816Sdougb CHECK(journal_read_rrhdr(j, &rrhdr)); 1699135446Strhodes /* 1700135446Strhodes * Perform a sanity check on the journal RR size. 1701135446Strhodes * The smallest possible RR has a 1-byte owner name 1702135446Strhodes * and a 10-byte header. The largest possible 1703135446Strhodes * RR has 65535 bytes of data, a header, and a maximum- 1704135446Strhodes * size owner name, well below 70 k total. 1705135446Strhodes */ 1706135446Strhodes if (rrhdr.size < 1+10 || rrhdr.size > 70000) { 1707135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 1708135446Strhodes "%s: journal corrupt: impossible RR size " 1709135446Strhodes "(%d bytes)", j->filename, rrhdr.size); 1710135446Strhodes FAIL(ISC_R_UNEXPECTED); 1711135446Strhodes } 1712135446Strhodes 1713135446Strhodes CHECK(size_buffer(j->mctx, &j->it.source, rrhdr.size)); 1714135446Strhodes CHECK(journal_read(j, j->it.source.base, rrhdr.size)); 1715135446Strhodes isc_buffer_add(&j->it.source, rrhdr.size); 1716135446Strhodes 1717135446Strhodes /* 1718135446Strhodes * The target buffer is made the same size 1719135446Strhodes * as the source buffer, with the assumption that when 1720135446Strhodes * no compression in present, the output of dns_*_fromwire() 1721135446Strhodes * is no larger than the input. 1722135446Strhodes */ 1723135446Strhodes CHECK(size_buffer(j->mctx, &j->it.target, rrhdr.size)); 1724135446Strhodes 1725135446Strhodes /* 1726135446Strhodes * Parse the owner name. We don't know where it 1727135446Strhodes * ends yet, so we make the entire "remaining" 1728135446Strhodes * part of the buffer "active". 1729135446Strhodes */ 1730135446Strhodes isc_buffer_setactive(&j->it.source, 1731135446Strhodes j->it.source.used - j->it.source.current); 1732135446Strhodes CHECK(dns_name_fromwire(&j->it.name, &j->it.source, 1733135446Strhodes &j->it.dctx, 0, &j->it.target)); 1734135446Strhodes 1735135446Strhodes /* 1736135446Strhodes * Check that the RR header is there, and parse it. 1737135446Strhodes */ 1738135446Strhodes if (isc_buffer_remaininglength(&j->it.source) < 10) 1739135446Strhodes FAIL(DNS_R_FORMERR); 1740135446Strhodes 1741135446Strhodes rdtype = isc_buffer_getuint16(&j->it.source); 1742135446Strhodes rdclass = isc_buffer_getuint16(&j->it.source); 1743135446Strhodes ttl = isc_buffer_getuint32(&j->it.source); 1744135446Strhodes rdlen = isc_buffer_getuint16(&j->it.source); 1745135446Strhodes 1746135446Strhodes /* 1747135446Strhodes * Parse the rdata. 1748135446Strhodes */ 1749174187Sdougb if (isc_buffer_remaininglength(&j->it.source) != rdlen) 1750174187Sdougb FAIL(DNS_R_FORMERR); 1751135446Strhodes isc_buffer_setactive(&j->it.source, rdlen); 1752135446Strhodes dns_rdata_reset(&j->it.rdata); 1753135446Strhodes CHECK(dns_rdata_fromwire(&j->it.rdata, rdclass, 1754135446Strhodes rdtype, &j->it.source, &j->it.dctx, 1755135446Strhodes 0, &j->it.target)); 1756135446Strhodes j->it.ttl = ttl; 1757135446Strhodes 1758135446Strhodes j->it.xpos += sizeof(journal_rawrrhdr_t) + rrhdr.size; 1759135446Strhodes if (rdtype == dns_rdatatype_soa) { 1760135446Strhodes /* XXX could do additional consistency checks here */ 1761135446Strhodes j->it.current_serial = dns_soa_getserial(&j->it.rdata); 1762135446Strhodes } 1763135446Strhodes 1764135446Strhodes result = ISC_R_SUCCESS; 1765135446Strhodes 1766135446Strhodes failure: 1767135446Strhodes j->it.result = result; 1768135446Strhodes return (result); 1769135446Strhodes} 1770135446Strhodes 1771135446Strhodesisc_result_t 1772135446Strhodesdns_journal_next_rr(dns_journal_t *j) { 1773135446Strhodes j->it.result = read_one_rr(j); 1774135446Strhodes return (j->it.result); 1775135446Strhodes} 1776135446Strhodes 1777135446Strhodesvoid 1778135446Strhodesdns_journal_current_rr(dns_journal_t *j, dns_name_t **name, isc_uint32_t *ttl, 1779135446Strhodes dns_rdata_t **rdata) 1780135446Strhodes{ 1781135446Strhodes REQUIRE(j->it.result == ISC_R_SUCCESS); 1782135446Strhodes *name = &j->it.name; 1783135446Strhodes *ttl = j->it.ttl; 1784135446Strhodes *rdata = &j->it.rdata; 1785135446Strhodes} 1786135446Strhodes 1787135446Strhodes/**************************************************************************/ 1788135446Strhodes/* 1789135446Strhodes * Generating diffs from databases 1790135446Strhodes */ 1791135446Strhodes 1792135446Strhodes/* 1793135446Strhodes * Construct a diff containing all the RRs at the current name of the 1794135446Strhodes * database iterator 'dbit' in database 'db', version 'ver'. 1795135446Strhodes * Set '*name' to the current name, and append the diff to 'diff'. 1796135446Strhodes * All new tuples will have the operation 'op'. 1797135446Strhodes * 1798135446Strhodes * Requires: 'name' must have buffer large enough to hold the name. 1799135446Strhodes * Typically, a dns_fixedname_t would be used. 1800135446Strhodes */ 1801135446Strhodesstatic isc_result_t 1802135446Strhodesget_name_diff(dns_db_t *db, dns_dbversion_t *ver, isc_stdtime_t now, 1803135446Strhodes dns_dbiterator_t *dbit, dns_name_t *name, dns_diffop_t op, 1804135446Strhodes dns_diff_t *diff) 1805135446Strhodes{ 1806135446Strhodes isc_result_t result; 1807135446Strhodes dns_dbnode_t *node = NULL; 1808135446Strhodes dns_rdatasetiter_t *rdsiter = NULL; 1809135446Strhodes dns_difftuple_t *tuple = NULL; 1810135446Strhodes 1811135446Strhodes result = dns_dbiterator_current(dbit, &node, name); 1812135446Strhodes if (result != ISC_R_SUCCESS) 1813135446Strhodes return (result); 1814135446Strhodes 1815135446Strhodes result = dns_db_allrdatasets(db, node, ver, now, &rdsiter); 1816135446Strhodes if (result != ISC_R_SUCCESS) 1817135446Strhodes goto cleanup_node; 1818135446Strhodes 1819135446Strhodes for (result = dns_rdatasetiter_first(rdsiter); 1820135446Strhodes result == ISC_R_SUCCESS; 1821135446Strhodes result = dns_rdatasetiter_next(rdsiter)) 1822135446Strhodes { 1823135446Strhodes dns_rdataset_t rdataset; 1824135446Strhodes 1825135446Strhodes dns_rdataset_init(&rdataset); 1826135446Strhodes dns_rdatasetiter_current(rdsiter, &rdataset); 1827135446Strhodes 1828135446Strhodes for (result = dns_rdataset_first(&rdataset); 1829135446Strhodes result == ISC_R_SUCCESS; 1830135446Strhodes result = dns_rdataset_next(&rdataset)) 1831135446Strhodes { 1832135446Strhodes dns_rdata_t rdata = DNS_RDATA_INIT; 1833135446Strhodes dns_rdataset_current(&rdataset, &rdata); 1834135446Strhodes result = dns_difftuple_create(diff->mctx, op, name, 1835135446Strhodes rdataset.ttl, &rdata, 1836135446Strhodes &tuple); 1837135446Strhodes if (result != ISC_R_SUCCESS) { 1838135446Strhodes dns_rdataset_disassociate(&rdataset); 1839135446Strhodes goto cleanup_iterator; 1840135446Strhodes } 1841135446Strhodes dns_diff_append(diff, &tuple); 1842135446Strhodes } 1843135446Strhodes dns_rdataset_disassociate(&rdataset); 1844135446Strhodes if (result != ISC_R_NOMORE) 1845135446Strhodes goto cleanup_iterator; 1846135446Strhodes } 1847135446Strhodes if (result != ISC_R_NOMORE) 1848135446Strhodes goto cleanup_iterator; 1849135446Strhodes 1850135446Strhodes result = ISC_R_SUCCESS; 1851135446Strhodes 1852135446Strhodes cleanup_iterator: 1853135446Strhodes dns_rdatasetiter_destroy(&rdsiter); 1854135446Strhodes 1855135446Strhodes cleanup_node: 1856135446Strhodes dns_db_detachnode(db, &node); 1857135446Strhodes 1858135446Strhodes return (result); 1859135446Strhodes} 1860135446Strhodes 1861135446Strhodes/* 1862135446Strhodes * Comparison function for use by dns_diff_subtract when sorting 1863135446Strhodes * the diffs to be subtracted. The sort keys are the rdata type 1864135446Strhodes * and the rdata itself. The owner name is ignored, because 1865135446Strhodes * it is known to be the same for all tuples. 1866135446Strhodes */ 1867135446Strhodesstatic int 1868135446Strhodesrdata_order(const void *av, const void *bv) { 1869135446Strhodes dns_difftuple_t const * const *ap = av; 1870135446Strhodes dns_difftuple_t const * const *bp = bv; 1871135446Strhodes dns_difftuple_t const *a = *ap; 1872135446Strhodes dns_difftuple_t const *b = *bp; 1873135446Strhodes int r; 1874135446Strhodes r = (b->rdata.type - a->rdata.type); 1875135446Strhodes if (r != 0) 1876135446Strhodes return (r); 1877135446Strhodes r = dns_rdata_compare(&a->rdata, &b->rdata); 1878135446Strhodes return (r); 1879135446Strhodes} 1880135446Strhodes 1881135446Strhodesstatic isc_result_t 1882135446Strhodesdns_diff_subtract(dns_diff_t diff[2], dns_diff_t *r) { 1883135446Strhodes isc_result_t result; 1884135446Strhodes dns_difftuple_t *p[2]; 1885135446Strhodes int i, t; 1886153816Sdougb isc_boolean_t append; 1887153816Sdougb 1888135446Strhodes CHECK(dns_diff_sort(&diff[0], rdata_order)); 1889135446Strhodes CHECK(dns_diff_sort(&diff[1], rdata_order)); 1890135446Strhodes 1891135446Strhodes for (;;) { 1892135446Strhodes p[0] = ISC_LIST_HEAD(diff[0].tuples); 1893135446Strhodes p[1] = ISC_LIST_HEAD(diff[1].tuples); 1894135446Strhodes if (p[0] == NULL && p[1] == NULL) 1895135446Strhodes break; 1896135446Strhodes 1897135446Strhodes for (i = 0; i < 2; i++) 1898135446Strhodes if (p[!i] == NULL) { 1899135446Strhodes ISC_LIST_UNLINK(diff[i].tuples, p[i], link); 1900135446Strhodes ISC_LIST_APPEND(r->tuples, p[i], link); 1901135446Strhodes goto next; 1902135446Strhodes } 1903135446Strhodes t = rdata_order(&p[0], &p[1]); 1904135446Strhodes if (t < 0) { 1905135446Strhodes ISC_LIST_UNLINK(diff[0].tuples, p[0], link); 1906135446Strhodes ISC_LIST_APPEND(r->tuples, p[0], link); 1907135446Strhodes goto next; 1908135446Strhodes } 1909135446Strhodes if (t > 0) { 1910135446Strhodes ISC_LIST_UNLINK(diff[1].tuples, p[1], link); 1911135446Strhodes ISC_LIST_APPEND(r->tuples, p[1], link); 1912135446Strhodes goto next; 1913135446Strhodes } 1914135446Strhodes INSIST(t == 0); 1915135446Strhodes /* 1916153816Sdougb * Identical RRs in both databases; skip them both 1917153816Sdougb * if the ttl differs. 1918135446Strhodes */ 1919153816Sdougb append = ISC_TF(p[0]->ttl != p[1]->ttl); 1920135446Strhodes for (i = 0; i < 2; i++) { 1921135446Strhodes ISC_LIST_UNLINK(diff[i].tuples, p[i], link); 1922153816Sdougb if (append) { 1923153816Sdougb ISC_LIST_APPEND(r->tuples, p[i], link); 1924153816Sdougb } else { 1925153816Sdougb dns_difftuple_free(&p[i]); 1926153816Sdougb } 1927135446Strhodes } 1928135446Strhodes next: ; 1929135446Strhodes } 1930135446Strhodes result = ISC_R_SUCCESS; 1931135446Strhodes failure: 1932135446Strhodes return (result); 1933135446Strhodes} 1934135446Strhodes 1935204619Sdougbstatic isc_result_t 1936254897Serwindiff_namespace(dns_db_t *dba, dns_dbversion_t *dbvera, 1937204619Sdougb dns_db_t *dbb, dns_dbversion_t *dbverb, 1938204619Sdougb unsigned int options, dns_diff_t *resultdiff) 1939135446Strhodes{ 1940135446Strhodes dns_db_t *db[2]; 1941135446Strhodes dns_dbversion_t *ver[2]; 1942135446Strhodes dns_dbiterator_t *dbit[2] = { NULL, NULL }; 1943135446Strhodes isc_boolean_t have[2] = { ISC_FALSE, ISC_FALSE }; 1944135446Strhodes dns_fixedname_t fixname[2]; 1945135446Strhodes isc_result_t result, itresult[2]; 1946204619Sdougb dns_diff_t diff[2]; 1947135446Strhodes int i, t; 1948135446Strhodes 1949135446Strhodes db[0] = dba, db[1] = dbb; 1950135446Strhodes ver[0] = dbvera, ver[1] = dbverb; 1951135446Strhodes 1952254897Serwin dns_diff_init(resultdiff->mctx, &diff[0]); 1953254897Serwin dns_diff_init(resultdiff->mctx, &diff[1]); 1954135446Strhodes 1955135446Strhodes dns_fixedname_init(&fixname[0]); 1956135446Strhodes dns_fixedname_init(&fixname[1]); 1957135446Strhodes 1958204619Sdougb result = dns_db_createiterator(db[0], options, &dbit[0]); 1959135446Strhodes if (result != ISC_R_SUCCESS) 1960135446Strhodes return (result); 1961204619Sdougb result = dns_db_createiterator(db[1], options, &dbit[1]); 1962135446Strhodes if (result != ISC_R_SUCCESS) 1963204619Sdougb goto cleanup_iterator; 1964135446Strhodes 1965135446Strhodes itresult[0] = dns_dbiterator_first(dbit[0]); 1966135446Strhodes itresult[1] = dns_dbiterator_first(dbit[1]); 1967135446Strhodes 1968135446Strhodes for (;;) { 1969135446Strhodes for (i = 0; i < 2; i++) { 1970135446Strhodes if (! have[i] && itresult[i] == ISC_R_SUCCESS) { 1971135446Strhodes CHECK(get_name_diff(db[i], ver[i], 0, dbit[i], 1972135446Strhodes dns_fixedname_name(&fixname[i]), 1973135446Strhodes i == 0 ? 1974135446Strhodes DNS_DIFFOP_ADD : 1975135446Strhodes DNS_DIFFOP_DEL, 1976135446Strhodes &diff[i])); 1977135446Strhodes itresult[i] = dns_dbiterator_next(dbit[i]); 1978135446Strhodes have[i] = ISC_TRUE; 1979135446Strhodes } 1980135446Strhodes } 1981135446Strhodes 1982135446Strhodes if (! have[0] && ! have[1]) { 1983135446Strhodes INSIST(ISC_LIST_EMPTY(diff[0].tuples)); 1984135446Strhodes INSIST(ISC_LIST_EMPTY(diff[1].tuples)); 1985135446Strhodes break; 1986135446Strhodes } 1987135446Strhodes 1988135446Strhodes for (i = 0; i < 2; i++) { 1989135446Strhodes if (! have[!i]) { 1990204619Sdougb ISC_LIST_APPENDLIST(resultdiff->tuples, 1991135446Strhodes diff[i].tuples, link); 1992135446Strhodes INSIST(ISC_LIST_EMPTY(diff[i].tuples)); 1993135446Strhodes have[i] = ISC_FALSE; 1994135446Strhodes goto next; 1995135446Strhodes } 1996135446Strhodes } 1997135446Strhodes 1998135446Strhodes t = dns_name_compare(dns_fixedname_name(&fixname[0]), 1999135446Strhodes dns_fixedname_name(&fixname[1])); 2000135446Strhodes if (t < 0) { 2001204619Sdougb ISC_LIST_APPENDLIST(resultdiff->tuples, 2002135446Strhodes diff[0].tuples, link); 2003135446Strhodes INSIST(ISC_LIST_EMPTY(diff[0].tuples)); 2004135446Strhodes have[0] = ISC_FALSE; 2005135446Strhodes continue; 2006135446Strhodes } 2007135446Strhodes if (t > 0) { 2008204619Sdougb ISC_LIST_APPENDLIST(resultdiff->tuples, 2009135446Strhodes diff[1].tuples, link); 2010135446Strhodes INSIST(ISC_LIST_EMPTY(diff[1].tuples)); 2011135446Strhodes have[1] = ISC_FALSE; 2012135446Strhodes continue; 2013135446Strhodes } 2014135446Strhodes INSIST(t == 0); 2015204619Sdougb CHECK(dns_diff_subtract(diff, resultdiff)); 2016135446Strhodes INSIST(ISC_LIST_EMPTY(diff[0].tuples)); 2017135446Strhodes INSIST(ISC_LIST_EMPTY(diff[1].tuples)); 2018135446Strhodes have[0] = have[1] = ISC_FALSE; 2019135446Strhodes next: ; 2020135446Strhodes } 2021135446Strhodes if (itresult[0] != ISC_R_NOMORE) 2022135446Strhodes FAIL(itresult[0]); 2023135446Strhodes if (itresult[1] != ISC_R_NOMORE) 2024135446Strhodes FAIL(itresult[1]); 2025135446Strhodes 2026204619Sdougb INSIST(ISC_LIST_EMPTY(diff[0].tuples)); 2027204619Sdougb INSIST(ISC_LIST_EMPTY(diff[1].tuples)); 2028204619Sdougb 2029204619Sdougb failure: 2030204619Sdougb dns_dbiterator_destroy(&dbit[1]); 2031254897Serwin 2032204619Sdougb cleanup_iterator: 2033204619Sdougb dns_dbiterator_destroy(&dbit[0]); 2034254897Serwin dns_diff_clear(&diff[0]); 2035254897Serwin dns_diff_clear(&diff[1]); 2036204619Sdougb return (result); 2037204619Sdougb} 2038204619Sdougb 2039204619Sdougb/* 2040204619Sdougb * Compare the databases 'dba' and 'dbb' and generate a journal 2041204619Sdougb * entry containing the changes to make 'dba' from 'dbb' (note 2042204619Sdougb * the order). This journal entry will consist of a single, 2043204619Sdougb * possibly very large transaction. 2044204619Sdougb */ 2045204619Sdougbisc_result_t 2046254897Serwindns_db_diff(isc_mem_t *mctx, dns_db_t *dba, dns_dbversion_t *dbvera, 2047254897Serwin dns_db_t *dbb, dns_dbversion_t *dbverb, const char *filename) 2048204619Sdougb{ 2049204619Sdougb isc_result_t result; 2050254897Serwin dns_diff_t diff; 2051254897Serwin 2052254897Serwin dns_diff_init(mctx, &diff); 2053254897Serwin 2054254897Serwin result = dns_db_diffx(&diff, dba, dbvera, dbb, dbverb, filename); 2055254897Serwin 2056254897Serwin dns_diff_clear(&diff); 2057254897Serwin 2058254897Serwin return (result); 2059254897Serwin} 2060254897Serwin 2061254897Serwinisc_result_t 2062254897Serwindns_db_diffx(dns_diff_t *diff, dns_db_t *dba, dns_dbversion_t *dbvera, 2063254897Serwin dns_db_t *dbb, dns_dbversion_t *dbverb, const char *filename) 2064254897Serwin{ 2065254897Serwin isc_result_t result; 2066204619Sdougb dns_journal_t *journal = NULL; 2067204619Sdougb 2068254897Serwin if (filename != NULL) { 2069254897Serwin result = dns_journal_open(diff->mctx, filename, 2070254897Serwin DNS_JOURNAL_CREATE, &journal); 2071254897Serwin if (result != ISC_R_SUCCESS) 2072254897Serwin return (result); 2073254897Serwin } 2074204619Sdougb 2075254897Serwin CHECK(diff_namespace(dba, dbvera, dbb, dbverb, DNS_DB_NONSEC3, diff)); 2076254897Serwin CHECK(diff_namespace(dba, dbvera, dbb, dbverb, DNS_DB_NSEC3ONLY, diff)); 2077204619Sdougb 2078254897Serwin if (journal != NULL) { 2079254897Serwin if (ISC_LIST_EMPTY(diff->tuples)) 2080254897Serwin isc_log_write(JOURNAL_DEBUG_LOGARGS(3), "no changes"); 2081254897Serwin else 2082254897Serwin CHECK(dns_journal_write_transaction(journal, diff)); 2083135446Strhodes } 2084254897Serwin 2085135446Strhodes failure: 2086254897Serwin if (journal != NULL) 2087254897Serwin dns_journal_destroy(&journal); 2088135446Strhodes return (result); 2089135446Strhodes} 2090135446Strhodes 2091135446Strhodesisc_result_t 2092135446Strhodesdns_journal_compact(isc_mem_t *mctx, char *filename, isc_uint32_t serial, 2093135446Strhodes isc_uint32_t target_size) 2094135446Strhodes{ 2095135446Strhodes unsigned int i; 2096135446Strhodes journal_pos_t best_guess; 2097135446Strhodes journal_pos_t current_pos; 2098135446Strhodes dns_journal_t *j = NULL; 2099174187Sdougb dns_journal_t *new = NULL; 2100135446Strhodes journal_rawheader_t rawheader; 2101135446Strhodes unsigned int copy_length; 2102262706Serwin size_t namelen; 2103135446Strhodes char *buf = NULL; 2104135446Strhodes unsigned int size = 0; 2105135446Strhodes isc_result_t result; 2106135446Strhodes unsigned int indexend; 2107174187Sdougb char newname[1024]; 2108174187Sdougb char backup[1024]; 2109174187Sdougb isc_boolean_t is_backup = ISC_FALSE; 2110135446Strhodes 2111174187Sdougb namelen = strlen(filename); 2112262706Serwin if (namelen > 4U && strcmp(filename + namelen - 4, ".jnl") == 0) 2113174187Sdougb namelen -= 4; 2114135446Strhodes 2115174187Sdougb result = isc_string_printf(newname, sizeof(newname), "%.*s.jnw", 2116262706Serwin (int)namelen, filename); 2117174187Sdougb if (result != ISC_R_SUCCESS) 2118174187Sdougb return (result); 2119174187Sdougb 2120174187Sdougb result = isc_string_printf(backup, sizeof(backup), "%.*s.jbk", 2121262706Serwin (int)namelen, filename); 2122174187Sdougb if (result != ISC_R_SUCCESS) 2123174187Sdougb return (result); 2124174187Sdougb 2125174187Sdougb result = journal_open(mctx, filename, ISC_FALSE, ISC_FALSE, &j); 2126174187Sdougb if (result == ISC_R_NOTFOUND) { 2127174187Sdougb is_backup = ISC_TRUE; 2128174187Sdougb result = journal_open(mctx, backup, ISC_FALSE, ISC_FALSE, &j); 2129174187Sdougb } 2130174187Sdougb if (result != ISC_R_SUCCESS) 2131174187Sdougb return (result); 2132174187Sdougb 2133135446Strhodes if (JOURNAL_EMPTY(&j->header)) { 2134135446Strhodes dns_journal_destroy(&j); 2135135446Strhodes return (ISC_R_SUCCESS); 2136135446Strhodes } 2137186462Sdougb 2138135446Strhodes if (DNS_SERIAL_GT(j->header.begin.serial, serial) || 2139135446Strhodes DNS_SERIAL_GT(serial, j->header.end.serial)) { 2140135446Strhodes dns_journal_destroy(&j); 2141135446Strhodes return (ISC_R_RANGE); 2142135446Strhodes } 2143135446Strhodes 2144135446Strhodes /* 2145135446Strhodes * Cope with very small target sizes. 2146135446Strhodes */ 2147135446Strhodes indexend = sizeof(journal_rawheader_t) + 2148135446Strhodes j->header.index_size * sizeof(journal_rawpos_t); 2149135446Strhodes if (target_size < indexend * 2) 2150135446Strhodes target_size = target_size/2 + indexend; 2151135446Strhodes 2152135446Strhodes /* 2153135446Strhodes * See if there is any work to do. 2154135446Strhodes */ 2155135446Strhodes if ((isc_uint32_t) j->header.end.offset < target_size) { 2156135446Strhodes dns_journal_destroy(&j); 2157135446Strhodes return (ISC_R_SUCCESS); 2158135446Strhodes } 2159174187Sdougb 2160174187Sdougb CHECK(journal_open(mctx, newname, ISC_TRUE, ISC_TRUE, &new)); 2161186462Sdougb 2162135446Strhodes /* 2163135446Strhodes * Remove overhead so space test below can succeed. 2164135446Strhodes */ 2165135446Strhodes if (target_size >= indexend) 2166135446Strhodes target_size -= indexend; 2167135446Strhodes 2168135446Strhodes /* 2169135446Strhodes * Find if we can create enough free space. 2170135446Strhodes */ 2171135446Strhodes best_guess = j->header.begin; 2172135446Strhodes for (i = 0; i < j->header.index_size; i++) { 2173135446Strhodes if (POS_VALID(j->index[i]) && 2174135446Strhodes DNS_SERIAL_GE(serial, j->index[i].serial) && 2175135446Strhodes ((isc_uint32_t)(j->header.end.offset - j->index[i].offset) 2176135446Strhodes >= target_size / 2) && 2177135446Strhodes j->index[i].offset > best_guess.offset) 2178135446Strhodes best_guess = j->index[i]; 2179135446Strhodes } 2180135446Strhodes 2181135446Strhodes current_pos = best_guess; 2182135446Strhodes while (current_pos.serial != serial) { 2183135446Strhodes CHECK(journal_next(j, ¤t_pos)); 2184135446Strhodes if (current_pos.serial == j->header.end.serial) 2185135446Strhodes break; 2186135446Strhodes 2187135446Strhodes if (DNS_SERIAL_GE(serial, current_pos.serial) && 2188135446Strhodes ((isc_uint32_t)(j->header.end.offset - current_pos.offset) 2189135446Strhodes >= (target_size / 2)) && 2190135446Strhodes current_pos.offset > best_guess.offset) 2191135446Strhodes best_guess = current_pos; 2192135446Strhodes else 2193135446Strhodes break; 2194135446Strhodes } 2195135446Strhodes 2196135446Strhodes INSIST(best_guess.serial != j->header.end.serial); 2197135446Strhodes if (best_guess.serial != serial) 2198135446Strhodes CHECK(journal_next(j, &best_guess)); 2199135446Strhodes 2200135446Strhodes /* 2201174187Sdougb * We should now be roughly half target_size provided 2202174187Sdougb * we did not reach 'serial'. If not we will just copy 2203174187Sdougb * all uncommitted deltas regardless of the size. 2204135446Strhodes */ 2205135446Strhodes copy_length = j->header.end.offset - best_guess.offset; 2206135446Strhodes 2207135446Strhodes if (copy_length != 0) { 2208135446Strhodes /* 2209135446Strhodes * Copy best_guess to end into space just freed. 2210135446Strhodes */ 2211135446Strhodes size = 64*1024; 2212135446Strhodes if (copy_length < size) 2213135446Strhodes size = copy_length; 2214135446Strhodes buf = isc_mem_get(mctx, size); 2215135446Strhodes if (buf == NULL) { 2216135446Strhodes result = ISC_R_NOMEMORY; 2217135446Strhodes goto failure; 2218135446Strhodes } 2219186462Sdougb 2220174187Sdougb CHECK(journal_seek(j, best_guess.offset)); 2221174187Sdougb CHECK(journal_seek(new, indexend)); 2222135446Strhodes for (i = 0; i < copy_length; i += size) { 2223174187Sdougb unsigned int len = (copy_length - i) > size ? size : 2224135446Strhodes (copy_length - i); 2225135446Strhodes CHECK(journal_read(j, buf, len)); 2226174187Sdougb CHECK(journal_write(new, buf, len)); 2227135446Strhodes } 2228135446Strhodes 2229174187Sdougb CHECK(journal_fsync(new)); 2230135446Strhodes 2231135446Strhodes /* 2232135446Strhodes * Compute new header. 2233135446Strhodes */ 2234174187Sdougb new->header.begin.serial = best_guess.serial; 2235174187Sdougb new->header.begin.offset = indexend; 2236174187Sdougb new->header.end.serial = j->header.end.serial; 2237174187Sdougb new->header.end.offset = indexend + copy_length; 2238254897Serwin new->header.sourceserial = j->header.sourceserial; 2239254897Serwin new->header.serialset = j->header.serialset; 2240174187Sdougb 2241135446Strhodes /* 2242135446Strhodes * Update the journal header. 2243135446Strhodes */ 2244174187Sdougb journal_header_encode(&new->header, &rawheader); 2245174187Sdougb CHECK(journal_seek(new, 0)); 2246174187Sdougb CHECK(journal_write(new, &rawheader, sizeof(rawheader))); 2247174187Sdougb CHECK(journal_fsync(new)); 2248135446Strhodes 2249135446Strhodes /* 2250135446Strhodes * Build new index. 2251135446Strhodes */ 2252174187Sdougb current_pos = new->header.begin; 2253174187Sdougb while (current_pos.serial != new->header.end.serial) { 2254174187Sdougb index_add(new, ¤t_pos); 2255174187Sdougb CHECK(journal_next(new, ¤t_pos)); 2256135446Strhodes } 2257135446Strhodes 2258135446Strhodes /* 2259135446Strhodes * Write index. 2260135446Strhodes */ 2261174187Sdougb CHECK(index_to_disk(new)); 2262174187Sdougb CHECK(journal_fsync(new)); 2263135446Strhodes 2264174187Sdougb indexend = new->header.end.offset; 2265225361Sdougb POST(indexend); 2266135446Strhodes } 2267216175Sdougb 2268216175Sdougb /* 2269216175Sdougb * Close both journals before trying to rename files (this is 2270216175Sdougb * necessary on WIN32). 2271216175Sdougb */ 2272216175Sdougb dns_journal_destroy(&j); 2273174187Sdougb dns_journal_destroy(&new); 2274174187Sdougb 2275174187Sdougb /* 2276174187Sdougb * With a UFS file system this should just succeed and be atomic. 2277174187Sdougb * Any IXFR outs will just continue and the old journal will be 2278174187Sdougb * removed on final close. 2279174187Sdougb * 2280216175Sdougb * With MSDOS / NTFS we need to do a two stage rename, triggered 2281216175Sdougb * by EEXIST. (If any IXFR's are running in other threads, however, 2282216175Sdougb * this will fail, and the journal will not be compacted. But 2283216175Sdougb * if so, hopefully they'll be finished by the next time we 2284216175Sdougb * compact.) 2285174187Sdougb */ 2286174187Sdougb if (rename(newname, filename) == -1) { 2287216175Sdougb if (errno == EEXIST && !is_backup) { 2288174187Sdougb result = isc_file_remove(backup); 2289174187Sdougb if (result != ISC_R_SUCCESS && 2290174187Sdougb result != ISC_R_FILENOTFOUND) 2291174187Sdougb goto failure; 2292174187Sdougb if (rename(filename, backup) == -1) 2293174187Sdougb goto maperrno; 2294174187Sdougb if (rename(newname, filename) == -1) 2295174187Sdougb goto maperrno; 2296174187Sdougb (void)isc_file_remove(backup); 2297174187Sdougb } else { 2298174187Sdougb maperrno: 2299174187Sdougb result = ISC_R_FAILURE; 2300174187Sdougb goto failure; 2301174187Sdougb } 2302174187Sdougb } 2303186462Sdougb 2304135446Strhodes result = ISC_R_SUCCESS; 2305135446Strhodes 2306135446Strhodes failure: 2307174187Sdougb (void)isc_file_remove(newname); 2308135446Strhodes if (buf != NULL) 2309135446Strhodes isc_mem_put(mctx, buf, size); 2310135446Strhodes if (j != NULL) 2311135446Strhodes dns_journal_destroy(&j); 2312174187Sdougb if (new != NULL) 2313174187Sdougb dns_journal_destroy(&new); 2314135446Strhodes return (result); 2315135446Strhodes} 2316135446Strhodes 2317135446Strhodesstatic isc_result_t 2318135446Strhodesindex_to_disk(dns_journal_t *j) { 2319135446Strhodes isc_result_t result = ISC_R_SUCCESS; 2320135446Strhodes 2321135446Strhodes if (j->header.index_size != 0) { 2322135446Strhodes unsigned int i; 2323135446Strhodes unsigned char *p; 2324135446Strhodes unsigned int rawbytes; 2325135446Strhodes 2326135446Strhodes rawbytes = j->header.index_size * sizeof(journal_rawpos_t); 2327135446Strhodes 2328135446Strhodes p = j->rawindex; 2329135446Strhodes for (i = 0; i < j->header.index_size; i++) { 2330135446Strhodes encode_uint32(j->index[i].serial, p); 2331135446Strhodes p += 4; 2332135446Strhodes encode_uint32(j->index[i].offset, p); 2333135446Strhodes p += 4; 2334135446Strhodes } 2335135446Strhodes INSIST(p == j->rawindex + rawbytes); 2336135446Strhodes 2337135446Strhodes CHECK(journal_seek(j, sizeof(journal_rawheader_t))); 2338135446Strhodes CHECK(journal_write(j, j->rawindex, rawbytes)); 2339135446Strhodes } 2340135446Strhodesfailure: 2341135446Strhodes return (result); 2342135446Strhodes} 2343