journal.c revision 204619
1135446Strhodes/* 2193149Sdougb * Copyright (C) 2004, 2005, 2007-2009 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 18204619Sdougb/* $Id: journal.c,v 1.103.48.6 2009/11/04 23:47:25 tbox 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 114135446Strhodesstatic isc_result_t index_to_disk(dns_journal_t *); 115135446Strhodes 116135446Strhodesstatic inline isc_uint32_t 117135446Strhodesdecode_uint32(unsigned char *p) { 118135446Strhodes return ((p[0] << 24) + 119135446Strhodes (p[1] << 16) + 120135446Strhodes (p[2] << 8) + 121135446Strhodes (p[3] << 0)); 122135446Strhodes} 123135446Strhodes 124135446Strhodesstatic inline void 125135446Strhodesencode_uint32(isc_uint32_t val, unsigned char *p) { 126135446Strhodes p[0] = (isc_uint8_t)(val >> 24); 127135446Strhodes p[1] = (isc_uint8_t)(val >> 16); 128135446Strhodes p[2] = (isc_uint8_t)(val >> 8); 129135446Strhodes p[3] = (isc_uint8_t)(val >> 0); 130135446Strhodes} 131135446Strhodes 132135446Strhodesisc_result_t 133135446Strhodesdns_db_createsoatuple(dns_db_t *db, dns_dbversion_t *ver, isc_mem_t *mctx, 134135446Strhodes dns_diffop_t op, dns_difftuple_t **tp) 135135446Strhodes{ 136135446Strhodes isc_result_t result; 137135446Strhodes dns_dbnode_t *node; 138135446Strhodes dns_rdataset_t rdataset; 139135446Strhodes dns_rdata_t rdata = DNS_RDATA_INIT; 140135446Strhodes dns_name_t *zonename; 141135446Strhodes 142135446Strhodes zonename = dns_db_origin(db); 143135446Strhodes 144135446Strhodes node = NULL; 145135446Strhodes result = dns_db_findnode(db, zonename, ISC_FALSE, &node); 146135446Strhodes if (result != ISC_R_SUCCESS) 147135446Strhodes goto nonode; 148135446Strhodes 149135446Strhodes dns_rdataset_init(&rdataset); 150135446Strhodes result = dns_db_findrdataset(db, node, ver, dns_rdatatype_soa, 0, 151135446Strhodes (isc_stdtime_t)0, &rdataset, NULL); 152186462Sdougb if (result != ISC_R_SUCCESS) 153135446Strhodes goto freenode; 154135446Strhodes 155135446Strhodes result = dns_rdataset_first(&rdataset); 156186462Sdougb if (result != ISC_R_SUCCESS) 157135446Strhodes goto freenode; 158135446Strhodes 159135446Strhodes dns_rdataset_current(&rdataset, &rdata); 160135446Strhodes 161135446Strhodes result = dns_difftuple_create(mctx, op, zonename, rdataset.ttl, 162135446Strhodes &rdata, tp); 163135446Strhodes 164135446Strhodes dns_rdataset_disassociate(&rdataset); 165135446Strhodes dns_db_detachnode(db, &node); 166135446Strhodes return (ISC_R_SUCCESS); 167135446Strhodes 168135446Strhodes freenode: 169135446Strhodes dns_db_detachnode(db, &node); 170135446Strhodes nonode: 171135446Strhodes UNEXPECTED_ERROR(__FILE__, __LINE__, "missing SOA"); 172135446Strhodes return (result); 173135446Strhodes} 174135446Strhodes 175193149Sdougb/* Journaling */ 176135446Strhodes 177170222Sdougb/*% 178135446Strhodes * On-disk representation of a "pointer" to a journal entry. 179135446Strhodes * These are used in the journal header to locate the beginning 180135446Strhodes * and end of the journal, and in the journal index to locate 181135446Strhodes * other transactions. 182135446Strhodes */ 183135446Strhodestypedef struct { 184170222Sdougb unsigned char serial[4]; /*%< SOA serial before update. */ 185135446Strhodes /* 186135446Strhodes * XXXRTH Should offset be 8 bytes? 187135446Strhodes * XXXDCL ... probably, since isc_offset_t is 8 bytes on many OSs. 188135446Strhodes * XXXAG ... but we will not be able to seek >2G anyway on many 189135446Strhodes * platforms as long as we are using fseek() rather 190135446Strhodes * than lseek(). 191135446Strhodes */ 192170222Sdougb unsigned char offset[4]; /*%< Offset from beginning of file. */ 193135446Strhodes} journal_rawpos_t; 194135446Strhodes 195135446Strhodes 196170222Sdougb/*% 197135446Strhodes * The header is of a fixed size, with some spare room for future 198135446Strhodes * extensions. 199135446Strhodes */ 200135446Strhodes#define JOURNAL_HEADER_SIZE 64 /* Bytes. */ 201135446Strhodes 202170222Sdougb/*% 203170222Sdougb * The on-disk representation of the journal header. 204170222Sdougb * All numbers are stored in big-endian order. 205170222Sdougb */ 206135446Strhodestypedef union { 207135446Strhodes struct { 208170222Sdougb /*% File format version ID. */ 209135446Strhodes unsigned char format[16]; 210170222Sdougb /*% Position of the first addressable transaction */ 211135446Strhodes journal_rawpos_t begin; 212170222Sdougb /*% Position of the next (yet nonexistent) transaction. */ 213135446Strhodes journal_rawpos_t end; 214170222Sdougb /*% Number of index entries following the header. */ 215135446Strhodes unsigned char index_size[4]; 216135446Strhodes } h; 217135446Strhodes /* Pad the header to a fixed size. */ 218135446Strhodes unsigned char pad[JOURNAL_HEADER_SIZE]; 219135446Strhodes} journal_rawheader_t; 220135446Strhodes 221170222Sdougb/*% 222135446Strhodes * The on-disk representation of the transaction header. 223135446Strhodes * There is one of these at the beginning of each transaction. 224135446Strhodes */ 225135446Strhodestypedef struct { 226170222Sdougb unsigned char size[4]; /*%< In bytes, excluding header. */ 227170222Sdougb unsigned char serial0[4]; /*%< SOA serial before update. */ 228170222Sdougb unsigned char serial1[4]; /*%< SOA serial after update. */ 229135446Strhodes} journal_rawxhdr_t; 230135446Strhodes 231170222Sdougb/*% 232135446Strhodes * The on-disk representation of the RR header. 233135446Strhodes * There is one of these at the beginning of each RR. 234135446Strhodes */ 235135446Strhodestypedef struct { 236170222Sdougb unsigned char size[4]; /*%< In bytes, excluding header. */ 237135446Strhodes} journal_rawrrhdr_t; 238135446Strhodes 239170222Sdougb/*% 240135446Strhodes * The in-core representation of the journal header. 241135446Strhodes */ 242135446Strhodestypedef struct { 243135446Strhodes isc_uint32_t serial; 244135446Strhodes isc_offset_t offset; 245135446Strhodes} journal_pos_t; 246135446Strhodes 247135446Strhodes#define POS_VALID(pos) ((pos).offset != 0) 248135446Strhodes#define POS_INVALIDATE(pos) ((pos).offset = 0, (pos).serial = 0) 249135446Strhodes 250135446Strhodestypedef struct { 251135446Strhodes unsigned char format[16]; 252135446Strhodes journal_pos_t begin; 253135446Strhodes journal_pos_t end; 254135446Strhodes isc_uint32_t index_size; 255135446Strhodes} journal_header_t; 256135446Strhodes 257170222Sdougb/*% 258135446Strhodes * The in-core representation of the transaction header. 259135446Strhodes */ 260135446Strhodes 261135446Strhodestypedef struct { 262135446Strhodes isc_uint32_t size; 263135446Strhodes isc_uint32_t serial0; 264135446Strhodes isc_uint32_t serial1; 265135446Strhodes} journal_xhdr_t; 266135446Strhodes 267170222Sdougb/*% 268135446Strhodes * The in-core representation of the RR header. 269135446Strhodes */ 270135446Strhodestypedef struct { 271135446Strhodes isc_uint32_t size; 272135446Strhodes} journal_rrhdr_t; 273135446Strhodes 274135446Strhodes 275170222Sdougb/*% 276135446Strhodes * Initial contents to store in the header of a newly created 277135446Strhodes * journal file. 278135446Strhodes * 279135446Strhodes * The header starts with the magic string ";BIND LOG V9\n" 280135446Strhodes * to identify the file as a BIND 9 journal file. An ASCII 281135446Strhodes * identification string is used rather than a binary magic 282135446Strhodes * number to be consistent with BIND 8 (BIND 8 journal files 283135446Strhodes * are ASCII text files). 284135446Strhodes */ 285135446Strhodes 286135446Strhodesstatic journal_header_t 287135446Strhodesinitial_journal_header = { ";BIND LOG V9\n", { 0, 0 }, { 0, 0 }, 0 }; 288135446Strhodes 289135446Strhodes#define JOURNAL_EMPTY(h) ((h)->begin.offset == (h)->end.offset) 290135446Strhodes 291135446Strhodestypedef enum { 292135446Strhodes JOURNAL_STATE_INVALID, 293135446Strhodes JOURNAL_STATE_READ, 294135446Strhodes JOURNAL_STATE_WRITE, 295135446Strhodes JOURNAL_STATE_TRANSACTION 296135446Strhodes} journal_state_t; 297135446Strhodes 298135446Strhodesstruct dns_journal { 299170222Sdougb unsigned int magic; /*%< JOUR */ 300170222Sdougb isc_mem_t *mctx; /*%< Memory context */ 301135446Strhodes journal_state_t state; 302170222Sdougb const char *filename; /*%< Journal file name */ 303170222Sdougb FILE * fp; /*%< File handle */ 304170222Sdougb isc_offset_t offset; /*%< Current file offset */ 305170222Sdougb journal_header_t header; /*%< In-core journal header */ 306170222Sdougb unsigned char *rawindex; /*%< In-core buffer for journal index in on-disk format */ 307170222Sdougb journal_pos_t *index; /*%< In-core journal index */ 308135446Strhodes 309170222Sdougb /*% Current transaction state (when writing). */ 310135446Strhodes struct { 311170222Sdougb unsigned int n_soa; /*%< Number of SOAs seen */ 312170222Sdougb journal_pos_t pos[2]; /*%< Begin/end position */ 313135446Strhodes } x; 314135446Strhodes 315170222Sdougb /*% Iteration state (when reading). */ 316135446Strhodes struct { 317135446Strhodes /* These define the part of the journal we iterate over. */ 318170222Sdougb journal_pos_t bpos; /*%< Position before first, */ 319170222Sdougb journal_pos_t epos; /*%< and after last transaction */ 320135446Strhodes /* The rest is iterator state. */ 321170222Sdougb isc_uint32_t current_serial; /*%< Current SOA serial */ 322170222Sdougb isc_buffer_t source; /*%< Data from disk */ 323170222Sdougb isc_buffer_t target; /*%< Data from _fromwire check */ 324170222Sdougb dns_decompress_t dctx; /*%< Dummy decompression ctx */ 325170222Sdougb dns_name_t name; /*%< Current domain name */ 326170222Sdougb dns_rdata_t rdata; /*%< Current rdata */ 327170222Sdougb isc_uint32_t ttl; /*%< Current TTL */ 328170222Sdougb unsigned int xsize; /*%< Size of transaction data */ 329170222Sdougb unsigned int xpos; /*%< Current position in it */ 330170222Sdougb isc_result_t result; /*%< Result of last call */ 331135446Strhodes } it; 332135446Strhodes}; 333135446Strhodes 334135446Strhodes#define DNS_JOURNAL_MAGIC ISC_MAGIC('J', 'O', 'U', 'R') 335135446Strhodes#define DNS_JOURNAL_VALID(t) ISC_MAGIC_VALID(t, DNS_JOURNAL_MAGIC) 336135446Strhodes 337135446Strhodesstatic void 338135446Strhodesjournal_pos_decode(journal_rawpos_t *raw, journal_pos_t *cooked) { 339135446Strhodes cooked->serial = decode_uint32(raw->serial); 340135446Strhodes cooked->offset = decode_uint32(raw->offset); 341135446Strhodes} 342135446Strhodes 343135446Strhodesstatic void 344135446Strhodesjournal_pos_encode(journal_rawpos_t *raw, journal_pos_t *cooked) { 345135446Strhodes encode_uint32(cooked->serial, raw->serial); 346135446Strhodes encode_uint32(cooked->offset, raw->offset); 347135446Strhodes} 348135446Strhodes 349135446Strhodesstatic void 350135446Strhodesjournal_header_decode(journal_rawheader_t *raw, journal_header_t *cooked) { 351135446Strhodes INSIST(sizeof(cooked->format) == sizeof(raw->h.format)); 352135446Strhodes memcpy(cooked->format, raw->h.format, sizeof(cooked->format)); 353135446Strhodes journal_pos_decode(&raw->h.begin, &cooked->begin); 354135446Strhodes journal_pos_decode(&raw->h.end, &cooked->end); 355135446Strhodes cooked->index_size = decode_uint32(raw->h.index_size); 356135446Strhodes} 357135446Strhodes 358135446Strhodesstatic void 359135446Strhodesjournal_header_encode(journal_header_t *cooked, journal_rawheader_t *raw) { 360135446Strhodes INSIST(sizeof(cooked->format) == sizeof(raw->h.format)); 361135446Strhodes memset(raw->pad, 0, sizeof(raw->pad)); 362135446Strhodes memcpy(raw->h.format, cooked->format, sizeof(raw->h.format)); 363135446Strhodes journal_pos_encode(&raw->h.begin, &cooked->begin); 364135446Strhodes journal_pos_encode(&raw->h.end, &cooked->end); 365135446Strhodes encode_uint32(cooked->index_size, raw->h.index_size); 366135446Strhodes} 367135446Strhodes 368135446Strhodes/* 369135446Strhodes * Journal file I/O subroutines, with error checking and reporting. 370135446Strhodes */ 371135446Strhodesstatic isc_result_t 372135446Strhodesjournal_seek(dns_journal_t *j, isc_uint32_t offset) { 373135446Strhodes isc_result_t result; 374135446Strhodes result = isc_stdio_seek(j->fp, (long)offset, SEEK_SET); 375135446Strhodes if (result != ISC_R_SUCCESS) { 376135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 377135446Strhodes "%s: seek: %s", j->filename, 378135446Strhodes isc_result_totext(result)); 379135446Strhodes return (ISC_R_UNEXPECTED); 380135446Strhodes } 381135446Strhodes j->offset = offset; 382135446Strhodes return (ISC_R_SUCCESS); 383135446Strhodes} 384135446Strhodes 385135446Strhodesstatic isc_result_t 386135446Strhodesjournal_read(dns_journal_t *j, void *mem, size_t nbytes) { 387135446Strhodes isc_result_t result; 388135446Strhodes 389135446Strhodes result = isc_stdio_read(mem, 1, nbytes, j->fp, NULL); 390135446Strhodes if (result != ISC_R_SUCCESS) { 391135446Strhodes if (result == ISC_R_EOF) 392135446Strhodes return (ISC_R_NOMORE); 393135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 394135446Strhodes "%s: read: %s", 395135446Strhodes j->filename, isc_result_totext(result)); 396135446Strhodes return (ISC_R_UNEXPECTED); 397135446Strhodes } 398135446Strhodes j->offset += nbytes; 399135446Strhodes return (ISC_R_SUCCESS); 400135446Strhodes} 401135446Strhodes 402135446Strhodesstatic isc_result_t 403135446Strhodesjournal_write(dns_journal_t *j, void *mem, size_t nbytes) { 404135446Strhodes isc_result_t result; 405135446Strhodes 406135446Strhodes result = isc_stdio_write(mem, 1, nbytes, j->fp, NULL); 407135446Strhodes if (result != ISC_R_SUCCESS) { 408135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 409135446Strhodes "%s: write: %s", 410135446Strhodes j->filename, isc_result_totext(result)); 411135446Strhodes return (ISC_R_UNEXPECTED); 412135446Strhodes } 413135446Strhodes j->offset += nbytes; 414135446Strhodes return (ISC_R_SUCCESS); 415135446Strhodes} 416135446Strhodes 417135446Strhodesstatic isc_result_t 418135446Strhodesjournal_fsync(dns_journal_t *j) { 419135446Strhodes isc_result_t result; 420135446Strhodes result = isc_stdio_flush(j->fp); 421135446Strhodes if (result != ISC_R_SUCCESS) { 422135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 423135446Strhodes "%s: flush: %s", 424135446Strhodes j->filename, isc_result_totext(result)); 425135446Strhodes return (ISC_R_UNEXPECTED); 426135446Strhodes } 427135446Strhodes result = isc_stdio_sync(j->fp); 428135446Strhodes if (result != ISC_R_SUCCESS) { 429135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 430135446Strhodes "%s: fsync: %s", 431135446Strhodes j->filename, isc_result_totext(result)); 432135446Strhodes return (ISC_R_UNEXPECTED); 433135446Strhodes } 434135446Strhodes return (ISC_R_SUCCESS); 435135446Strhodes} 436135446Strhodes 437135446Strhodes/* 438135446Strhodes * Read/write a transaction header at the current file position. 439135446Strhodes */ 440135446Strhodes 441135446Strhodesstatic isc_result_t 442135446Strhodesjournal_read_xhdr(dns_journal_t *j, journal_xhdr_t *xhdr) { 443135446Strhodes journal_rawxhdr_t raw; 444135446Strhodes isc_result_t result; 445135446Strhodes result = journal_read(j, &raw, sizeof(raw)); 446135446Strhodes if (result != ISC_R_SUCCESS) 447135446Strhodes return (result); 448135446Strhodes xhdr->size = decode_uint32(raw.size); 449135446Strhodes xhdr->serial0 = decode_uint32(raw.serial0); 450135446Strhodes xhdr->serial1 = decode_uint32(raw.serial1); 451135446Strhodes return (ISC_R_SUCCESS); 452135446Strhodes} 453135446Strhodes 454135446Strhodesstatic isc_result_t 455135446Strhodesjournal_write_xhdr(dns_journal_t *j, isc_uint32_t size, 456135446Strhodes isc_uint32_t serial0, isc_uint32_t serial1) 457135446Strhodes{ 458135446Strhodes journal_rawxhdr_t raw; 459135446Strhodes encode_uint32(size, raw.size); 460135446Strhodes encode_uint32(serial0, raw.serial0); 461135446Strhodes encode_uint32(serial1, raw.serial1); 462135446Strhodes return (journal_write(j, &raw, sizeof(raw))); 463135446Strhodes} 464135446Strhodes 465135446Strhodes 466135446Strhodes/* 467135446Strhodes * Read an RR header at the current file position. 468135446Strhodes */ 469135446Strhodes 470135446Strhodesstatic isc_result_t 471135446Strhodesjournal_read_rrhdr(dns_journal_t *j, journal_rrhdr_t *rrhdr) { 472135446Strhodes journal_rawrrhdr_t raw; 473135446Strhodes isc_result_t result; 474135446Strhodes result = journal_read(j, &raw, sizeof(raw)); 475135446Strhodes if (result != ISC_R_SUCCESS) 476135446Strhodes return (result); 477135446Strhodes rrhdr->size = decode_uint32(raw.size); 478135446Strhodes return (ISC_R_SUCCESS); 479135446Strhodes} 480135446Strhodes 481135446Strhodesstatic isc_result_t 482135446Strhodesjournal_file_create(isc_mem_t *mctx, const char *filename) { 483135446Strhodes FILE *fp = NULL; 484135446Strhodes isc_result_t result; 485135446Strhodes journal_header_t header; 486135446Strhodes journal_rawheader_t rawheader; 487135446Strhodes int index_size = 56; /* XXX configurable */ 488135446Strhodes int size; 489135446Strhodes void *mem; /* Memory for temporary index image. */ 490135446Strhodes 491135446Strhodes INSIST(sizeof(journal_rawheader_t) == JOURNAL_HEADER_SIZE); 492135446Strhodes 493135446Strhodes result = isc_stdio_open(filename, "wb", &fp); 494135446Strhodes if (result != ISC_R_SUCCESS) { 495135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 496135446Strhodes "%s: create: %s", 497135446Strhodes filename, isc_result_totext(result)); 498135446Strhodes return (ISC_R_UNEXPECTED); 499135446Strhodes } 500135446Strhodes 501135446Strhodes header = initial_journal_header; 502135446Strhodes header.index_size = index_size; 503135446Strhodes journal_header_encode(&header, &rawheader); 504135446Strhodes 505135446Strhodes size = sizeof(journal_rawheader_t) + 506135446Strhodes index_size * sizeof(journal_rawpos_t); 507135446Strhodes 508135446Strhodes mem = isc_mem_get(mctx, size); 509135446Strhodes if (mem == NULL) { 510135446Strhodes (void)isc_stdio_close(fp); 511135446Strhodes (void)isc_file_remove(filename); 512135446Strhodes return (ISC_R_NOMEMORY); 513135446Strhodes } 514135446Strhodes memset(mem, 0, size); 515135446Strhodes memcpy(mem, &rawheader, sizeof(rawheader)); 516135446Strhodes 517135446Strhodes result = isc_stdio_write(mem, 1, (size_t) size, fp, NULL); 518135446Strhodes if (result != ISC_R_SUCCESS) { 519135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 520135446Strhodes "%s: write: %s", 521135446Strhodes filename, isc_result_totext(result)); 522135446Strhodes (void)isc_stdio_close(fp); 523135446Strhodes (void)isc_file_remove(filename); 524135446Strhodes isc_mem_put(mctx, mem, size); 525135446Strhodes return (ISC_R_UNEXPECTED); 526135446Strhodes } 527135446Strhodes isc_mem_put(mctx, mem, size); 528135446Strhodes 529135446Strhodes result = isc_stdio_close(fp); 530135446Strhodes if (result != ISC_R_SUCCESS) { 531135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 532135446Strhodes "%s: close: %s", 533135446Strhodes filename, isc_result_totext(result)); 534135446Strhodes (void)isc_file_remove(filename); 535135446Strhodes return (ISC_R_UNEXPECTED); 536135446Strhodes } 537135446Strhodes 538135446Strhodes return (ISC_R_SUCCESS); 539135446Strhodes} 540135446Strhodes 541135446Strhodesstatic isc_result_t 542135446Strhodesjournal_open(isc_mem_t *mctx, const char *filename, isc_boolean_t write, 543135446Strhodes isc_boolean_t create, dns_journal_t **journalp) { 544135446Strhodes FILE *fp = NULL; 545135446Strhodes isc_result_t result; 546135446Strhodes journal_rawheader_t rawheader; 547135446Strhodes dns_journal_t *j; 548135446Strhodes 549135446Strhodes INSIST(journalp != NULL && *journalp == NULL); 550135446Strhodes j = isc_mem_get(mctx, sizeof(*j)); 551135446Strhodes if (j == NULL) 552135446Strhodes return (ISC_R_NOMEMORY); 553135446Strhodes 554135446Strhodes j->mctx = mctx; 555135446Strhodes j->state = JOURNAL_STATE_INVALID; 556135446Strhodes j->fp = NULL; 557135446Strhodes j->filename = filename; 558135446Strhodes j->index = NULL; 559135446Strhodes j->rawindex = NULL; 560135446Strhodes 561135446Strhodes result = isc_stdio_open(j->filename, write ? "rb+" : "rb", &fp); 562135446Strhodes 563135446Strhodes if (result == ISC_R_FILENOTFOUND) { 564135446Strhodes if (create) { 565135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, 566135446Strhodes ISC_LOG_INFO, 567135446Strhodes "journal file %s does not exist, " 568135446Strhodes "creating it", 569135446Strhodes j->filename); 570135446Strhodes CHECK(journal_file_create(mctx, filename)); 571135446Strhodes /* 572135446Strhodes * Retry. 573135446Strhodes */ 574135446Strhodes result = isc_stdio_open(j->filename, "rb+", &fp); 575135446Strhodes } else { 576135446Strhodes FAIL(ISC_R_NOTFOUND); 577135446Strhodes } 578135446Strhodes } 579135446Strhodes if (result != ISC_R_SUCCESS) { 580135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 581135446Strhodes "%s: open: %s", 582135446Strhodes j->filename, isc_result_totext(result)); 583135446Strhodes FAIL(ISC_R_UNEXPECTED); 584135446Strhodes } 585135446Strhodes 586135446Strhodes j->fp = fp; 587135446Strhodes 588135446Strhodes /* 589135446Strhodes * Set magic early so that seek/read can succeed. 590135446Strhodes */ 591135446Strhodes j->magic = DNS_JOURNAL_MAGIC; 592135446Strhodes 593135446Strhodes CHECK(journal_seek(j, 0)); 594135446Strhodes CHECK(journal_read(j, &rawheader, sizeof(rawheader))); 595135446Strhodes 596135446Strhodes if (memcmp(rawheader.h.format, initial_journal_header.format, 597135446Strhodes sizeof(initial_journal_header.format)) != 0) { 598135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 599135446Strhodes "%s: journal format not recognized", 600135446Strhodes j->filename); 601135446Strhodes FAIL(ISC_R_UNEXPECTED); 602135446Strhodes } 603135446Strhodes journal_header_decode(&rawheader, &j->header); 604135446Strhodes 605135446Strhodes /* 606135446Strhodes * If there is an index, read the raw index into a dynamically 607135446Strhodes * allocated buffer and then convert it into a cooked index. 608135446Strhodes */ 609135446Strhodes if (j->header.index_size != 0) { 610135446Strhodes unsigned int i; 611135446Strhodes unsigned int rawbytes; 612135446Strhodes unsigned char *p; 613135446Strhodes 614135446Strhodes rawbytes = j->header.index_size * sizeof(journal_rawpos_t); 615135446Strhodes j->rawindex = isc_mem_get(mctx, rawbytes); 616135446Strhodes if (j->rawindex == NULL) 617135446Strhodes FAIL(ISC_R_NOMEMORY); 618135446Strhodes 619135446Strhodes CHECK(journal_read(j, j->rawindex, rawbytes)); 620135446Strhodes 621135446Strhodes j->index = isc_mem_get(mctx, j->header.index_size * 622135446Strhodes sizeof(journal_pos_t)); 623135446Strhodes if (j->index == NULL) 624135446Strhodes FAIL(ISC_R_NOMEMORY); 625135446Strhodes 626135446Strhodes p = j->rawindex; 627135446Strhodes for (i = 0; i < j->header.index_size; i++) { 628135446Strhodes j->index[i].serial = decode_uint32(p); 629135446Strhodes p += 4; 630135446Strhodes j->index[i].offset = decode_uint32(p); 631135446Strhodes p += 4; 632135446Strhodes } 633135446Strhodes INSIST(p == j->rawindex + rawbytes); 634135446Strhodes } 635135446Strhodes j->offset = -1; /* Invalid, must seek explicitly. */ 636135446Strhodes 637135446Strhodes /* 638135446Strhodes * Initialize the iterator. 639135446Strhodes */ 640135446Strhodes dns_name_init(&j->it.name, NULL); 641135446Strhodes dns_rdata_init(&j->it.rdata); 642135446Strhodes 643135446Strhodes /* 644193149Sdougb * Set up empty initial buffers for unchecked and checked 645135446Strhodes * wire format RR data. They will be reallocated 646135446Strhodes * later. 647135446Strhodes */ 648135446Strhodes isc_buffer_init(&j->it.source, NULL, 0); 649135446Strhodes isc_buffer_init(&j->it.target, NULL, 0); 650135446Strhodes dns_decompress_init(&j->it.dctx, -1, DNS_DECOMPRESS_NONE); 651135446Strhodes 652135446Strhodes j->state = 653135446Strhodes write ? JOURNAL_STATE_WRITE : JOURNAL_STATE_READ; 654135446Strhodes 655135446Strhodes *journalp = j; 656135446Strhodes return (ISC_R_SUCCESS); 657135446Strhodes 658135446Strhodes failure: 659135446Strhodes j->magic = 0; 660135446Strhodes if (j->index != NULL) { 661135446Strhodes isc_mem_put(j->mctx, j->index, j->header.index_size * 662135446Strhodes sizeof(journal_rawpos_t)); 663135446Strhodes j->index = NULL; 664135446Strhodes } 665135446Strhodes if (j->fp != NULL) 666135446Strhodes (void)isc_stdio_close(j->fp); 667135446Strhodes isc_mem_put(j->mctx, j, sizeof(*j)); 668135446Strhodes return (result); 669135446Strhodes} 670135446Strhodes 671135446Strhodesisc_result_t 672135446Strhodesdns_journal_open(isc_mem_t *mctx, const char *filename, isc_boolean_t write, 673135446Strhodes dns_journal_t **journalp) { 674174187Sdougb isc_result_t result; 675174187Sdougb int namelen; 676174187Sdougb char backup[1024]; 677186462Sdougb 678174187Sdougb result = journal_open(mctx, filename, write, write, journalp); 679174187Sdougb if (result == ISC_R_NOTFOUND) { 680174187Sdougb namelen = strlen(filename); 681174187Sdougb if (namelen > 4 && strcmp(filename + namelen - 4, ".jnl") == 0) 682174187Sdougb namelen -= 4; 683174187Sdougb 684174187Sdougb result = isc_string_printf(backup, sizeof(backup), "%.*s.jbk", 685174187Sdougb namelen, filename); 686174187Sdougb if (result != ISC_R_SUCCESS) 687174187Sdougb return (result); 688174187Sdougb result = journal_open(mctx, backup, write, write, journalp); 689174187Sdougb } 690174187Sdougb return (result); 691135446Strhodes} 692135446Strhodes 693135446Strhodes/* 694135446Strhodes * A comparison function defining the sorting order for 695135446Strhodes * entries in the IXFR-style journal file. 696135446Strhodes * 697135446Strhodes * The IXFR format requires that deletions are sorted before 698135446Strhodes * additions, and within either one, SOA records are sorted 699135446Strhodes * before others. 700135446Strhodes * 701135446Strhodes * Also sort the non-SOA records by type as a courtesy to the 702135446Strhodes * server receiving the IXFR - it may help reduce the amount of 703135446Strhodes * rdataset merging it has to do. 704135446Strhodes */ 705135446Strhodesstatic int 706135446Strhodesixfr_order(const void *av, const void *bv) { 707135446Strhodes dns_difftuple_t const * const *ap = av; 708135446Strhodes dns_difftuple_t const * const *bp = bv; 709135446Strhodes dns_difftuple_t const *a = *ap; 710135446Strhodes dns_difftuple_t const *b = *bp; 711135446Strhodes int r; 712193149Sdougb int bop = 0, aop = 0; 713135446Strhodes 714193149Sdougb switch (a->op) { 715193149Sdougb case DNS_DIFFOP_DEL: 716193149Sdougb case DNS_DIFFOP_DELRESIGN: 717193149Sdougb aop = 1; 718193149Sdougb break; 719193149Sdougb case DNS_DIFFOP_ADD: 720193149Sdougb case DNS_DIFFOP_ADDRESIGN: 721193149Sdougb aop = 0; 722193149Sdougb break; 723193149Sdougb default: 724193149Sdougb INSIST(0); 725193149Sdougb } 726193149Sdougb 727193149Sdougb switch (b->op) { 728193149Sdougb case DNS_DIFFOP_DEL: 729193149Sdougb case DNS_DIFFOP_DELRESIGN: 730193149Sdougb bop = 1; 731193149Sdougb break; 732193149Sdougb case DNS_DIFFOP_ADD: 733193149Sdougb case DNS_DIFFOP_ADDRESIGN: 734193149Sdougb bop = 0; 735193149Sdougb break; 736193149Sdougb default: 737193149Sdougb INSIST(0); 738193149Sdougb } 739193149Sdougb 740193149Sdougb r = bop - aop; 741135446Strhodes if (r != 0) 742135446Strhodes return (r); 743135446Strhodes 744135446Strhodes r = (b->rdata.type == dns_rdatatype_soa) - 745135446Strhodes (a->rdata.type == dns_rdatatype_soa); 746135446Strhodes if (r != 0) 747135446Strhodes return (r); 748135446Strhodes 749135446Strhodes r = (a->rdata.type - b->rdata.type); 750135446Strhodes return (r); 751135446Strhodes} 752135446Strhodes 753135446Strhodes/* 754135446Strhodes * Advance '*pos' to the next journal transaction. 755135446Strhodes * 756135446Strhodes * Requires: 757135446Strhodes * *pos refers to a valid journal transaction. 758135446Strhodes * 759135446Strhodes * Ensures: 760135446Strhodes * When ISC_R_SUCCESS is returned, 761135446Strhodes * *pos refers to the next journal transaction. 762135446Strhodes * 763135446Strhodes * Returns one of: 764135446Strhodes * 765135446Strhodes * ISC_R_SUCCESS 766135446Strhodes * ISC_R_NOMORE *pos pointed at the last transaction 767135446Strhodes * Other results due to file errors are possible. 768135446Strhodes */ 769135446Strhodesstatic isc_result_t 770135446Strhodesjournal_next(dns_journal_t *j, journal_pos_t *pos) { 771135446Strhodes isc_result_t result; 772135446Strhodes journal_xhdr_t xhdr; 773135446Strhodes REQUIRE(DNS_JOURNAL_VALID(j)); 774135446Strhodes 775135446Strhodes result = journal_seek(j, pos->offset); 776135446Strhodes if (result != ISC_R_SUCCESS) 777135446Strhodes return (result); 778135446Strhodes 779135446Strhodes if (pos->serial == j->header.end.serial) 780135446Strhodes return (ISC_R_NOMORE); 781135446Strhodes /* 782135446Strhodes * Read the header of the current transaction. 783135446Strhodes * This will return ISC_R_NOMORE if we are at EOF. 784135446Strhodes */ 785135446Strhodes result = journal_read_xhdr(j, &xhdr); 786135446Strhodes if (result != ISC_R_SUCCESS) 787135446Strhodes return (result); 788135446Strhodes 789135446Strhodes /* 790135446Strhodes * Check serial number consistency. 791135446Strhodes */ 792135446Strhodes if (xhdr.serial0 != pos->serial) { 793135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 794135446Strhodes "%s: journal file corrupt: " 795135446Strhodes "expected serial %u, got %u", 796135446Strhodes j->filename, pos->serial, xhdr.serial0); 797135446Strhodes return (ISC_R_UNEXPECTED); 798135446Strhodes } 799135446Strhodes 800135446Strhodes /* 801135446Strhodes * Check for offset wraparound. 802135446Strhodes */ 803135446Strhodes if ((isc_offset_t)(pos->offset + sizeof(journal_rawxhdr_t) + xhdr.size) 804135446Strhodes < pos->offset) { 805135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 806135446Strhodes "%s: offset too large", j->filename); 807135446Strhodes return (ISC_R_UNEXPECTED); 808135446Strhodes } 809135446Strhodes 810135446Strhodes pos->offset += sizeof(journal_rawxhdr_t) + xhdr.size; 811135446Strhodes pos->serial = xhdr.serial1; 812135446Strhodes return (ISC_R_SUCCESS); 813135446Strhodes} 814135446Strhodes 815135446Strhodes/* 816135446Strhodes * If the index of the journal 'j' contains an entry "better" 817135446Strhodes * than '*best_guess', replace '*best_guess' with it. 818135446Strhodes * 819135446Strhodes * "Better" means having a serial number closer to 'serial' 820135446Strhodes * but not greater than 'serial'. 821135446Strhodes */ 822135446Strhodesstatic void 823135446Strhodesindex_find(dns_journal_t *j, isc_uint32_t serial, journal_pos_t *best_guess) { 824135446Strhodes unsigned int i; 825135446Strhodes if (j->index == NULL) 826135446Strhodes return; 827135446Strhodes for (i = 0; i < j->header.index_size; i++) { 828135446Strhodes if (POS_VALID(j->index[i]) && 829135446Strhodes DNS_SERIAL_GE(serial, j->index[i].serial) && 830135446Strhodes DNS_SERIAL_GT(j->index[i].serial, best_guess->serial)) 831135446Strhodes *best_guess = j->index[i]; 832135446Strhodes } 833135446Strhodes} 834135446Strhodes 835135446Strhodes/* 836135446Strhodes * Add a new index entry. If there is no room, make room by removing 837135446Strhodes * the odd-numbered entries and compacting the others into the first 838135446Strhodes * half of the index. This decimates old index entries exponentially 839135446Strhodes * over time, so that the index always contains a much larger fraction 840135446Strhodes * of recent serial numbers than of old ones. This is deliberate - 841135446Strhodes * most index searches are for outgoing IXFR, and IXFR tends to request 842135446Strhodes * recent versions more often than old ones. 843135446Strhodes */ 844135446Strhodesstatic void 845135446Strhodesindex_add(dns_journal_t *j, journal_pos_t *pos) { 846135446Strhodes unsigned int i; 847135446Strhodes if (j->index == NULL) 848135446Strhodes return; 849135446Strhodes /* 850135446Strhodes * Search for a vacant position. 851135446Strhodes */ 852135446Strhodes for (i = 0; i < j->header.index_size; i++) { 853135446Strhodes if (! POS_VALID(j->index[i])) 854135446Strhodes break; 855135446Strhodes } 856135446Strhodes if (i == j->header.index_size) { 857135446Strhodes unsigned int k = 0; 858135446Strhodes /* 859135446Strhodes * Found no vacant position. Make some room. 860135446Strhodes */ 861135446Strhodes for (i = 0; i < j->header.index_size; i += 2) { 862135446Strhodes j->index[k++] = j->index[i]; 863135446Strhodes } 864135446Strhodes i = k; /* 'i' identifies the first vacant position. */ 865135446Strhodes while (k < j->header.index_size) { 866135446Strhodes POS_INVALIDATE(j->index[k]); 867135446Strhodes k++; 868135446Strhodes } 869135446Strhodes } 870135446Strhodes INSIST(i < j->header.index_size); 871135446Strhodes INSIST(! POS_VALID(j->index[i])); 872135446Strhodes 873135446Strhodes /* 874135446Strhodes * Store the new index entry. 875135446Strhodes */ 876135446Strhodes j->index[i] = *pos; 877135446Strhodes} 878135446Strhodes 879135446Strhodes/* 880135446Strhodes * Invalidate any existing index entries that could become 881135446Strhodes * ambiguous when a new transaction with number 'serial' is added. 882135446Strhodes */ 883135446Strhodesstatic void 884135446Strhodesindex_invalidate(dns_journal_t *j, isc_uint32_t serial) { 885135446Strhodes unsigned int i; 886135446Strhodes if (j->index == NULL) 887135446Strhodes return; 888135446Strhodes for (i = 0; i < j->header.index_size; i++) { 889135446Strhodes if (! DNS_SERIAL_GT(serial, j->index[i].serial)) 890135446Strhodes POS_INVALIDATE(j->index[i]); 891135446Strhodes } 892135446Strhodes} 893135446Strhodes 894135446Strhodes/* 895135446Strhodes * Try to find a transaction with initial serial number 'serial' 896135446Strhodes * in the journal 'j'. 897135446Strhodes * 898135446Strhodes * If found, store its position at '*pos' and return ISC_R_SUCCESS. 899135446Strhodes * 900135446Strhodes * If 'serial' is current (= the ending serial number of the 901135446Strhodes * last transaction in the journal), set '*pos' to 902135446Strhodes * the position immediately following the last transaction and 903135446Strhodes * return ISC_R_SUCCESS. 904135446Strhodes * 905135446Strhodes * If 'serial' is within the range of addressable serial numbers 906135446Strhodes * covered by the journal but that particular serial number is missing 907135446Strhodes * (from the journal, not just from the index), return ISC_R_NOTFOUND. 908135446Strhodes * 909135446Strhodes * If 'serial' is outside the range of addressable serial numbers 910135446Strhodes * covered by the journal, return ISC_R_RANGE. 911135446Strhodes * 912135446Strhodes */ 913135446Strhodesstatic isc_result_t 914135446Strhodesjournal_find(dns_journal_t *j, isc_uint32_t serial, journal_pos_t *pos) { 915135446Strhodes isc_result_t result; 916135446Strhodes journal_pos_t current_pos; 917135446Strhodes REQUIRE(DNS_JOURNAL_VALID(j)); 918135446Strhodes 919135446Strhodes if (DNS_SERIAL_GT(j->header.begin.serial, serial)) 920135446Strhodes return (ISC_R_RANGE); 921135446Strhodes if (DNS_SERIAL_GT(serial, j->header.end.serial)) 922135446Strhodes return (ISC_R_RANGE); 923135446Strhodes if (serial == j->header.end.serial) { 924135446Strhodes *pos = j->header.end; 925135446Strhodes return (ISC_R_SUCCESS); 926135446Strhodes } 927135446Strhodes 928135446Strhodes current_pos = j->header.begin; 929135446Strhodes index_find(j, serial, ¤t_pos); 930135446Strhodes 931135446Strhodes while (current_pos.serial != serial) { 932135446Strhodes if (DNS_SERIAL_GT(current_pos.serial, serial)) 933135446Strhodes return (ISC_R_NOTFOUND); 934135446Strhodes result = journal_next(j, ¤t_pos); 935135446Strhodes if (result != ISC_R_SUCCESS) 936135446Strhodes return (result); 937135446Strhodes } 938135446Strhodes *pos = current_pos; 939135446Strhodes return (ISC_R_SUCCESS); 940135446Strhodes} 941135446Strhodes 942135446Strhodesisc_result_t 943135446Strhodesdns_journal_begin_transaction(dns_journal_t *j) { 944135446Strhodes isc_uint32_t offset; 945135446Strhodes isc_result_t result; 946135446Strhodes journal_rawxhdr_t hdr; 947135446Strhodes 948135446Strhodes REQUIRE(DNS_JOURNAL_VALID(j)); 949135446Strhodes REQUIRE(j->state == JOURNAL_STATE_WRITE); 950135446Strhodes 951135446Strhodes /* 952135446Strhodes * Find the file offset where the new transaction should 953135446Strhodes * be written, and seek there. 954135446Strhodes */ 955135446Strhodes if (JOURNAL_EMPTY(&j->header)) { 956135446Strhodes offset = sizeof(journal_rawheader_t) + 957135446Strhodes j->header.index_size * sizeof(journal_rawpos_t); 958135446Strhodes } else { 959135446Strhodes offset = j->header.end.offset; 960135446Strhodes } 961135446Strhodes j->x.pos[0].offset = offset; 962135446Strhodes j->x.pos[1].offset = offset; /* Initial value, will be incremented. */ 963135446Strhodes j->x.n_soa = 0; 964135446Strhodes 965135446Strhodes CHECK(journal_seek(j, offset)); 966135446Strhodes 967135446Strhodes /* 968135446Strhodes * Write a dummy transaction header of all zeroes to reserve 969135446Strhodes * space. It will be filled in when the transaction is 970135446Strhodes * finished. 971135446Strhodes */ 972135446Strhodes memset(&hdr, 0, sizeof(hdr)); 973135446Strhodes CHECK(journal_write(j, &hdr, sizeof(hdr))); 974135446Strhodes j->x.pos[1].offset = j->offset; 975135446Strhodes 976135446Strhodes j->state = JOURNAL_STATE_TRANSACTION; 977135446Strhodes result = ISC_R_SUCCESS; 978135446Strhodes failure: 979135446Strhodes return (result); 980135446Strhodes} 981135446Strhodes 982135446Strhodesisc_result_t 983135446Strhodesdns_journal_writediff(dns_journal_t *j, dns_diff_t *diff) { 984135446Strhodes dns_difftuple_t *t; 985135446Strhodes isc_buffer_t buffer; 986135446Strhodes void *mem = NULL; 987135446Strhodes unsigned int size; 988135446Strhodes isc_result_t result; 989135446Strhodes isc_region_t used; 990135446Strhodes 991135446Strhodes REQUIRE(DNS_DIFF_VALID(diff)); 992135446Strhodes REQUIRE(j->state == JOURNAL_STATE_TRANSACTION); 993135446Strhodes 994135446Strhodes isc_log_write(JOURNAL_DEBUG_LOGARGS(3), "writing to journal"); 995135446Strhodes (void)dns_diff_print(diff, NULL); 996135446Strhodes 997135446Strhodes /* 998135446Strhodes * Pass 1: determine the buffer size needed, and 999135446Strhodes * keep track of SOA serial numbers. 1000135446Strhodes */ 1001135446Strhodes size = 0; 1002135446Strhodes for (t = ISC_LIST_HEAD(diff->tuples); t != NULL; 1003135446Strhodes t = ISC_LIST_NEXT(t, link)) 1004135446Strhodes { 1005135446Strhodes if (t->rdata.type == dns_rdatatype_soa) { 1006135446Strhodes if (j->x.n_soa < 2) 1007135446Strhodes j->x.pos[j->x.n_soa].serial = 1008135446Strhodes dns_soa_getserial(&t->rdata); 1009135446Strhodes j->x.n_soa++; 1010135446Strhodes } 1011135446Strhodes size += sizeof(journal_rawrrhdr_t); 1012135446Strhodes size += t->name.length; /* XXX should have access macro? */ 1013135446Strhodes size += 10; 1014135446Strhodes size += t->rdata.length; 1015135446Strhodes } 1016135446Strhodes 1017135446Strhodes mem = isc_mem_get(j->mctx, size); 1018135446Strhodes if (mem == NULL) 1019135446Strhodes return (ISC_R_NOMEMORY); 1020135446Strhodes 1021135446Strhodes isc_buffer_init(&buffer, mem, size); 1022135446Strhodes 1023135446Strhodes /* 1024135446Strhodes * Pass 2. Write RRs to buffer. 1025135446Strhodes */ 1026135446Strhodes for (t = ISC_LIST_HEAD(diff->tuples); t != NULL; 1027135446Strhodes t = ISC_LIST_NEXT(t, link)) 1028135446Strhodes { 1029135446Strhodes /* 1030135446Strhodes * Write the RR header. 1031135446Strhodes */ 1032135446Strhodes isc_buffer_putuint32(&buffer, t->name.length + 10 + 1033135446Strhodes t->rdata.length); 1034135446Strhodes /* 1035135446Strhodes * Write the owner name, RR header, and RR data. 1036135446Strhodes */ 1037135446Strhodes isc_buffer_putmem(&buffer, t->name.ndata, t->name.length); 1038135446Strhodes isc_buffer_putuint16(&buffer, t->rdata.type); 1039135446Strhodes isc_buffer_putuint16(&buffer, t->rdata.rdclass); 1040135446Strhodes isc_buffer_putuint32(&buffer, t->ttl); 1041135446Strhodes INSIST(t->rdata.length < 65536); 1042135446Strhodes isc_buffer_putuint16(&buffer, (isc_uint16_t)t->rdata.length); 1043135446Strhodes INSIST(isc_buffer_availablelength(&buffer) >= t->rdata.length); 1044135446Strhodes isc_buffer_putmem(&buffer, t->rdata.data, t->rdata.length); 1045135446Strhodes } 1046135446Strhodes 1047135446Strhodes isc_buffer_usedregion(&buffer, &used); 1048135446Strhodes INSIST(used.length == size); 1049135446Strhodes 1050135446Strhodes j->x.pos[1].offset += used.length; 1051135446Strhodes 1052135446Strhodes /* 1053135446Strhodes * Write the buffer contents to the journal file. 1054135446Strhodes */ 1055135446Strhodes CHECK(journal_write(j, used.base, used.length)); 1056135446Strhodes 1057135446Strhodes result = ISC_R_SUCCESS; 1058135446Strhodes 1059135446Strhodes failure: 1060135446Strhodes if (mem != NULL) 1061135446Strhodes isc_mem_put(j->mctx, mem, size); 1062135446Strhodes return (result); 1063135446Strhodes 1064135446Strhodes} 1065135446Strhodes 1066135446Strhodesisc_result_t 1067135446Strhodesdns_journal_commit(dns_journal_t *j) { 1068135446Strhodes isc_result_t result; 1069135446Strhodes journal_rawheader_t rawheader; 1070135446Strhodes 1071135446Strhodes REQUIRE(DNS_JOURNAL_VALID(j)); 1072135446Strhodes REQUIRE(j->state == JOURNAL_STATE_TRANSACTION); 1073135446Strhodes 1074135446Strhodes /* 1075135446Strhodes * Perform some basic consistency checks. 1076135446Strhodes */ 1077135446Strhodes if (j->x.n_soa != 2) { 1078135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 1079143731Sdougb "%s: malformed transaction: %d SOAs", 1080143731Sdougb j->filename, j->x.n_soa); 1081135446Strhodes return (ISC_R_UNEXPECTED); 1082135446Strhodes } 1083135446Strhodes if (! (DNS_SERIAL_GT(j->x.pos[1].serial, j->x.pos[0].serial) || 1084135446Strhodes (bind8_compat && 1085135446Strhodes j->x.pos[1].serial == j->x.pos[0].serial))) 1086135446Strhodes { 1087135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 1088143731Sdougb "%s: malformed transaction: serial number " 1089143731Sdougb "would decrease", j->filename); 1090135446Strhodes return (ISC_R_UNEXPECTED); 1091135446Strhodes } 1092135446Strhodes if (! JOURNAL_EMPTY(&j->header)) { 1093135446Strhodes if (j->x.pos[0].serial != j->header.end.serial) { 1094135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 1095135446Strhodes "malformed transaction: " 1096135446Strhodes "%s last serial %u != " 1097135446Strhodes "transaction first serial %u", 1098135446Strhodes j->filename, 1099135446Strhodes j->header.end.serial, 1100135446Strhodes j->x.pos[0].serial); 1101135446Strhodes return (ISC_R_UNEXPECTED); 1102135446Strhodes } 1103135446Strhodes } 1104135446Strhodes 1105135446Strhodes /* 1106135446Strhodes * Some old journal entries may become non-addressable 1107135446Strhodes * when we increment the current serial number. Purge them 1108135446Strhodes * by stepping header.begin forward to the first addressable 1109135446Strhodes * transaction. Also purge them from the index. 1110135446Strhodes */ 1111135446Strhodes if (! JOURNAL_EMPTY(&j->header)) { 1112135446Strhodes while (! DNS_SERIAL_GT(j->x.pos[1].serial, 1113135446Strhodes j->header.begin.serial)) { 1114135446Strhodes CHECK(journal_next(j, &j->header.begin)); 1115135446Strhodes } 1116135446Strhodes index_invalidate(j, j->x.pos[1].serial); 1117135446Strhodes } 1118135446Strhodes#ifdef notyet 1119135446Strhodes if (DNS_SERIAL_GT(last_dumped_serial, j->x.pos[1].serial)) { 1120135446Strhodes force_dump(...); 1121135446Strhodes } 1122135446Strhodes#endif 1123135446Strhodes 1124135446Strhodes /* 1125135446Strhodes * Commit the transaction data to stable storage. 1126135446Strhodes */ 1127135446Strhodes CHECK(journal_fsync(j)); 1128135446Strhodes 1129135446Strhodes /* 1130135446Strhodes * Update the transaction header. 1131135446Strhodes */ 1132135446Strhodes CHECK(journal_seek(j, j->x.pos[0].offset)); 1133135446Strhodes CHECK(journal_write_xhdr(j, (j->x.pos[1].offset - j->x.pos[0].offset) - 1134135446Strhodes sizeof(journal_rawxhdr_t), 1135135446Strhodes j->x.pos[0].serial, j->x.pos[1].serial)); 1136135446Strhodes 1137135446Strhodes /* 1138135446Strhodes * Update the journal header. 1139135446Strhodes */ 1140135446Strhodes if (JOURNAL_EMPTY(&j->header)) { 1141135446Strhodes j->header.begin = j->x.pos[0]; 1142135446Strhodes } 1143135446Strhodes j->header.end = j->x.pos[1]; 1144135446Strhodes journal_header_encode(&j->header, &rawheader); 1145135446Strhodes CHECK(journal_seek(j, 0)); 1146135446Strhodes CHECK(journal_write(j, &rawheader, sizeof(rawheader))); 1147135446Strhodes 1148135446Strhodes /* 1149135446Strhodes * Update the index. 1150135446Strhodes */ 1151135446Strhodes index_add(j, &j->x.pos[0]); 1152135446Strhodes 1153135446Strhodes /* 1154135446Strhodes * Convert the index into on-disk format and write 1155135446Strhodes * it to disk. 1156135446Strhodes */ 1157135446Strhodes CHECK(index_to_disk(j)); 1158135446Strhodes 1159135446Strhodes /* 1160135446Strhodes * Commit the header to stable storage. 1161135446Strhodes */ 1162135446Strhodes CHECK(journal_fsync(j)); 1163135446Strhodes 1164135446Strhodes /* 1165135446Strhodes * We no longer have a transaction open. 1166135446Strhodes */ 1167135446Strhodes j->state = JOURNAL_STATE_WRITE; 1168135446Strhodes 1169135446Strhodes result = ISC_R_SUCCESS; 1170135446Strhodes 1171135446Strhodes failure: 1172135446Strhodes return (result); 1173135446Strhodes} 1174135446Strhodes 1175135446Strhodesisc_result_t 1176135446Strhodesdns_journal_write_transaction(dns_journal_t *j, dns_diff_t *diff) { 1177135446Strhodes isc_result_t result; 1178135446Strhodes CHECK(dns_diff_sort(diff, ixfr_order)); 1179135446Strhodes CHECK(dns_journal_begin_transaction(j)); 1180135446Strhodes CHECK(dns_journal_writediff(j, diff)); 1181135446Strhodes CHECK(dns_journal_commit(j)); 1182135446Strhodes result = ISC_R_SUCCESS; 1183135446Strhodes failure: 1184135446Strhodes return (result); 1185135446Strhodes} 1186135446Strhodes 1187135446Strhodesvoid 1188135446Strhodesdns_journal_destroy(dns_journal_t **journalp) { 1189135446Strhodes dns_journal_t *j = *journalp; 1190135446Strhodes REQUIRE(DNS_JOURNAL_VALID(j)); 1191135446Strhodes 1192135446Strhodes j->it.result = ISC_R_FAILURE; 1193135446Strhodes dns_name_invalidate(&j->it.name); 1194135446Strhodes dns_decompress_invalidate(&j->it.dctx); 1195135446Strhodes if (j->rawindex != NULL) 1196135446Strhodes isc_mem_put(j->mctx, j->rawindex, j->header.index_size * 1197135446Strhodes sizeof(journal_rawpos_t)); 1198135446Strhodes if (j->index != NULL) 1199135446Strhodes isc_mem_put(j->mctx, j->index, j->header.index_size * 1200135446Strhodes sizeof(journal_pos_t)); 1201135446Strhodes if (j->it.target.base != NULL) 1202135446Strhodes isc_mem_put(j->mctx, j->it.target.base, j->it.target.length); 1203135446Strhodes if (j->it.source.base != NULL) 1204135446Strhodes isc_mem_put(j->mctx, j->it.source.base, j->it.source.length); 1205135446Strhodes 1206135446Strhodes if (j->fp != NULL) 1207135446Strhodes (void)isc_stdio_close(j->fp); 1208135446Strhodes j->magic = 0; 1209135446Strhodes isc_mem_put(j->mctx, j, sizeof(*j)); 1210135446Strhodes *journalp = NULL; 1211135446Strhodes} 1212135446Strhodes 1213135446Strhodes/* 1214135446Strhodes * Roll the open journal 'j' into the database 'db'. 1215135446Strhodes * A new database version will be created. 1216135446Strhodes */ 1217135446Strhodes 1218135446Strhodes/* XXX Share code with incoming IXFR? */ 1219135446Strhodes 1220135446Strhodesstatic isc_result_t 1221204619Sdougbroll_forward(dns_journal_t *j, dns_db_t *db, unsigned int options, 1222204619Sdougb isc_uint32_t resign) 1223204619Sdougb{ 1224135446Strhodes isc_buffer_t source; /* Transaction data from disk */ 1225135446Strhodes isc_buffer_t target; /* Ditto after _fromwire check */ 1226135446Strhodes isc_uint32_t db_serial; /* Database SOA serial */ 1227135446Strhodes isc_uint32_t end_serial; /* Last journal SOA serial */ 1228135446Strhodes isc_result_t result; 1229135446Strhodes dns_dbversion_t *ver = NULL; 1230135446Strhodes journal_pos_t pos; 1231135446Strhodes dns_diff_t diff; 1232135446Strhodes unsigned int n_soa = 0; 1233135446Strhodes unsigned int n_put = 0; 1234193149Sdougb dns_diffop_t op; 1235135446Strhodes 1236135446Strhodes REQUIRE(DNS_JOURNAL_VALID(j)); 1237135446Strhodes REQUIRE(DNS_DB_VALID(db)); 1238135446Strhodes 1239135446Strhodes dns_diff_init(j->mctx, &diff); 1240204619Sdougb diff.resign = resign; 1241135446Strhodes 1242135446Strhodes /* 1243193149Sdougb * Set up empty initial buffers for unchecked and checked 1244135446Strhodes * wire format transaction data. They will be reallocated 1245135446Strhodes * later. 1246135446Strhodes */ 1247135446Strhodes isc_buffer_init(&source, NULL, 0); 1248135446Strhodes isc_buffer_init(&target, NULL, 0); 1249135446Strhodes 1250135446Strhodes /* 1251135446Strhodes * Create the new database version. 1252135446Strhodes */ 1253135446Strhodes CHECK(dns_db_newversion(db, &ver)); 1254135446Strhodes 1255135446Strhodes /* 1256135446Strhodes * Get the current database SOA serial number. 1257135446Strhodes */ 1258135446Strhodes CHECK(dns_db_getsoaserial(db, ver, &db_serial)); 1259135446Strhodes 1260135446Strhodes /* 1261135446Strhodes * Locate a journal entry for the current database serial. 1262135446Strhodes */ 1263135446Strhodes CHECK(journal_find(j, db_serial, &pos)); 1264135446Strhodes /* 1265135446Strhodes * XXX do more drastic things, like marking zone stale, 1266135446Strhodes * if this fails? 1267135446Strhodes */ 1268135446Strhodes /* 1269135446Strhodes * XXXRTH The zone code should probably mark the zone as bad and 1270135446Strhodes * scream loudly into the log if this is a dynamic update 1271135446Strhodes * log reply that failed. 1272135446Strhodes */ 1273135446Strhodes 1274135446Strhodes end_serial = dns_journal_last_serial(j); 1275135446Strhodes if (db_serial == end_serial) 1276135446Strhodes CHECK(DNS_R_UPTODATE); 1277135446Strhodes 1278135446Strhodes CHECK(dns_journal_iter_init(j, db_serial, end_serial)); 1279135446Strhodes 1280135446Strhodes for (result = dns_journal_first_rr(j); 1281135446Strhodes result == ISC_R_SUCCESS; 1282135446Strhodes result = dns_journal_next_rr(j)) 1283135446Strhodes { 1284135446Strhodes dns_name_t *name; 1285135446Strhodes isc_uint32_t ttl; 1286135446Strhodes dns_rdata_t *rdata; 1287135446Strhodes dns_difftuple_t *tuple = NULL; 1288135446Strhodes 1289135446Strhodes name = NULL; 1290135446Strhodes rdata = NULL; 1291135446Strhodes dns_journal_current_rr(j, &name, &ttl, &rdata); 1292135446Strhodes 1293135446Strhodes if (rdata->type == dns_rdatatype_soa) { 1294135446Strhodes n_soa++; 1295135446Strhodes if (n_soa == 2) 1296135446Strhodes db_serial = j->it.current_serial; 1297135446Strhodes } 1298135446Strhodes 1299135446Strhodes if (n_soa == 3) 1300135446Strhodes n_soa = 1; 1301135446Strhodes if (n_soa == 0) { 1302135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 1303135446Strhodes "%s: journal file corrupt: missing " 1304135446Strhodes "initial SOA", j->filename); 1305135446Strhodes FAIL(ISC_R_UNEXPECTED); 1306135446Strhodes } 1307193149Sdougb if ((options & DNS_JOURNALOPT_RESIGN) != 0) 1308193149Sdougb op = (n_soa == 1) ? DNS_DIFFOP_DELRESIGN : 1309193149Sdougb DNS_DIFFOP_ADDRESIGN; 1310193149Sdougb else 1311193149Sdougb op = (n_soa == 1) ? DNS_DIFFOP_DEL : DNS_DIFFOP_ADD; 1312193149Sdougb 1313193149Sdougb CHECK(dns_difftuple_create(diff.mctx, op, name, ttl, rdata, 1314193149Sdougb &tuple)); 1315135446Strhodes dns_diff_append(&diff, &tuple); 1316135446Strhodes 1317135446Strhodes if (++n_put > 100) { 1318135446Strhodes isc_log_write(JOURNAL_DEBUG_LOGARGS(3), 1319143731Sdougb "%s: applying diff to database (%u)", 1320143731Sdougb j->filename, db_serial); 1321135446Strhodes (void)dns_diff_print(&diff, NULL); 1322135446Strhodes CHECK(dns_diff_apply(&diff, db, ver)); 1323135446Strhodes dns_diff_clear(&diff); 1324135446Strhodes n_put = 0; 1325135446Strhodes } 1326135446Strhodes } 1327135446Strhodes if (result == ISC_R_NOMORE) 1328135446Strhodes result = ISC_R_SUCCESS; 1329135446Strhodes CHECK(result); 1330135446Strhodes 1331135446Strhodes if (n_put != 0) { 1332135446Strhodes isc_log_write(JOURNAL_DEBUG_LOGARGS(3), 1333143731Sdougb "%s: applying final diff to database (%u)", 1334143731Sdougb j->filename, db_serial); 1335135446Strhodes (void)dns_diff_print(&diff, NULL); 1336135446Strhodes CHECK(dns_diff_apply(&diff, db, ver)); 1337135446Strhodes dns_diff_clear(&diff); 1338135446Strhodes } 1339135446Strhodes 1340135446Strhodes failure: 1341135446Strhodes if (ver != NULL) 1342135446Strhodes dns_db_closeversion(db, &ver, result == ISC_R_SUCCESS ? 1343135446Strhodes ISC_TRUE : ISC_FALSE); 1344135446Strhodes 1345135446Strhodes if (source.base != NULL) 1346135446Strhodes isc_mem_put(j->mctx, source.base, source.length); 1347135446Strhodes if (target.base != NULL) 1348135446Strhodes isc_mem_put(j->mctx, target.base, target.length); 1349135446Strhodes 1350135446Strhodes dns_diff_clear(&diff); 1351135446Strhodes 1352135446Strhodes return (result); 1353135446Strhodes} 1354135446Strhodes 1355135446Strhodesisc_result_t 1356193149Sdougbdns_journal_rollforward(isc_mem_t *mctx, dns_db_t *db, 1357193149Sdougb unsigned int options, const char *filename) 1358193149Sdougb{ 1359204619Sdougb REQUIRE((options & DNS_JOURNALOPT_RESIGN) == 0); 1360204619Sdougb return (dns_journal_rollforward2(mctx, db, options, 0, filename)); 1361204619Sdougb} 1362204619Sdougb 1363204619Sdougbisc_result_t 1364204619Sdougbdns_journal_rollforward2(isc_mem_t *mctx, dns_db_t *db, unsigned int options, 1365204619Sdougb isc_uint32_t resign, const char *filename) 1366204619Sdougb{ 1367135446Strhodes dns_journal_t *j; 1368135446Strhodes isc_result_t result; 1369135446Strhodes 1370135446Strhodes REQUIRE(DNS_DB_VALID(db)); 1371135446Strhodes REQUIRE(filename != NULL); 1372135446Strhodes 1373135446Strhodes j = NULL; 1374135446Strhodes result = dns_journal_open(mctx, filename, ISC_FALSE, &j); 1375135446Strhodes if (result == ISC_R_NOTFOUND) { 1376135446Strhodes isc_log_write(JOURNAL_DEBUG_LOGARGS(3), 1377135446Strhodes "no journal file, but that's OK"); 1378135446Strhodes return (DNS_R_NOJOURNAL); 1379135446Strhodes } 1380135446Strhodes if (result != ISC_R_SUCCESS) 1381135446Strhodes return (result); 1382135446Strhodes if (JOURNAL_EMPTY(&j->header)) 1383135446Strhodes result = DNS_R_UPTODATE; 1384135446Strhodes else 1385204619Sdougb result = roll_forward(j, db, options, resign); 1386135446Strhodes 1387135446Strhodes dns_journal_destroy(&j); 1388135446Strhodes 1389135446Strhodes return (result); 1390135446Strhodes} 1391135446Strhodes 1392135446Strhodesisc_result_t 1393135446Strhodesdns_journal_print(isc_mem_t *mctx, const char *filename, FILE *file) { 1394135446Strhodes dns_journal_t *j; 1395135446Strhodes isc_buffer_t source; /* Transaction data from disk */ 1396135446Strhodes isc_buffer_t target; /* Ditto after _fromwire check */ 1397135446Strhodes isc_uint32_t start_serial; /* Database SOA serial */ 1398135446Strhodes isc_uint32_t end_serial; /* Last journal SOA serial */ 1399135446Strhodes isc_result_t result; 1400135446Strhodes dns_diff_t diff; 1401135446Strhodes unsigned int n_soa = 0; 1402135446Strhodes unsigned int n_put = 0; 1403135446Strhodes 1404135446Strhodes REQUIRE(filename != NULL); 1405135446Strhodes 1406135446Strhodes j = NULL; 1407135446Strhodes result = dns_journal_open(mctx, filename, ISC_FALSE, &j); 1408135446Strhodes if (result == ISC_R_NOTFOUND) { 1409135446Strhodes isc_log_write(JOURNAL_DEBUG_LOGARGS(3), "no journal file"); 1410135446Strhodes return (DNS_R_NOJOURNAL); 1411135446Strhodes } 1412135446Strhodes 1413135446Strhodes if (result != ISC_R_SUCCESS) { 1414135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 1415143731Sdougb "journal open failure: %s: %s", 1416186462Sdougb isc_result_totext(result), filename); 1417135446Strhodes return (result); 1418135446Strhodes } 1419135446Strhodes 1420135446Strhodes dns_diff_init(j->mctx, &diff); 1421135446Strhodes 1422135446Strhodes /* 1423193149Sdougb * Set up empty initial buffers for unchecked and checked 1424135446Strhodes * wire format transaction data. They will be reallocated 1425135446Strhodes * later. 1426135446Strhodes */ 1427135446Strhodes isc_buffer_init(&source, NULL, 0); 1428135446Strhodes isc_buffer_init(&target, NULL, 0); 1429135446Strhodes 1430135446Strhodes start_serial = dns_journal_first_serial(j); 1431135446Strhodes end_serial = dns_journal_last_serial(j); 1432135446Strhodes 1433135446Strhodes CHECK(dns_journal_iter_init(j, start_serial, end_serial)); 1434135446Strhodes 1435135446Strhodes for (result = dns_journal_first_rr(j); 1436135446Strhodes result == ISC_R_SUCCESS; 1437135446Strhodes result = dns_journal_next_rr(j)) 1438135446Strhodes { 1439135446Strhodes dns_name_t *name; 1440135446Strhodes isc_uint32_t ttl; 1441135446Strhodes dns_rdata_t *rdata; 1442135446Strhodes dns_difftuple_t *tuple = NULL; 1443135446Strhodes 1444135446Strhodes name = NULL; 1445135446Strhodes rdata = NULL; 1446135446Strhodes dns_journal_current_rr(j, &name, &ttl, &rdata); 1447135446Strhodes 1448135446Strhodes if (rdata->type == dns_rdatatype_soa) 1449135446Strhodes n_soa++; 1450135446Strhodes 1451135446Strhodes if (n_soa == 3) 1452135446Strhodes n_soa = 1; 1453135446Strhodes if (n_soa == 0) { 1454186462Sdougb isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 1455186462Sdougb "%s: journal file corrupt: missing " 1456186462Sdougb "initial SOA", j->filename); 1457135446Strhodes FAIL(ISC_R_UNEXPECTED); 1458135446Strhodes } 1459135446Strhodes CHECK(dns_difftuple_create(diff.mctx, n_soa == 1 ? 1460135446Strhodes DNS_DIFFOP_DEL : DNS_DIFFOP_ADD, 1461135446Strhodes name, ttl, rdata, &tuple)); 1462135446Strhodes dns_diff_append(&diff, &tuple); 1463135446Strhodes 1464135446Strhodes if (++n_put > 100) { 1465135446Strhodes result = dns_diff_print(&diff, file); 1466135446Strhodes dns_diff_clear(&diff); 1467135446Strhodes n_put = 0; 1468135446Strhodes if (result != ISC_R_SUCCESS) 1469135446Strhodes break; 1470135446Strhodes } 1471135446Strhodes } 1472135446Strhodes if (result == ISC_R_NOMORE) 1473135446Strhodes result = ISC_R_SUCCESS; 1474135446Strhodes CHECK(result); 1475135446Strhodes 1476135446Strhodes if (n_put != 0) { 1477135446Strhodes result = dns_diff_print(&diff, file); 1478135446Strhodes dns_diff_clear(&diff); 1479135446Strhodes } 1480135446Strhodes goto cleanup; 1481135446Strhodes 1482135446Strhodes failure: 1483135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 1484135446Strhodes "%s: cannot print: journal file corrupt", j->filename); 1485135446Strhodes 1486135446Strhodes cleanup: 1487135446Strhodes if (source.base != NULL) 1488135446Strhodes isc_mem_put(j->mctx, source.base, source.length); 1489135446Strhodes if (target.base != NULL) 1490135446Strhodes isc_mem_put(j->mctx, target.base, target.length); 1491135446Strhodes 1492135446Strhodes dns_diff_clear(&diff); 1493135446Strhodes dns_journal_destroy(&j); 1494135446Strhodes 1495135446Strhodes return (result); 1496135446Strhodes} 1497135446Strhodes 1498135446Strhodes/**************************************************************************/ 1499135446Strhodes/* 1500135446Strhodes * Miscellaneous accessors. 1501135446Strhodes */ 1502135446Strhodesisc_uint32_t dns_journal_first_serial(dns_journal_t *j) { 1503135446Strhodes return (j->header.begin.serial); 1504135446Strhodes} 1505135446Strhodes 1506135446Strhodesisc_uint32_t dns_journal_last_serial(dns_journal_t *j) { 1507135446Strhodes return (j->header.end.serial); 1508135446Strhodes} 1509135446Strhodes 1510135446Strhodes/**************************************************************************/ 1511135446Strhodes/* 1512135446Strhodes * Iteration support. 1513135446Strhodes * 1514135446Strhodes * When serving an outgoing IXFR, we transmit a part the journal starting 1515135446Strhodes * at the serial number in the IXFR request and ending at the serial 1516135446Strhodes * number that is current when the IXFR request arrives. The ending 1517135446Strhodes * serial number is not necessarily at the end of the journal: 1518135446Strhodes * the journal may grow while the IXFR is in progress, but we stop 1519135446Strhodes * when we reach the serial number that was current when the IXFR started. 1520135446Strhodes */ 1521135446Strhodes 1522135446Strhodesstatic isc_result_t read_one_rr(dns_journal_t *j); 1523135446Strhodes 1524135446Strhodes/* 1525135446Strhodes * Make sure the buffer 'b' is has at least 'size' bytes 1526135446Strhodes * allocated, and clear it. 1527135446Strhodes * 1528135446Strhodes * Requires: 1529135446Strhodes * Either b->base is NULL, or it points to b->length bytes of memory 1530135446Strhodes * previously allocated by isc_mem_get(). 1531135446Strhodes */ 1532135446Strhodes 1533135446Strhodesstatic isc_result_t 1534135446Strhodessize_buffer(isc_mem_t *mctx, isc_buffer_t *b, unsigned size) { 1535135446Strhodes if (b->length < size) { 1536135446Strhodes void *mem = isc_mem_get(mctx, size); 1537135446Strhodes if (mem == NULL) 1538135446Strhodes return (ISC_R_NOMEMORY); 1539135446Strhodes if (b->base != NULL) 1540135446Strhodes isc_mem_put(mctx, b->base, b->length); 1541135446Strhodes b->base = mem; 1542135446Strhodes b->length = size; 1543135446Strhodes } 1544135446Strhodes isc_buffer_clear(b); 1545135446Strhodes return (ISC_R_SUCCESS); 1546135446Strhodes} 1547135446Strhodes 1548135446Strhodesisc_result_t 1549135446Strhodesdns_journal_iter_init(dns_journal_t *j, 1550135446Strhodes isc_uint32_t begin_serial, isc_uint32_t end_serial) 1551135446Strhodes{ 1552135446Strhodes isc_result_t result; 1553135446Strhodes 1554135446Strhodes CHECK(journal_find(j, begin_serial, &j->it.bpos)); 1555135446Strhodes INSIST(j->it.bpos.serial == begin_serial); 1556135446Strhodes 1557135446Strhodes CHECK(journal_find(j, end_serial, &j->it.epos)); 1558135446Strhodes INSIST(j->it.epos.serial == end_serial); 1559135446Strhodes 1560135446Strhodes result = ISC_R_SUCCESS; 1561135446Strhodes failure: 1562135446Strhodes j->it.result = result; 1563135446Strhodes return (j->it.result); 1564135446Strhodes} 1565135446Strhodes 1566135446Strhodes 1567135446Strhodesisc_result_t 1568135446Strhodesdns_journal_first_rr(dns_journal_t *j) { 1569135446Strhodes isc_result_t result; 1570135446Strhodes 1571135446Strhodes /* 1572135446Strhodes * Seek to the beginning of the first transaction we are 1573135446Strhodes * interested in. 1574135446Strhodes */ 1575135446Strhodes CHECK(journal_seek(j, j->it.bpos.offset)); 1576135446Strhodes j->it.current_serial = j->it.bpos.serial; 1577135446Strhodes 1578135446Strhodes j->it.xsize = 0; /* We have no transaction data yet... */ 1579135446Strhodes j->it.xpos = 0; /* ...and haven't used any of it. */ 1580135446Strhodes 1581135446Strhodes return (read_one_rr(j)); 1582135446Strhodes 1583135446Strhodes failure: 1584135446Strhodes return (result); 1585135446Strhodes} 1586135446Strhodes 1587135446Strhodesstatic isc_result_t 1588135446Strhodesread_one_rr(dns_journal_t *j) { 1589135446Strhodes isc_result_t result; 1590135446Strhodes 1591135446Strhodes dns_rdatatype_t rdtype; 1592135446Strhodes dns_rdataclass_t rdclass; 1593135446Strhodes unsigned int rdlen; 1594135446Strhodes isc_uint32_t ttl; 1595135446Strhodes journal_xhdr_t xhdr; 1596135446Strhodes journal_rrhdr_t rrhdr; 1597135446Strhodes 1598135446Strhodes INSIST(j->offset <= j->it.epos.offset); 1599135446Strhodes if (j->offset == j->it.epos.offset) 1600135446Strhodes return (ISC_R_NOMORE); 1601135446Strhodes if (j->it.xpos == j->it.xsize) { 1602135446Strhodes /* 1603135446Strhodes * We are at a transaction boundary. 1604135446Strhodes * Read another transaction header. 1605135446Strhodes */ 1606135446Strhodes CHECK(journal_read_xhdr(j, &xhdr)); 1607135446Strhodes if (xhdr.size == 0) { 1608135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 1609143731Sdougb "%s: journal corrupt: empty transaction", 1610143731Sdougb j->filename); 1611135446Strhodes FAIL(ISC_R_UNEXPECTED); 1612135446Strhodes } 1613135446Strhodes if (xhdr.serial0 != j->it.current_serial) { 1614135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 1615135446Strhodes "%s: journal file corrupt: " 1616135446Strhodes "expected serial %u, got %u", 1617135446Strhodes j->filename, 1618135446Strhodes j->it.current_serial, xhdr.serial0); 1619135446Strhodes FAIL(ISC_R_UNEXPECTED); 1620135446Strhodes } 1621135446Strhodes j->it.xsize = xhdr.size; 1622135446Strhodes j->it.xpos = 0; 1623135446Strhodes } 1624135446Strhodes /* 1625135446Strhodes * Read an RR. 1626135446Strhodes */ 1627153816Sdougb CHECK(journal_read_rrhdr(j, &rrhdr)); 1628135446Strhodes /* 1629135446Strhodes * Perform a sanity check on the journal RR size. 1630135446Strhodes * The smallest possible RR has a 1-byte owner name 1631135446Strhodes * and a 10-byte header. The largest possible 1632135446Strhodes * RR has 65535 bytes of data, a header, and a maximum- 1633135446Strhodes * size owner name, well below 70 k total. 1634135446Strhodes */ 1635135446Strhodes if (rrhdr.size < 1+10 || rrhdr.size > 70000) { 1636135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 1637135446Strhodes "%s: journal corrupt: impossible RR size " 1638135446Strhodes "(%d bytes)", j->filename, rrhdr.size); 1639135446Strhodes FAIL(ISC_R_UNEXPECTED); 1640135446Strhodes } 1641135446Strhodes 1642135446Strhodes CHECK(size_buffer(j->mctx, &j->it.source, rrhdr.size)); 1643135446Strhodes CHECK(journal_read(j, j->it.source.base, rrhdr.size)); 1644135446Strhodes isc_buffer_add(&j->it.source, rrhdr.size); 1645135446Strhodes 1646135446Strhodes /* 1647135446Strhodes * The target buffer is made the same size 1648135446Strhodes * as the source buffer, with the assumption that when 1649135446Strhodes * no compression in present, the output of dns_*_fromwire() 1650135446Strhodes * is no larger than the input. 1651135446Strhodes */ 1652135446Strhodes CHECK(size_buffer(j->mctx, &j->it.target, rrhdr.size)); 1653135446Strhodes 1654135446Strhodes /* 1655135446Strhodes * Parse the owner name. We don't know where it 1656135446Strhodes * ends yet, so we make the entire "remaining" 1657135446Strhodes * part of the buffer "active". 1658135446Strhodes */ 1659135446Strhodes isc_buffer_setactive(&j->it.source, 1660135446Strhodes j->it.source.used - j->it.source.current); 1661135446Strhodes CHECK(dns_name_fromwire(&j->it.name, &j->it.source, 1662135446Strhodes &j->it.dctx, 0, &j->it.target)); 1663135446Strhodes 1664135446Strhodes /* 1665135446Strhodes * Check that the RR header is there, and parse it. 1666135446Strhodes */ 1667135446Strhodes if (isc_buffer_remaininglength(&j->it.source) < 10) 1668135446Strhodes FAIL(DNS_R_FORMERR); 1669135446Strhodes 1670135446Strhodes rdtype = isc_buffer_getuint16(&j->it.source); 1671135446Strhodes rdclass = isc_buffer_getuint16(&j->it.source); 1672135446Strhodes ttl = isc_buffer_getuint32(&j->it.source); 1673135446Strhodes rdlen = isc_buffer_getuint16(&j->it.source); 1674135446Strhodes 1675135446Strhodes /* 1676135446Strhodes * Parse the rdata. 1677135446Strhodes */ 1678174187Sdougb if (isc_buffer_remaininglength(&j->it.source) != rdlen) 1679174187Sdougb FAIL(DNS_R_FORMERR); 1680135446Strhodes isc_buffer_setactive(&j->it.source, rdlen); 1681135446Strhodes dns_rdata_reset(&j->it.rdata); 1682135446Strhodes CHECK(dns_rdata_fromwire(&j->it.rdata, rdclass, 1683135446Strhodes rdtype, &j->it.source, &j->it.dctx, 1684135446Strhodes 0, &j->it.target)); 1685135446Strhodes j->it.ttl = ttl; 1686135446Strhodes 1687135446Strhodes j->it.xpos += sizeof(journal_rawrrhdr_t) + rrhdr.size; 1688135446Strhodes if (rdtype == dns_rdatatype_soa) { 1689135446Strhodes /* XXX could do additional consistency checks here */ 1690135446Strhodes j->it.current_serial = dns_soa_getserial(&j->it.rdata); 1691135446Strhodes } 1692135446Strhodes 1693135446Strhodes result = ISC_R_SUCCESS; 1694135446Strhodes 1695135446Strhodes failure: 1696135446Strhodes j->it.result = result; 1697135446Strhodes return (result); 1698135446Strhodes} 1699135446Strhodes 1700135446Strhodesisc_result_t 1701135446Strhodesdns_journal_next_rr(dns_journal_t *j) { 1702135446Strhodes j->it.result = read_one_rr(j); 1703135446Strhodes return (j->it.result); 1704135446Strhodes} 1705135446Strhodes 1706135446Strhodesvoid 1707135446Strhodesdns_journal_current_rr(dns_journal_t *j, dns_name_t **name, isc_uint32_t *ttl, 1708135446Strhodes dns_rdata_t **rdata) 1709135446Strhodes{ 1710135446Strhodes REQUIRE(j->it.result == ISC_R_SUCCESS); 1711135446Strhodes *name = &j->it.name; 1712135446Strhodes *ttl = j->it.ttl; 1713135446Strhodes *rdata = &j->it.rdata; 1714135446Strhodes} 1715135446Strhodes 1716135446Strhodes/**************************************************************************/ 1717135446Strhodes/* 1718135446Strhodes * Generating diffs from databases 1719135446Strhodes */ 1720135446Strhodes 1721135446Strhodes/* 1722135446Strhodes * Construct a diff containing all the RRs at the current name of the 1723135446Strhodes * database iterator 'dbit' in database 'db', version 'ver'. 1724135446Strhodes * Set '*name' to the current name, and append the diff to 'diff'. 1725135446Strhodes * All new tuples will have the operation 'op'. 1726135446Strhodes * 1727135446Strhodes * Requires: 'name' must have buffer large enough to hold the name. 1728135446Strhodes * Typically, a dns_fixedname_t would be used. 1729135446Strhodes */ 1730135446Strhodesstatic isc_result_t 1731135446Strhodesget_name_diff(dns_db_t *db, dns_dbversion_t *ver, isc_stdtime_t now, 1732135446Strhodes dns_dbiterator_t *dbit, dns_name_t *name, dns_diffop_t op, 1733135446Strhodes dns_diff_t *diff) 1734135446Strhodes{ 1735135446Strhodes isc_result_t result; 1736135446Strhodes dns_dbnode_t *node = NULL; 1737135446Strhodes dns_rdatasetiter_t *rdsiter = NULL; 1738135446Strhodes dns_difftuple_t *tuple = NULL; 1739135446Strhodes 1740135446Strhodes result = dns_dbiterator_current(dbit, &node, name); 1741135446Strhodes if (result != ISC_R_SUCCESS) 1742135446Strhodes return (result); 1743135446Strhodes 1744135446Strhodes result = dns_db_allrdatasets(db, node, ver, now, &rdsiter); 1745135446Strhodes if (result != ISC_R_SUCCESS) 1746135446Strhodes goto cleanup_node; 1747135446Strhodes 1748135446Strhodes for (result = dns_rdatasetiter_first(rdsiter); 1749135446Strhodes result == ISC_R_SUCCESS; 1750135446Strhodes result = dns_rdatasetiter_next(rdsiter)) 1751135446Strhodes { 1752135446Strhodes dns_rdataset_t rdataset; 1753135446Strhodes 1754135446Strhodes dns_rdataset_init(&rdataset); 1755135446Strhodes dns_rdatasetiter_current(rdsiter, &rdataset); 1756135446Strhodes 1757135446Strhodes for (result = dns_rdataset_first(&rdataset); 1758135446Strhodes result == ISC_R_SUCCESS; 1759135446Strhodes result = dns_rdataset_next(&rdataset)) 1760135446Strhodes { 1761135446Strhodes dns_rdata_t rdata = DNS_RDATA_INIT; 1762135446Strhodes dns_rdataset_current(&rdataset, &rdata); 1763135446Strhodes result = dns_difftuple_create(diff->mctx, op, name, 1764135446Strhodes rdataset.ttl, &rdata, 1765135446Strhodes &tuple); 1766135446Strhodes if (result != ISC_R_SUCCESS) { 1767135446Strhodes dns_rdataset_disassociate(&rdataset); 1768135446Strhodes goto cleanup_iterator; 1769135446Strhodes } 1770135446Strhodes dns_diff_append(diff, &tuple); 1771135446Strhodes } 1772135446Strhodes dns_rdataset_disassociate(&rdataset); 1773135446Strhodes if (result != ISC_R_NOMORE) 1774135446Strhodes goto cleanup_iterator; 1775135446Strhodes } 1776135446Strhodes if (result != ISC_R_NOMORE) 1777135446Strhodes goto cleanup_iterator; 1778135446Strhodes 1779135446Strhodes result = ISC_R_SUCCESS; 1780135446Strhodes 1781135446Strhodes cleanup_iterator: 1782135446Strhodes dns_rdatasetiter_destroy(&rdsiter); 1783135446Strhodes 1784135446Strhodes cleanup_node: 1785135446Strhodes dns_db_detachnode(db, &node); 1786135446Strhodes 1787135446Strhodes return (result); 1788135446Strhodes} 1789135446Strhodes 1790135446Strhodes/* 1791135446Strhodes * Comparison function for use by dns_diff_subtract when sorting 1792135446Strhodes * the diffs to be subtracted. The sort keys are the rdata type 1793135446Strhodes * and the rdata itself. The owner name is ignored, because 1794135446Strhodes * it is known to be the same for all tuples. 1795135446Strhodes */ 1796135446Strhodesstatic int 1797135446Strhodesrdata_order(const void *av, const void *bv) { 1798135446Strhodes dns_difftuple_t const * const *ap = av; 1799135446Strhodes dns_difftuple_t const * const *bp = bv; 1800135446Strhodes dns_difftuple_t const *a = *ap; 1801135446Strhodes dns_difftuple_t const *b = *bp; 1802135446Strhodes int r; 1803135446Strhodes r = (b->rdata.type - a->rdata.type); 1804135446Strhodes if (r != 0) 1805135446Strhodes return (r); 1806135446Strhodes r = dns_rdata_compare(&a->rdata, &b->rdata); 1807135446Strhodes return (r); 1808135446Strhodes} 1809135446Strhodes 1810135446Strhodesstatic isc_result_t 1811135446Strhodesdns_diff_subtract(dns_diff_t diff[2], dns_diff_t *r) { 1812135446Strhodes isc_result_t result; 1813135446Strhodes dns_difftuple_t *p[2]; 1814135446Strhodes int i, t; 1815153816Sdougb isc_boolean_t append; 1816153816Sdougb 1817135446Strhodes CHECK(dns_diff_sort(&diff[0], rdata_order)); 1818135446Strhodes CHECK(dns_diff_sort(&diff[1], rdata_order)); 1819135446Strhodes 1820135446Strhodes for (;;) { 1821135446Strhodes p[0] = ISC_LIST_HEAD(diff[0].tuples); 1822135446Strhodes p[1] = ISC_LIST_HEAD(diff[1].tuples); 1823135446Strhodes if (p[0] == NULL && p[1] == NULL) 1824135446Strhodes break; 1825135446Strhodes 1826135446Strhodes for (i = 0; i < 2; i++) 1827135446Strhodes if (p[!i] == NULL) { 1828135446Strhodes ISC_LIST_UNLINK(diff[i].tuples, p[i], link); 1829135446Strhodes ISC_LIST_APPEND(r->tuples, p[i], link); 1830135446Strhodes goto next; 1831135446Strhodes } 1832135446Strhodes t = rdata_order(&p[0], &p[1]); 1833135446Strhodes if (t < 0) { 1834135446Strhodes ISC_LIST_UNLINK(diff[0].tuples, p[0], link); 1835135446Strhodes ISC_LIST_APPEND(r->tuples, p[0], link); 1836135446Strhodes goto next; 1837135446Strhodes } 1838135446Strhodes if (t > 0) { 1839135446Strhodes ISC_LIST_UNLINK(diff[1].tuples, p[1], link); 1840135446Strhodes ISC_LIST_APPEND(r->tuples, p[1], link); 1841135446Strhodes goto next; 1842135446Strhodes } 1843135446Strhodes INSIST(t == 0); 1844135446Strhodes /* 1845153816Sdougb * Identical RRs in both databases; skip them both 1846153816Sdougb * if the ttl differs. 1847135446Strhodes */ 1848153816Sdougb append = ISC_TF(p[0]->ttl != p[1]->ttl); 1849135446Strhodes for (i = 0; i < 2; i++) { 1850135446Strhodes ISC_LIST_UNLINK(diff[i].tuples, p[i], link); 1851153816Sdougb if (append) { 1852153816Sdougb ISC_LIST_APPEND(r->tuples, p[i], link); 1853153816Sdougb } else { 1854153816Sdougb dns_difftuple_free(&p[i]); 1855153816Sdougb } 1856135446Strhodes } 1857135446Strhodes next: ; 1858135446Strhodes } 1859135446Strhodes result = ISC_R_SUCCESS; 1860135446Strhodes failure: 1861135446Strhodes return (result); 1862135446Strhodes} 1863135446Strhodes 1864204619Sdougbstatic isc_result_t 1865204619Sdougbdiff_namespace(isc_mem_t *mctx, 1866204619Sdougb dns_db_t *dba, dns_dbversion_t *dbvera, 1867204619Sdougb dns_db_t *dbb, dns_dbversion_t *dbverb, 1868204619Sdougb unsigned int options, dns_diff_t *resultdiff) 1869135446Strhodes{ 1870135446Strhodes dns_db_t *db[2]; 1871135446Strhodes dns_dbversion_t *ver[2]; 1872135446Strhodes dns_dbiterator_t *dbit[2] = { NULL, NULL }; 1873135446Strhodes isc_boolean_t have[2] = { ISC_FALSE, ISC_FALSE }; 1874135446Strhodes dns_fixedname_t fixname[2]; 1875135446Strhodes isc_result_t result, itresult[2]; 1876204619Sdougb dns_diff_t diff[2]; 1877135446Strhodes int i, t; 1878135446Strhodes 1879135446Strhodes db[0] = dba, db[1] = dbb; 1880135446Strhodes ver[0] = dbvera, ver[1] = dbverb; 1881135446Strhodes 1882135446Strhodes dns_diff_init(mctx, &diff[0]); 1883135446Strhodes dns_diff_init(mctx, &diff[1]); 1884135446Strhodes 1885135446Strhodes dns_fixedname_init(&fixname[0]); 1886135446Strhodes dns_fixedname_init(&fixname[1]); 1887135446Strhodes 1888204619Sdougb result = dns_db_createiterator(db[0], options, &dbit[0]); 1889135446Strhodes if (result != ISC_R_SUCCESS) 1890135446Strhodes return (result); 1891204619Sdougb result = dns_db_createiterator(db[1], options, &dbit[1]); 1892135446Strhodes if (result != ISC_R_SUCCESS) 1893204619Sdougb goto cleanup_iterator; 1894135446Strhodes 1895135446Strhodes itresult[0] = dns_dbiterator_first(dbit[0]); 1896135446Strhodes itresult[1] = dns_dbiterator_first(dbit[1]); 1897135446Strhodes 1898135446Strhodes for (;;) { 1899135446Strhodes for (i = 0; i < 2; i++) { 1900135446Strhodes if (! have[i] && itresult[i] == ISC_R_SUCCESS) { 1901135446Strhodes CHECK(get_name_diff(db[i], ver[i], 0, dbit[i], 1902135446Strhodes dns_fixedname_name(&fixname[i]), 1903135446Strhodes i == 0 ? 1904135446Strhodes DNS_DIFFOP_ADD : 1905135446Strhodes DNS_DIFFOP_DEL, 1906135446Strhodes &diff[i])); 1907135446Strhodes itresult[i] = dns_dbiterator_next(dbit[i]); 1908135446Strhodes have[i] = ISC_TRUE; 1909135446Strhodes } 1910135446Strhodes } 1911135446Strhodes 1912135446Strhodes if (! have[0] && ! have[1]) { 1913135446Strhodes INSIST(ISC_LIST_EMPTY(diff[0].tuples)); 1914135446Strhodes INSIST(ISC_LIST_EMPTY(diff[1].tuples)); 1915135446Strhodes break; 1916135446Strhodes } 1917135446Strhodes 1918135446Strhodes for (i = 0; i < 2; i++) { 1919135446Strhodes if (! have[!i]) { 1920204619Sdougb ISC_LIST_APPENDLIST(resultdiff->tuples, 1921135446Strhodes diff[i].tuples, link); 1922135446Strhodes INSIST(ISC_LIST_EMPTY(diff[i].tuples)); 1923135446Strhodes have[i] = ISC_FALSE; 1924135446Strhodes goto next; 1925135446Strhodes } 1926135446Strhodes } 1927135446Strhodes 1928135446Strhodes t = dns_name_compare(dns_fixedname_name(&fixname[0]), 1929135446Strhodes dns_fixedname_name(&fixname[1])); 1930135446Strhodes if (t < 0) { 1931204619Sdougb ISC_LIST_APPENDLIST(resultdiff->tuples, 1932135446Strhodes diff[0].tuples, link); 1933135446Strhodes INSIST(ISC_LIST_EMPTY(diff[0].tuples)); 1934135446Strhodes have[0] = ISC_FALSE; 1935135446Strhodes continue; 1936135446Strhodes } 1937135446Strhodes if (t > 0) { 1938204619Sdougb ISC_LIST_APPENDLIST(resultdiff->tuples, 1939135446Strhodes diff[1].tuples, link); 1940135446Strhodes INSIST(ISC_LIST_EMPTY(diff[1].tuples)); 1941135446Strhodes have[1] = ISC_FALSE; 1942135446Strhodes continue; 1943135446Strhodes } 1944135446Strhodes INSIST(t == 0); 1945204619Sdougb CHECK(dns_diff_subtract(diff, resultdiff)); 1946135446Strhodes INSIST(ISC_LIST_EMPTY(diff[0].tuples)); 1947135446Strhodes INSIST(ISC_LIST_EMPTY(diff[1].tuples)); 1948135446Strhodes have[0] = have[1] = ISC_FALSE; 1949135446Strhodes next: ; 1950135446Strhodes } 1951135446Strhodes if (itresult[0] != ISC_R_NOMORE) 1952135446Strhodes FAIL(itresult[0]); 1953135446Strhodes if (itresult[1] != ISC_R_NOMORE) 1954135446Strhodes FAIL(itresult[1]); 1955135446Strhodes 1956204619Sdougb INSIST(ISC_LIST_EMPTY(diff[0].tuples)); 1957204619Sdougb INSIST(ISC_LIST_EMPTY(diff[1].tuples)); 1958204619Sdougb 1959204619Sdougb failure: 1960204619Sdougb dns_dbiterator_destroy(&dbit[1]); 1961204619Sdougb cleanup_iterator: 1962204619Sdougb dns_dbiterator_destroy(&dbit[0]); 1963204619Sdougb return (result); 1964204619Sdougb} 1965204619Sdougb 1966204619Sdougb/* 1967204619Sdougb * Compare the databases 'dba' and 'dbb' and generate a journal 1968204619Sdougb * entry containing the changes to make 'dba' from 'dbb' (note 1969204619Sdougb * the order). This journal entry will consist of a single, 1970204619Sdougb * possibly very large transaction. 1971204619Sdougb */ 1972204619Sdougbisc_result_t 1973204619Sdougbdns_db_diff(isc_mem_t *mctx, 1974204619Sdougb dns_db_t *dba, dns_dbversion_t *dbvera, 1975204619Sdougb dns_db_t *dbb, dns_dbversion_t *dbverb, 1976204619Sdougb const char *journal_filename) 1977204619Sdougb{ 1978204619Sdougb isc_result_t result; 1979204619Sdougb dns_journal_t *journal = NULL; 1980204619Sdougb dns_diff_t resultdiff; 1981204619Sdougb 1982204619Sdougb result = dns_journal_open(mctx, journal_filename, ISC_TRUE, &journal); 1983204619Sdougb if (result != ISC_R_SUCCESS) 1984204619Sdougb return (result); 1985204619Sdougb 1986204619Sdougb dns_diff_init(mctx, &resultdiff); 1987204619Sdougb 1988204619Sdougb CHECK(diff_namespace(mctx, dba, dbvera, dbb, dbverb, 1989204619Sdougb DNS_DB_NONSEC3, &resultdiff)); 1990204619Sdougb CHECK(diff_namespace(mctx, dba, dbvera, dbb, dbverb, 1991204619Sdougb DNS_DB_NSEC3ONLY, &resultdiff)); 1992135446Strhodes if (ISC_LIST_EMPTY(resultdiff.tuples)) { 1993135446Strhodes isc_log_write(JOURNAL_DEBUG_LOGARGS(3), "no changes"); 1994135446Strhodes } else { 1995135446Strhodes CHECK(dns_journal_write_transaction(journal, &resultdiff)); 1996135446Strhodes } 1997135446Strhodes failure: 1998135446Strhodes dns_diff_clear(&resultdiff); 1999135446Strhodes dns_journal_destroy(&journal); 2000135446Strhodes return (result); 2001135446Strhodes} 2002135446Strhodes 2003135446Strhodesisc_result_t 2004135446Strhodesdns_journal_compact(isc_mem_t *mctx, char *filename, isc_uint32_t serial, 2005135446Strhodes isc_uint32_t target_size) 2006135446Strhodes{ 2007135446Strhodes unsigned int i; 2008135446Strhodes journal_pos_t best_guess; 2009135446Strhodes journal_pos_t current_pos; 2010135446Strhodes dns_journal_t *j = NULL; 2011174187Sdougb dns_journal_t *new = NULL; 2012135446Strhodes journal_rawheader_t rawheader; 2013135446Strhodes unsigned int copy_length; 2014174187Sdougb int namelen; 2015135446Strhodes char *buf = NULL; 2016135446Strhodes unsigned int size = 0; 2017135446Strhodes isc_result_t result; 2018135446Strhodes unsigned int indexend; 2019174187Sdougb char newname[1024]; 2020174187Sdougb char backup[1024]; 2021174187Sdougb isc_boolean_t is_backup = ISC_FALSE; 2022135446Strhodes 2023174187Sdougb namelen = strlen(filename); 2024174187Sdougb if (namelen > 4 && strcmp(filename + namelen - 4, ".jnl") == 0) 2025174187Sdougb namelen -= 4; 2026135446Strhodes 2027174187Sdougb result = isc_string_printf(newname, sizeof(newname), "%.*s.jnw", 2028174187Sdougb namelen, filename); 2029174187Sdougb if (result != ISC_R_SUCCESS) 2030174187Sdougb return (result); 2031174187Sdougb 2032174187Sdougb result = isc_string_printf(backup, sizeof(backup), "%.*s.jbk", 2033174187Sdougb namelen, filename); 2034174187Sdougb if (result != ISC_R_SUCCESS) 2035174187Sdougb return (result); 2036174187Sdougb 2037174187Sdougb result = journal_open(mctx, filename, ISC_FALSE, ISC_FALSE, &j); 2038174187Sdougb if (result == ISC_R_NOTFOUND) { 2039174187Sdougb is_backup = ISC_TRUE; 2040174187Sdougb result = journal_open(mctx, backup, ISC_FALSE, ISC_FALSE, &j); 2041174187Sdougb } 2042174187Sdougb if (result != ISC_R_SUCCESS) 2043174187Sdougb return (result); 2044174187Sdougb 2045135446Strhodes if (JOURNAL_EMPTY(&j->header)) { 2046135446Strhodes dns_journal_destroy(&j); 2047135446Strhodes return (ISC_R_SUCCESS); 2048135446Strhodes } 2049186462Sdougb 2050135446Strhodes if (DNS_SERIAL_GT(j->header.begin.serial, serial) || 2051135446Strhodes DNS_SERIAL_GT(serial, j->header.end.serial)) { 2052135446Strhodes dns_journal_destroy(&j); 2053135446Strhodes return (ISC_R_RANGE); 2054135446Strhodes } 2055135446Strhodes 2056135446Strhodes /* 2057135446Strhodes * Cope with very small target sizes. 2058135446Strhodes */ 2059135446Strhodes indexend = sizeof(journal_rawheader_t) + 2060135446Strhodes j->header.index_size * sizeof(journal_rawpos_t); 2061135446Strhodes if (target_size < indexend * 2) 2062135446Strhodes target_size = target_size/2 + indexend; 2063135446Strhodes 2064135446Strhodes /* 2065135446Strhodes * See if there is any work to do. 2066135446Strhodes */ 2067135446Strhodes if ((isc_uint32_t) j->header.end.offset < target_size) { 2068135446Strhodes dns_journal_destroy(&j); 2069135446Strhodes return (ISC_R_SUCCESS); 2070135446Strhodes } 2071174187Sdougb 2072174187Sdougb CHECK(journal_open(mctx, newname, ISC_TRUE, ISC_TRUE, &new)); 2073186462Sdougb 2074135446Strhodes /* 2075135446Strhodes * Remove overhead so space test below can succeed. 2076135446Strhodes */ 2077135446Strhodes if (target_size >= indexend) 2078135446Strhodes target_size -= indexend; 2079135446Strhodes 2080135446Strhodes /* 2081135446Strhodes * Find if we can create enough free space. 2082135446Strhodes */ 2083135446Strhodes best_guess = j->header.begin; 2084135446Strhodes for (i = 0; i < j->header.index_size; i++) { 2085135446Strhodes if (POS_VALID(j->index[i]) && 2086135446Strhodes DNS_SERIAL_GE(serial, j->index[i].serial) && 2087135446Strhodes ((isc_uint32_t)(j->header.end.offset - j->index[i].offset) 2088135446Strhodes >= target_size / 2) && 2089135446Strhodes j->index[i].offset > best_guess.offset) 2090135446Strhodes best_guess = j->index[i]; 2091135446Strhodes } 2092135446Strhodes 2093135446Strhodes current_pos = best_guess; 2094135446Strhodes while (current_pos.serial != serial) { 2095135446Strhodes CHECK(journal_next(j, ¤t_pos)); 2096135446Strhodes if (current_pos.serial == j->header.end.serial) 2097135446Strhodes break; 2098135446Strhodes 2099135446Strhodes if (DNS_SERIAL_GE(serial, current_pos.serial) && 2100135446Strhodes ((isc_uint32_t)(j->header.end.offset - current_pos.offset) 2101135446Strhodes >= (target_size / 2)) && 2102135446Strhodes current_pos.offset > best_guess.offset) 2103135446Strhodes best_guess = current_pos; 2104135446Strhodes else 2105135446Strhodes break; 2106135446Strhodes } 2107135446Strhodes 2108135446Strhodes INSIST(best_guess.serial != j->header.end.serial); 2109135446Strhodes if (best_guess.serial != serial) 2110135446Strhodes CHECK(journal_next(j, &best_guess)); 2111135446Strhodes 2112135446Strhodes /* 2113174187Sdougb * We should now be roughly half target_size provided 2114174187Sdougb * we did not reach 'serial'. If not we will just copy 2115174187Sdougb * all uncommitted deltas regardless of the size. 2116135446Strhodes */ 2117135446Strhodes copy_length = j->header.end.offset - best_guess.offset; 2118135446Strhodes 2119135446Strhodes if (copy_length != 0) { 2120135446Strhodes /* 2121135446Strhodes * Copy best_guess to end into space just freed. 2122135446Strhodes */ 2123135446Strhodes size = 64*1024; 2124135446Strhodes if (copy_length < size) 2125135446Strhodes size = copy_length; 2126135446Strhodes buf = isc_mem_get(mctx, size); 2127135446Strhodes if (buf == NULL) { 2128135446Strhodes result = ISC_R_NOMEMORY; 2129135446Strhodes goto failure; 2130135446Strhodes } 2131186462Sdougb 2132174187Sdougb CHECK(journal_seek(j, best_guess.offset)); 2133174187Sdougb CHECK(journal_seek(new, indexend)); 2134135446Strhodes for (i = 0; i < copy_length; i += size) { 2135174187Sdougb unsigned int len = (copy_length - i) > size ? size : 2136135446Strhodes (copy_length - i); 2137135446Strhodes CHECK(journal_read(j, buf, len)); 2138174187Sdougb CHECK(journal_write(new, buf, len)); 2139135446Strhodes } 2140135446Strhodes 2141174187Sdougb CHECK(journal_fsync(new)); 2142135446Strhodes 2143135446Strhodes /* 2144135446Strhodes * Compute new header. 2145135446Strhodes */ 2146174187Sdougb new->header.begin.serial = best_guess.serial; 2147174187Sdougb new->header.begin.offset = indexend; 2148174187Sdougb new->header.end.serial = j->header.end.serial; 2149174187Sdougb new->header.end.offset = indexend + copy_length; 2150174187Sdougb 2151135446Strhodes /* 2152135446Strhodes * Update the journal header. 2153135446Strhodes */ 2154174187Sdougb journal_header_encode(&new->header, &rawheader); 2155174187Sdougb CHECK(journal_seek(new, 0)); 2156174187Sdougb CHECK(journal_write(new, &rawheader, sizeof(rawheader))); 2157174187Sdougb CHECK(journal_fsync(new)); 2158135446Strhodes 2159135446Strhodes /* 2160135446Strhodes * Build new index. 2161135446Strhodes */ 2162174187Sdougb current_pos = new->header.begin; 2163174187Sdougb while (current_pos.serial != new->header.end.serial) { 2164174187Sdougb index_add(new, ¤t_pos); 2165174187Sdougb CHECK(journal_next(new, ¤t_pos)); 2166135446Strhodes } 2167135446Strhodes 2168135446Strhodes /* 2169135446Strhodes * Write index. 2170135446Strhodes */ 2171174187Sdougb CHECK(index_to_disk(new)); 2172174187Sdougb CHECK(journal_fsync(new)); 2173135446Strhodes 2174174187Sdougb indexend = new->header.end.offset; 2175135446Strhodes } 2176174187Sdougb dns_journal_destroy(&new); 2177174187Sdougb 2178174187Sdougb /* 2179174187Sdougb * With a UFS file system this should just succeed and be atomic. 2180174187Sdougb * Any IXFR outs will just continue and the old journal will be 2181174187Sdougb * removed on final close. 2182174187Sdougb * 2183174187Sdougb * With MSDOS / NTFS we need to do a two stage rename triggered 2184174187Sdougb * bu EEXISTS. Hopefully all IXFR's that were active at the last 2185174187Sdougb * rename are now complete. 2186174187Sdougb */ 2187174187Sdougb if (rename(newname, filename) == -1) { 2188174187Sdougb if (errno == EACCES && !is_backup) { 2189174187Sdougb result = isc_file_remove(backup); 2190174187Sdougb if (result != ISC_R_SUCCESS && 2191174187Sdougb result != ISC_R_FILENOTFOUND) 2192174187Sdougb goto failure; 2193174187Sdougb if (rename(filename, backup) == -1) 2194174187Sdougb goto maperrno; 2195174187Sdougb if (rename(newname, filename) == -1) 2196174187Sdougb goto maperrno; 2197174187Sdougb (void)isc_file_remove(backup); 2198174187Sdougb } else { 2199174187Sdougb maperrno: 2200174187Sdougb result = ISC_R_FAILURE; 2201174187Sdougb goto failure; 2202174187Sdougb } 2203174187Sdougb } 2204186462Sdougb 2205135446Strhodes dns_journal_destroy(&j); 2206135446Strhodes result = ISC_R_SUCCESS; 2207135446Strhodes 2208135446Strhodes failure: 2209174187Sdougb (void)isc_file_remove(newname); 2210135446Strhodes if (buf != NULL) 2211135446Strhodes isc_mem_put(mctx, buf, size); 2212135446Strhodes if (j != NULL) 2213135446Strhodes dns_journal_destroy(&j); 2214174187Sdougb if (new != NULL) 2215174187Sdougb dns_journal_destroy(&new); 2216135446Strhodes return (result); 2217135446Strhodes} 2218135446Strhodes 2219135446Strhodesstatic isc_result_t 2220135446Strhodesindex_to_disk(dns_journal_t *j) { 2221135446Strhodes isc_result_t result = ISC_R_SUCCESS; 2222135446Strhodes 2223135446Strhodes if (j->header.index_size != 0) { 2224135446Strhodes unsigned int i; 2225135446Strhodes unsigned char *p; 2226135446Strhodes unsigned int rawbytes; 2227135446Strhodes 2228135446Strhodes rawbytes = j->header.index_size * sizeof(journal_rawpos_t); 2229135446Strhodes 2230135446Strhodes p = j->rawindex; 2231135446Strhodes for (i = 0; i < j->header.index_size; i++) { 2232135446Strhodes encode_uint32(j->index[i].serial, p); 2233135446Strhodes p += 4; 2234135446Strhodes encode_uint32(j->index[i].offset, p); 2235135446Strhodes p += 4; 2236135446Strhodes } 2237135446Strhodes INSIST(p == j->rawindex + rawbytes); 2238135446Strhodes 2239135446Strhodes CHECK(journal_seek(j, sizeof(journal_rawheader_t))); 2240135446Strhodes CHECK(journal_write(j, j->rawindex, rawbytes)); 2241135446Strhodes } 2242135446Strhodesfailure: 2243135446Strhodes return (result); 2244135446Strhodes} 2245