journal.c revision 186462
1135446Strhodes/* 2186462Sdougb * Copyright (C) 2004, 2005, 2007, 2008 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 18186462Sdougb/* $Id: journal.c,v 1.86.18.14 2008/09/25 04:01:36 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 45170222Sdougb * \brief Journalling. 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 175170222Sdougb/* Journalling */ 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 /* 644135446Strhodes * Set up empty initial buffers for uncheched 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; 712135446Strhodes 713135446Strhodes r = (b->op == DNS_DIFFOP_DEL) - (a->op == DNS_DIFFOP_DEL); 714135446Strhodes if (r != 0) 715135446Strhodes return (r); 716135446Strhodes 717135446Strhodes r = (b->rdata.type == dns_rdatatype_soa) - 718135446Strhodes (a->rdata.type == dns_rdatatype_soa); 719135446Strhodes if (r != 0) 720135446Strhodes return (r); 721135446Strhodes 722135446Strhodes r = (a->rdata.type - b->rdata.type); 723135446Strhodes return (r); 724135446Strhodes} 725135446Strhodes 726135446Strhodes/* 727135446Strhodes * Advance '*pos' to the next journal transaction. 728135446Strhodes * 729135446Strhodes * Requires: 730135446Strhodes * *pos refers to a valid journal transaction. 731135446Strhodes * 732135446Strhodes * Ensures: 733135446Strhodes * When ISC_R_SUCCESS is returned, 734135446Strhodes * *pos refers to the next journal transaction. 735135446Strhodes * 736135446Strhodes * Returns one of: 737135446Strhodes * 738135446Strhodes * ISC_R_SUCCESS 739135446Strhodes * ISC_R_NOMORE *pos pointed at the last transaction 740135446Strhodes * Other results due to file errors are possible. 741135446Strhodes */ 742135446Strhodesstatic isc_result_t 743135446Strhodesjournal_next(dns_journal_t *j, journal_pos_t *pos) { 744135446Strhodes isc_result_t result; 745135446Strhodes journal_xhdr_t xhdr; 746135446Strhodes REQUIRE(DNS_JOURNAL_VALID(j)); 747135446Strhodes 748135446Strhodes result = journal_seek(j, pos->offset); 749135446Strhodes if (result != ISC_R_SUCCESS) 750135446Strhodes return (result); 751135446Strhodes 752135446Strhodes if (pos->serial == j->header.end.serial) 753135446Strhodes return (ISC_R_NOMORE); 754135446Strhodes /* 755135446Strhodes * Read the header of the current transaction. 756135446Strhodes * This will return ISC_R_NOMORE if we are at EOF. 757135446Strhodes */ 758135446Strhodes result = journal_read_xhdr(j, &xhdr); 759135446Strhodes if (result != ISC_R_SUCCESS) 760135446Strhodes return (result); 761135446Strhodes 762135446Strhodes /* 763135446Strhodes * Check serial number consistency. 764135446Strhodes */ 765135446Strhodes if (xhdr.serial0 != pos->serial) { 766135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 767135446Strhodes "%s: journal file corrupt: " 768135446Strhodes "expected serial %u, got %u", 769135446Strhodes j->filename, pos->serial, xhdr.serial0); 770135446Strhodes return (ISC_R_UNEXPECTED); 771135446Strhodes } 772135446Strhodes 773135446Strhodes /* 774135446Strhodes * Check for offset wraparound. 775135446Strhodes */ 776135446Strhodes if ((isc_offset_t)(pos->offset + sizeof(journal_rawxhdr_t) + xhdr.size) 777135446Strhodes < pos->offset) { 778135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 779135446Strhodes "%s: offset too large", j->filename); 780135446Strhodes return (ISC_R_UNEXPECTED); 781135446Strhodes } 782135446Strhodes 783135446Strhodes pos->offset += sizeof(journal_rawxhdr_t) + xhdr.size; 784135446Strhodes pos->serial = xhdr.serial1; 785135446Strhodes return (ISC_R_SUCCESS); 786135446Strhodes} 787135446Strhodes 788135446Strhodes/* 789135446Strhodes * If the index of the journal 'j' contains an entry "better" 790135446Strhodes * than '*best_guess', replace '*best_guess' with it. 791135446Strhodes * 792135446Strhodes * "Better" means having a serial number closer to 'serial' 793135446Strhodes * but not greater than 'serial'. 794135446Strhodes */ 795135446Strhodesstatic void 796135446Strhodesindex_find(dns_journal_t *j, isc_uint32_t serial, journal_pos_t *best_guess) { 797135446Strhodes unsigned int i; 798135446Strhodes if (j->index == NULL) 799135446Strhodes return; 800135446Strhodes for (i = 0; i < j->header.index_size; i++) { 801135446Strhodes if (POS_VALID(j->index[i]) && 802135446Strhodes DNS_SERIAL_GE(serial, j->index[i].serial) && 803135446Strhodes DNS_SERIAL_GT(j->index[i].serial, best_guess->serial)) 804135446Strhodes *best_guess = j->index[i]; 805135446Strhodes } 806135446Strhodes} 807135446Strhodes 808135446Strhodes/* 809135446Strhodes * Add a new index entry. If there is no room, make room by removing 810135446Strhodes * the odd-numbered entries and compacting the others into the first 811135446Strhodes * half of the index. This decimates old index entries exponentially 812135446Strhodes * over time, so that the index always contains a much larger fraction 813135446Strhodes * of recent serial numbers than of old ones. This is deliberate - 814135446Strhodes * most index searches are for outgoing IXFR, and IXFR tends to request 815135446Strhodes * recent versions more often than old ones. 816135446Strhodes */ 817135446Strhodesstatic void 818135446Strhodesindex_add(dns_journal_t *j, journal_pos_t *pos) { 819135446Strhodes unsigned int i; 820135446Strhodes if (j->index == NULL) 821135446Strhodes return; 822135446Strhodes /* 823135446Strhodes * Search for a vacant position. 824135446Strhodes */ 825135446Strhodes for (i = 0; i < j->header.index_size; i++) { 826135446Strhodes if (! POS_VALID(j->index[i])) 827135446Strhodes break; 828135446Strhodes } 829135446Strhodes if (i == j->header.index_size) { 830135446Strhodes unsigned int k = 0; 831135446Strhodes /* 832135446Strhodes * Found no vacant position. Make some room. 833135446Strhodes */ 834135446Strhodes for (i = 0; i < j->header.index_size; i += 2) { 835135446Strhodes j->index[k++] = j->index[i]; 836135446Strhodes } 837135446Strhodes i = k; /* 'i' identifies the first vacant position. */ 838135446Strhodes while (k < j->header.index_size) { 839135446Strhodes POS_INVALIDATE(j->index[k]); 840135446Strhodes k++; 841135446Strhodes } 842135446Strhodes } 843135446Strhodes INSIST(i < j->header.index_size); 844135446Strhodes INSIST(! POS_VALID(j->index[i])); 845135446Strhodes 846135446Strhodes /* 847135446Strhodes * Store the new index entry. 848135446Strhodes */ 849135446Strhodes j->index[i] = *pos; 850135446Strhodes} 851135446Strhodes 852135446Strhodes/* 853135446Strhodes * Invalidate any existing index entries that could become 854135446Strhodes * ambiguous when a new transaction with number 'serial' is added. 855135446Strhodes */ 856135446Strhodesstatic void 857135446Strhodesindex_invalidate(dns_journal_t *j, isc_uint32_t serial) { 858135446Strhodes unsigned int i; 859135446Strhodes if (j->index == NULL) 860135446Strhodes return; 861135446Strhodes for (i = 0; i < j->header.index_size; i++) { 862135446Strhodes if (! DNS_SERIAL_GT(serial, j->index[i].serial)) 863135446Strhodes POS_INVALIDATE(j->index[i]); 864135446Strhodes } 865135446Strhodes} 866135446Strhodes 867135446Strhodes/* 868135446Strhodes * Try to find a transaction with initial serial number 'serial' 869135446Strhodes * in the journal 'j'. 870135446Strhodes * 871135446Strhodes * If found, store its position at '*pos' and return ISC_R_SUCCESS. 872135446Strhodes * 873135446Strhodes * If 'serial' is current (= the ending serial number of the 874135446Strhodes * last transaction in the journal), set '*pos' to 875135446Strhodes * the position immediately following the last transaction and 876135446Strhodes * return ISC_R_SUCCESS. 877135446Strhodes * 878135446Strhodes * If 'serial' is within the range of addressable serial numbers 879135446Strhodes * covered by the journal but that particular serial number is missing 880135446Strhodes * (from the journal, not just from the index), return ISC_R_NOTFOUND. 881135446Strhodes * 882135446Strhodes * If 'serial' is outside the range of addressable serial numbers 883135446Strhodes * covered by the journal, return ISC_R_RANGE. 884135446Strhodes * 885135446Strhodes */ 886135446Strhodesstatic isc_result_t 887135446Strhodesjournal_find(dns_journal_t *j, isc_uint32_t serial, journal_pos_t *pos) { 888135446Strhodes isc_result_t result; 889135446Strhodes journal_pos_t current_pos; 890135446Strhodes REQUIRE(DNS_JOURNAL_VALID(j)); 891135446Strhodes 892135446Strhodes if (DNS_SERIAL_GT(j->header.begin.serial, serial)) 893135446Strhodes return (ISC_R_RANGE); 894135446Strhodes if (DNS_SERIAL_GT(serial, j->header.end.serial)) 895135446Strhodes return (ISC_R_RANGE); 896135446Strhodes if (serial == j->header.end.serial) { 897135446Strhodes *pos = j->header.end; 898135446Strhodes return (ISC_R_SUCCESS); 899135446Strhodes } 900135446Strhodes 901135446Strhodes current_pos = j->header.begin; 902135446Strhodes index_find(j, serial, ¤t_pos); 903135446Strhodes 904135446Strhodes while (current_pos.serial != serial) { 905135446Strhodes if (DNS_SERIAL_GT(current_pos.serial, serial)) 906135446Strhodes return (ISC_R_NOTFOUND); 907135446Strhodes result = journal_next(j, ¤t_pos); 908135446Strhodes if (result != ISC_R_SUCCESS) 909135446Strhodes return (result); 910135446Strhodes } 911135446Strhodes *pos = current_pos; 912135446Strhodes return (ISC_R_SUCCESS); 913135446Strhodes} 914135446Strhodes 915135446Strhodesisc_result_t 916135446Strhodesdns_journal_begin_transaction(dns_journal_t *j) { 917135446Strhodes isc_uint32_t offset; 918135446Strhodes isc_result_t result; 919135446Strhodes journal_rawxhdr_t hdr; 920135446Strhodes 921135446Strhodes REQUIRE(DNS_JOURNAL_VALID(j)); 922135446Strhodes REQUIRE(j->state == JOURNAL_STATE_WRITE); 923135446Strhodes 924135446Strhodes /* 925135446Strhodes * Find the file offset where the new transaction should 926135446Strhodes * be written, and seek there. 927135446Strhodes */ 928135446Strhodes if (JOURNAL_EMPTY(&j->header)) { 929135446Strhodes offset = sizeof(journal_rawheader_t) + 930135446Strhodes j->header.index_size * sizeof(journal_rawpos_t); 931135446Strhodes } else { 932135446Strhodes offset = j->header.end.offset; 933135446Strhodes } 934135446Strhodes j->x.pos[0].offset = offset; 935135446Strhodes j->x.pos[1].offset = offset; /* Initial value, will be incremented. */ 936135446Strhodes j->x.n_soa = 0; 937135446Strhodes 938135446Strhodes CHECK(journal_seek(j, offset)); 939135446Strhodes 940135446Strhodes /* 941135446Strhodes * Write a dummy transaction header of all zeroes to reserve 942135446Strhodes * space. It will be filled in when the transaction is 943135446Strhodes * finished. 944135446Strhodes */ 945135446Strhodes memset(&hdr, 0, sizeof(hdr)); 946135446Strhodes CHECK(journal_write(j, &hdr, sizeof(hdr))); 947135446Strhodes j->x.pos[1].offset = j->offset; 948135446Strhodes 949135446Strhodes j->state = JOURNAL_STATE_TRANSACTION; 950135446Strhodes result = ISC_R_SUCCESS; 951135446Strhodes failure: 952135446Strhodes return (result); 953135446Strhodes} 954135446Strhodes 955135446Strhodesisc_result_t 956135446Strhodesdns_journal_writediff(dns_journal_t *j, dns_diff_t *diff) { 957135446Strhodes dns_difftuple_t *t; 958135446Strhodes isc_buffer_t buffer; 959135446Strhodes void *mem = NULL; 960135446Strhodes unsigned int size; 961135446Strhodes isc_result_t result; 962135446Strhodes isc_region_t used; 963135446Strhodes 964135446Strhodes REQUIRE(DNS_DIFF_VALID(diff)); 965135446Strhodes REQUIRE(j->state == JOURNAL_STATE_TRANSACTION); 966135446Strhodes 967135446Strhodes isc_log_write(JOURNAL_DEBUG_LOGARGS(3), "writing to journal"); 968135446Strhodes (void)dns_diff_print(diff, NULL); 969135446Strhodes 970135446Strhodes /* 971135446Strhodes * Pass 1: determine the buffer size needed, and 972135446Strhodes * keep track of SOA serial numbers. 973135446Strhodes */ 974135446Strhodes size = 0; 975135446Strhodes for (t = ISC_LIST_HEAD(diff->tuples); t != NULL; 976135446Strhodes t = ISC_LIST_NEXT(t, link)) 977135446Strhodes { 978135446Strhodes if (t->rdata.type == dns_rdatatype_soa) { 979135446Strhodes if (j->x.n_soa < 2) 980135446Strhodes j->x.pos[j->x.n_soa].serial = 981135446Strhodes dns_soa_getserial(&t->rdata); 982135446Strhodes j->x.n_soa++; 983135446Strhodes } 984135446Strhodes size += sizeof(journal_rawrrhdr_t); 985135446Strhodes size += t->name.length; /* XXX should have access macro? */ 986135446Strhodes size += 10; 987135446Strhodes size += t->rdata.length; 988135446Strhodes } 989135446Strhodes 990135446Strhodes mem = isc_mem_get(j->mctx, size); 991135446Strhodes if (mem == NULL) 992135446Strhodes return (ISC_R_NOMEMORY); 993135446Strhodes 994135446Strhodes isc_buffer_init(&buffer, mem, size); 995135446Strhodes 996135446Strhodes /* 997135446Strhodes * Pass 2. Write RRs to buffer. 998135446Strhodes */ 999135446Strhodes for (t = ISC_LIST_HEAD(diff->tuples); t != NULL; 1000135446Strhodes t = ISC_LIST_NEXT(t, link)) 1001135446Strhodes { 1002135446Strhodes /* 1003135446Strhodes * Write the RR header. 1004135446Strhodes */ 1005135446Strhodes isc_buffer_putuint32(&buffer, t->name.length + 10 + 1006135446Strhodes t->rdata.length); 1007135446Strhodes /* 1008135446Strhodes * Write the owner name, RR header, and RR data. 1009135446Strhodes */ 1010135446Strhodes isc_buffer_putmem(&buffer, t->name.ndata, t->name.length); 1011135446Strhodes isc_buffer_putuint16(&buffer, t->rdata.type); 1012135446Strhodes isc_buffer_putuint16(&buffer, t->rdata.rdclass); 1013135446Strhodes isc_buffer_putuint32(&buffer, t->ttl); 1014135446Strhodes INSIST(t->rdata.length < 65536); 1015135446Strhodes isc_buffer_putuint16(&buffer, (isc_uint16_t)t->rdata.length); 1016135446Strhodes INSIST(isc_buffer_availablelength(&buffer) >= t->rdata.length); 1017135446Strhodes isc_buffer_putmem(&buffer, t->rdata.data, t->rdata.length); 1018135446Strhodes } 1019135446Strhodes 1020135446Strhodes isc_buffer_usedregion(&buffer, &used); 1021135446Strhodes INSIST(used.length == size); 1022135446Strhodes 1023135446Strhodes j->x.pos[1].offset += used.length; 1024135446Strhodes 1025135446Strhodes /* 1026135446Strhodes * Write the buffer contents to the journal file. 1027135446Strhodes */ 1028135446Strhodes CHECK(journal_write(j, used.base, used.length)); 1029135446Strhodes 1030135446Strhodes result = ISC_R_SUCCESS; 1031135446Strhodes 1032135446Strhodes failure: 1033135446Strhodes if (mem != NULL) 1034135446Strhodes isc_mem_put(j->mctx, mem, size); 1035135446Strhodes return (result); 1036135446Strhodes 1037135446Strhodes} 1038135446Strhodes 1039135446Strhodesisc_result_t 1040135446Strhodesdns_journal_commit(dns_journal_t *j) { 1041135446Strhodes isc_result_t result; 1042135446Strhodes journal_rawheader_t rawheader; 1043135446Strhodes 1044135446Strhodes REQUIRE(DNS_JOURNAL_VALID(j)); 1045135446Strhodes REQUIRE(j->state == JOURNAL_STATE_TRANSACTION); 1046135446Strhodes 1047135446Strhodes /* 1048135446Strhodes * Perform some basic consistency checks. 1049135446Strhodes */ 1050135446Strhodes if (j->x.n_soa != 2) { 1051135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 1052143731Sdougb "%s: malformed transaction: %d SOAs", 1053143731Sdougb j->filename, j->x.n_soa); 1054135446Strhodes return (ISC_R_UNEXPECTED); 1055135446Strhodes } 1056135446Strhodes if (! (DNS_SERIAL_GT(j->x.pos[1].serial, j->x.pos[0].serial) || 1057135446Strhodes (bind8_compat && 1058135446Strhodes j->x.pos[1].serial == j->x.pos[0].serial))) 1059135446Strhodes { 1060135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 1061143731Sdougb "%s: malformed transaction: serial number " 1062143731Sdougb "would decrease", j->filename); 1063135446Strhodes return (ISC_R_UNEXPECTED); 1064135446Strhodes } 1065135446Strhodes if (! JOURNAL_EMPTY(&j->header)) { 1066135446Strhodes if (j->x.pos[0].serial != j->header.end.serial) { 1067135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 1068135446Strhodes "malformed transaction: " 1069135446Strhodes "%s last serial %u != " 1070135446Strhodes "transaction first serial %u", 1071135446Strhodes j->filename, 1072135446Strhodes j->header.end.serial, 1073135446Strhodes j->x.pos[0].serial); 1074135446Strhodes return (ISC_R_UNEXPECTED); 1075135446Strhodes } 1076135446Strhodes } 1077135446Strhodes 1078135446Strhodes /* 1079135446Strhodes * Some old journal entries may become non-addressable 1080135446Strhodes * when we increment the current serial number. Purge them 1081135446Strhodes * by stepping header.begin forward to the first addressable 1082135446Strhodes * transaction. Also purge them from the index. 1083135446Strhodes */ 1084135446Strhodes if (! JOURNAL_EMPTY(&j->header)) { 1085135446Strhodes while (! DNS_SERIAL_GT(j->x.pos[1].serial, 1086135446Strhodes j->header.begin.serial)) { 1087135446Strhodes CHECK(journal_next(j, &j->header.begin)); 1088135446Strhodes } 1089135446Strhodes index_invalidate(j, j->x.pos[1].serial); 1090135446Strhodes } 1091135446Strhodes#ifdef notyet 1092135446Strhodes if (DNS_SERIAL_GT(last_dumped_serial, j->x.pos[1].serial)) { 1093135446Strhodes force_dump(...); 1094135446Strhodes } 1095135446Strhodes#endif 1096135446Strhodes 1097135446Strhodes /* 1098135446Strhodes * Commit the transaction data to stable storage. 1099135446Strhodes */ 1100135446Strhodes CHECK(journal_fsync(j)); 1101135446Strhodes 1102135446Strhodes /* 1103135446Strhodes * Update the transaction header. 1104135446Strhodes */ 1105135446Strhodes CHECK(journal_seek(j, j->x.pos[0].offset)); 1106135446Strhodes CHECK(journal_write_xhdr(j, (j->x.pos[1].offset - j->x.pos[0].offset) - 1107135446Strhodes sizeof(journal_rawxhdr_t), 1108135446Strhodes j->x.pos[0].serial, j->x.pos[1].serial)); 1109135446Strhodes 1110135446Strhodes /* 1111135446Strhodes * Update the journal header. 1112135446Strhodes */ 1113135446Strhodes if (JOURNAL_EMPTY(&j->header)) { 1114135446Strhodes j->header.begin = j->x.pos[0]; 1115135446Strhodes } 1116135446Strhodes j->header.end = j->x.pos[1]; 1117135446Strhodes journal_header_encode(&j->header, &rawheader); 1118135446Strhodes CHECK(journal_seek(j, 0)); 1119135446Strhodes CHECK(journal_write(j, &rawheader, sizeof(rawheader))); 1120135446Strhodes 1121135446Strhodes /* 1122135446Strhodes * Update the index. 1123135446Strhodes */ 1124135446Strhodes index_add(j, &j->x.pos[0]); 1125135446Strhodes 1126135446Strhodes /* 1127135446Strhodes * Convert the index into on-disk format and write 1128135446Strhodes * it to disk. 1129135446Strhodes */ 1130135446Strhodes CHECK(index_to_disk(j)); 1131135446Strhodes 1132135446Strhodes /* 1133135446Strhodes * Commit the header to stable storage. 1134135446Strhodes */ 1135135446Strhodes CHECK(journal_fsync(j)); 1136135446Strhodes 1137135446Strhodes /* 1138135446Strhodes * We no longer have a transaction open. 1139135446Strhodes */ 1140135446Strhodes j->state = JOURNAL_STATE_WRITE; 1141135446Strhodes 1142135446Strhodes result = ISC_R_SUCCESS; 1143135446Strhodes 1144135446Strhodes failure: 1145135446Strhodes return (result); 1146135446Strhodes} 1147135446Strhodes 1148135446Strhodesisc_result_t 1149135446Strhodesdns_journal_write_transaction(dns_journal_t *j, dns_diff_t *diff) { 1150135446Strhodes isc_result_t result; 1151135446Strhodes CHECK(dns_diff_sort(diff, ixfr_order)); 1152135446Strhodes CHECK(dns_journal_begin_transaction(j)); 1153135446Strhodes CHECK(dns_journal_writediff(j, diff)); 1154135446Strhodes CHECK(dns_journal_commit(j)); 1155135446Strhodes result = ISC_R_SUCCESS; 1156135446Strhodes failure: 1157135446Strhodes return (result); 1158135446Strhodes} 1159135446Strhodes 1160135446Strhodesvoid 1161135446Strhodesdns_journal_destroy(dns_journal_t **journalp) { 1162135446Strhodes dns_journal_t *j = *journalp; 1163135446Strhodes REQUIRE(DNS_JOURNAL_VALID(j)); 1164135446Strhodes 1165135446Strhodes j->it.result = ISC_R_FAILURE; 1166135446Strhodes dns_name_invalidate(&j->it.name); 1167135446Strhodes dns_decompress_invalidate(&j->it.dctx); 1168135446Strhodes if (j->rawindex != NULL) 1169135446Strhodes isc_mem_put(j->mctx, j->rawindex, j->header.index_size * 1170135446Strhodes sizeof(journal_rawpos_t)); 1171135446Strhodes if (j->index != NULL) 1172135446Strhodes isc_mem_put(j->mctx, j->index, j->header.index_size * 1173135446Strhodes sizeof(journal_pos_t)); 1174135446Strhodes if (j->it.target.base != NULL) 1175135446Strhodes isc_mem_put(j->mctx, j->it.target.base, j->it.target.length); 1176135446Strhodes if (j->it.source.base != NULL) 1177135446Strhodes isc_mem_put(j->mctx, j->it.source.base, j->it.source.length); 1178135446Strhodes 1179135446Strhodes if (j->fp != NULL) 1180135446Strhodes (void)isc_stdio_close(j->fp); 1181135446Strhodes j->magic = 0; 1182135446Strhodes isc_mem_put(j->mctx, j, sizeof(*j)); 1183135446Strhodes *journalp = NULL; 1184135446Strhodes} 1185135446Strhodes 1186135446Strhodes/* 1187135446Strhodes * Roll the open journal 'j' into the database 'db'. 1188135446Strhodes * A new database version will be created. 1189135446Strhodes */ 1190135446Strhodes 1191135446Strhodes/* XXX Share code with incoming IXFR? */ 1192135446Strhodes 1193135446Strhodesstatic isc_result_t 1194135446Strhodesroll_forward(dns_journal_t *j, dns_db_t *db) { 1195135446Strhodes isc_buffer_t source; /* Transaction data from disk */ 1196135446Strhodes isc_buffer_t target; /* Ditto after _fromwire check */ 1197135446Strhodes isc_uint32_t db_serial; /* Database SOA serial */ 1198135446Strhodes isc_uint32_t end_serial; /* Last journal SOA serial */ 1199135446Strhodes isc_result_t result; 1200135446Strhodes dns_dbversion_t *ver = NULL; 1201135446Strhodes journal_pos_t pos; 1202135446Strhodes dns_diff_t diff; 1203135446Strhodes unsigned int n_soa = 0; 1204135446Strhodes unsigned int n_put = 0; 1205135446Strhodes 1206135446Strhodes REQUIRE(DNS_JOURNAL_VALID(j)); 1207135446Strhodes REQUIRE(DNS_DB_VALID(db)); 1208135446Strhodes 1209135446Strhodes dns_diff_init(j->mctx, &diff); 1210135446Strhodes 1211135446Strhodes /* 1212135446Strhodes * Set up empty initial buffers for uncheched and checked 1213135446Strhodes * wire format transaction data. They will be reallocated 1214135446Strhodes * later. 1215135446Strhodes */ 1216135446Strhodes isc_buffer_init(&source, NULL, 0); 1217135446Strhodes isc_buffer_init(&target, NULL, 0); 1218135446Strhodes 1219135446Strhodes /* 1220135446Strhodes * Create the new database version. 1221135446Strhodes */ 1222135446Strhodes CHECK(dns_db_newversion(db, &ver)); 1223135446Strhodes 1224135446Strhodes /* 1225135446Strhodes * Get the current database SOA serial number. 1226135446Strhodes */ 1227135446Strhodes CHECK(dns_db_getsoaserial(db, ver, &db_serial)); 1228135446Strhodes 1229135446Strhodes /* 1230135446Strhodes * Locate a journal entry for the current database serial. 1231135446Strhodes */ 1232135446Strhodes CHECK(journal_find(j, db_serial, &pos)); 1233135446Strhodes /* 1234135446Strhodes * XXX do more drastic things, like marking zone stale, 1235135446Strhodes * if this fails? 1236135446Strhodes */ 1237135446Strhodes /* 1238135446Strhodes * XXXRTH The zone code should probably mark the zone as bad and 1239135446Strhodes * scream loudly into the log if this is a dynamic update 1240135446Strhodes * log reply that failed. 1241135446Strhodes */ 1242135446Strhodes 1243135446Strhodes end_serial = dns_journal_last_serial(j); 1244135446Strhodes if (db_serial == end_serial) 1245135446Strhodes CHECK(DNS_R_UPTODATE); 1246135446Strhodes 1247135446Strhodes CHECK(dns_journal_iter_init(j, db_serial, end_serial)); 1248135446Strhodes 1249135446Strhodes for (result = dns_journal_first_rr(j); 1250135446Strhodes result == ISC_R_SUCCESS; 1251135446Strhodes result = dns_journal_next_rr(j)) 1252135446Strhodes { 1253135446Strhodes dns_name_t *name; 1254135446Strhodes isc_uint32_t ttl; 1255135446Strhodes dns_rdata_t *rdata; 1256135446Strhodes dns_difftuple_t *tuple = NULL; 1257135446Strhodes 1258135446Strhodes name = NULL; 1259135446Strhodes rdata = NULL; 1260135446Strhodes dns_journal_current_rr(j, &name, &ttl, &rdata); 1261135446Strhodes 1262135446Strhodes if (rdata->type == dns_rdatatype_soa) { 1263135446Strhodes n_soa++; 1264135446Strhodes if (n_soa == 2) 1265135446Strhodes db_serial = j->it.current_serial; 1266135446Strhodes } 1267135446Strhodes 1268135446Strhodes if (n_soa == 3) 1269135446Strhodes n_soa = 1; 1270135446Strhodes if (n_soa == 0) { 1271135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 1272135446Strhodes "%s: journal file corrupt: missing " 1273135446Strhodes "initial SOA", j->filename); 1274135446Strhodes FAIL(ISC_R_UNEXPECTED); 1275135446Strhodes } 1276135446Strhodes CHECK(dns_difftuple_create(diff.mctx, n_soa == 1 ? 1277135446Strhodes DNS_DIFFOP_DEL : DNS_DIFFOP_ADD, 1278135446Strhodes name, ttl, rdata, &tuple)); 1279135446Strhodes dns_diff_append(&diff, &tuple); 1280135446Strhodes 1281135446Strhodes if (++n_put > 100) { 1282135446Strhodes isc_log_write(JOURNAL_DEBUG_LOGARGS(3), 1283143731Sdougb "%s: applying diff to database (%u)", 1284143731Sdougb j->filename, db_serial); 1285135446Strhodes (void)dns_diff_print(&diff, NULL); 1286135446Strhodes CHECK(dns_diff_apply(&diff, db, ver)); 1287135446Strhodes dns_diff_clear(&diff); 1288135446Strhodes n_put = 0; 1289135446Strhodes } 1290135446Strhodes } 1291135446Strhodes if (result == ISC_R_NOMORE) 1292135446Strhodes result = ISC_R_SUCCESS; 1293135446Strhodes CHECK(result); 1294135446Strhodes 1295135446Strhodes if (n_put != 0) { 1296135446Strhodes isc_log_write(JOURNAL_DEBUG_LOGARGS(3), 1297143731Sdougb "%s: applying final diff to database (%u)", 1298143731Sdougb j->filename, db_serial); 1299135446Strhodes (void)dns_diff_print(&diff, NULL); 1300135446Strhodes CHECK(dns_diff_apply(&diff, db, ver)); 1301135446Strhodes dns_diff_clear(&diff); 1302135446Strhodes } 1303135446Strhodes 1304135446Strhodes failure: 1305135446Strhodes if (ver != NULL) 1306135446Strhodes dns_db_closeversion(db, &ver, result == ISC_R_SUCCESS ? 1307135446Strhodes ISC_TRUE : ISC_FALSE); 1308135446Strhodes 1309135446Strhodes if (source.base != NULL) 1310135446Strhodes isc_mem_put(j->mctx, source.base, source.length); 1311135446Strhodes if (target.base != NULL) 1312135446Strhodes isc_mem_put(j->mctx, target.base, target.length); 1313135446Strhodes 1314135446Strhodes dns_diff_clear(&diff); 1315135446Strhodes 1316135446Strhodes return (result); 1317135446Strhodes} 1318135446Strhodes 1319135446Strhodesisc_result_t 1320135446Strhodesdns_journal_rollforward(isc_mem_t *mctx, dns_db_t *db, const char *filename) { 1321135446Strhodes dns_journal_t *j; 1322135446Strhodes isc_result_t result; 1323135446Strhodes 1324135446Strhodes REQUIRE(DNS_DB_VALID(db)); 1325135446Strhodes REQUIRE(filename != NULL); 1326135446Strhodes 1327135446Strhodes j = NULL; 1328135446Strhodes result = dns_journal_open(mctx, filename, ISC_FALSE, &j); 1329135446Strhodes if (result == ISC_R_NOTFOUND) { 1330135446Strhodes isc_log_write(JOURNAL_DEBUG_LOGARGS(3), 1331135446Strhodes "no journal file, but that's OK"); 1332135446Strhodes return (DNS_R_NOJOURNAL); 1333135446Strhodes } 1334135446Strhodes if (result != ISC_R_SUCCESS) 1335135446Strhodes return (result); 1336135446Strhodes if (JOURNAL_EMPTY(&j->header)) 1337135446Strhodes result = DNS_R_UPTODATE; 1338135446Strhodes else 1339135446Strhodes result = roll_forward(j, db); 1340135446Strhodes 1341135446Strhodes dns_journal_destroy(&j); 1342135446Strhodes 1343135446Strhodes return (result); 1344135446Strhodes} 1345135446Strhodes 1346135446Strhodesisc_result_t 1347135446Strhodesdns_journal_print(isc_mem_t *mctx, const char *filename, FILE *file) { 1348135446Strhodes dns_journal_t *j; 1349135446Strhodes isc_buffer_t source; /* Transaction data from disk */ 1350135446Strhodes isc_buffer_t target; /* Ditto after _fromwire check */ 1351135446Strhodes isc_uint32_t start_serial; /* Database SOA serial */ 1352135446Strhodes isc_uint32_t end_serial; /* Last journal SOA serial */ 1353135446Strhodes isc_result_t result; 1354135446Strhodes dns_diff_t diff; 1355135446Strhodes unsigned int n_soa = 0; 1356135446Strhodes unsigned int n_put = 0; 1357135446Strhodes 1358135446Strhodes REQUIRE(filename != NULL); 1359135446Strhodes 1360135446Strhodes j = NULL; 1361135446Strhodes result = dns_journal_open(mctx, filename, ISC_FALSE, &j); 1362135446Strhodes if (result == ISC_R_NOTFOUND) { 1363135446Strhodes isc_log_write(JOURNAL_DEBUG_LOGARGS(3), "no journal file"); 1364135446Strhodes return (DNS_R_NOJOURNAL); 1365135446Strhodes } 1366135446Strhodes 1367135446Strhodes if (result != ISC_R_SUCCESS) { 1368135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 1369143731Sdougb "journal open failure: %s: %s", 1370186462Sdougb isc_result_totext(result), filename); 1371135446Strhodes return (result); 1372135446Strhodes } 1373135446Strhodes 1374135446Strhodes dns_diff_init(j->mctx, &diff); 1375135446Strhodes 1376135446Strhodes /* 1377135446Strhodes * Set up empty initial buffers for uncheched and checked 1378135446Strhodes * wire format transaction data. They will be reallocated 1379135446Strhodes * later. 1380135446Strhodes */ 1381135446Strhodes isc_buffer_init(&source, NULL, 0); 1382135446Strhodes isc_buffer_init(&target, NULL, 0); 1383135446Strhodes 1384135446Strhodes start_serial = dns_journal_first_serial(j); 1385135446Strhodes end_serial = dns_journal_last_serial(j); 1386135446Strhodes 1387135446Strhodes CHECK(dns_journal_iter_init(j, start_serial, end_serial)); 1388135446Strhodes 1389135446Strhodes for (result = dns_journal_first_rr(j); 1390135446Strhodes result == ISC_R_SUCCESS; 1391135446Strhodes result = dns_journal_next_rr(j)) 1392135446Strhodes { 1393135446Strhodes dns_name_t *name; 1394135446Strhodes isc_uint32_t ttl; 1395135446Strhodes dns_rdata_t *rdata; 1396135446Strhodes dns_difftuple_t *tuple = NULL; 1397135446Strhodes 1398135446Strhodes name = NULL; 1399135446Strhodes rdata = NULL; 1400135446Strhodes dns_journal_current_rr(j, &name, &ttl, &rdata); 1401135446Strhodes 1402135446Strhodes if (rdata->type == dns_rdatatype_soa) 1403135446Strhodes n_soa++; 1404135446Strhodes 1405135446Strhodes if (n_soa == 3) 1406135446Strhodes n_soa = 1; 1407135446Strhodes if (n_soa == 0) { 1408186462Sdougb isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 1409186462Sdougb "%s: journal file corrupt: missing " 1410186462Sdougb "initial SOA", j->filename); 1411135446Strhodes FAIL(ISC_R_UNEXPECTED); 1412135446Strhodes } 1413135446Strhodes CHECK(dns_difftuple_create(diff.mctx, n_soa == 1 ? 1414135446Strhodes DNS_DIFFOP_DEL : DNS_DIFFOP_ADD, 1415135446Strhodes name, ttl, rdata, &tuple)); 1416135446Strhodes dns_diff_append(&diff, &tuple); 1417135446Strhodes 1418135446Strhodes if (++n_put > 100) { 1419135446Strhodes result = dns_diff_print(&diff, file); 1420135446Strhodes dns_diff_clear(&diff); 1421135446Strhodes n_put = 0; 1422135446Strhodes if (result != ISC_R_SUCCESS) 1423135446Strhodes break; 1424135446Strhodes } 1425135446Strhodes } 1426135446Strhodes if (result == ISC_R_NOMORE) 1427135446Strhodes result = ISC_R_SUCCESS; 1428135446Strhodes CHECK(result); 1429135446Strhodes 1430135446Strhodes if (n_put != 0) { 1431135446Strhodes result = dns_diff_print(&diff, file); 1432135446Strhodes dns_diff_clear(&diff); 1433135446Strhodes } 1434135446Strhodes goto cleanup; 1435135446Strhodes 1436135446Strhodes failure: 1437135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 1438135446Strhodes "%s: cannot print: journal file corrupt", j->filename); 1439135446Strhodes 1440135446Strhodes cleanup: 1441135446Strhodes if (source.base != NULL) 1442135446Strhodes isc_mem_put(j->mctx, source.base, source.length); 1443135446Strhodes if (target.base != NULL) 1444135446Strhodes isc_mem_put(j->mctx, target.base, target.length); 1445135446Strhodes 1446135446Strhodes dns_diff_clear(&diff); 1447135446Strhodes dns_journal_destroy(&j); 1448135446Strhodes 1449135446Strhodes return (result); 1450135446Strhodes} 1451135446Strhodes 1452135446Strhodes/**************************************************************************/ 1453135446Strhodes/* 1454135446Strhodes * Miscellaneous accessors. 1455135446Strhodes */ 1456135446Strhodesisc_uint32_t dns_journal_first_serial(dns_journal_t *j) { 1457135446Strhodes return (j->header.begin.serial); 1458135446Strhodes} 1459135446Strhodes 1460135446Strhodesisc_uint32_t dns_journal_last_serial(dns_journal_t *j) { 1461135446Strhodes return (j->header.end.serial); 1462135446Strhodes} 1463135446Strhodes 1464135446Strhodes/**************************************************************************/ 1465135446Strhodes/* 1466135446Strhodes * Iteration support. 1467135446Strhodes * 1468135446Strhodes * When serving an outgoing IXFR, we transmit a part the journal starting 1469135446Strhodes * at the serial number in the IXFR request and ending at the serial 1470135446Strhodes * number that is current when the IXFR request arrives. The ending 1471135446Strhodes * serial number is not necessarily at the end of the journal: 1472135446Strhodes * the journal may grow while the IXFR is in progress, but we stop 1473135446Strhodes * when we reach the serial number that was current when the IXFR started. 1474135446Strhodes */ 1475135446Strhodes 1476135446Strhodesstatic isc_result_t read_one_rr(dns_journal_t *j); 1477135446Strhodes 1478135446Strhodes/* 1479135446Strhodes * Make sure the buffer 'b' is has at least 'size' bytes 1480135446Strhodes * allocated, and clear it. 1481135446Strhodes * 1482135446Strhodes * Requires: 1483135446Strhodes * Either b->base is NULL, or it points to b->length bytes of memory 1484135446Strhodes * previously allocated by isc_mem_get(). 1485135446Strhodes */ 1486135446Strhodes 1487135446Strhodesstatic isc_result_t 1488135446Strhodessize_buffer(isc_mem_t *mctx, isc_buffer_t *b, unsigned size) { 1489135446Strhodes if (b->length < size) { 1490135446Strhodes void *mem = isc_mem_get(mctx, size); 1491135446Strhodes if (mem == NULL) 1492135446Strhodes return (ISC_R_NOMEMORY); 1493135446Strhodes if (b->base != NULL) 1494135446Strhodes isc_mem_put(mctx, b->base, b->length); 1495135446Strhodes b->base = mem; 1496135446Strhodes b->length = size; 1497135446Strhodes } 1498135446Strhodes isc_buffer_clear(b); 1499135446Strhodes return (ISC_R_SUCCESS); 1500135446Strhodes} 1501135446Strhodes 1502135446Strhodesisc_result_t 1503135446Strhodesdns_journal_iter_init(dns_journal_t *j, 1504135446Strhodes isc_uint32_t begin_serial, isc_uint32_t end_serial) 1505135446Strhodes{ 1506135446Strhodes isc_result_t result; 1507135446Strhodes 1508135446Strhodes CHECK(journal_find(j, begin_serial, &j->it.bpos)); 1509135446Strhodes INSIST(j->it.bpos.serial == begin_serial); 1510135446Strhodes 1511135446Strhodes CHECK(journal_find(j, end_serial, &j->it.epos)); 1512135446Strhodes INSIST(j->it.epos.serial == end_serial); 1513135446Strhodes 1514135446Strhodes result = ISC_R_SUCCESS; 1515135446Strhodes failure: 1516135446Strhodes j->it.result = result; 1517135446Strhodes return (j->it.result); 1518135446Strhodes} 1519135446Strhodes 1520135446Strhodes 1521135446Strhodesisc_result_t 1522135446Strhodesdns_journal_first_rr(dns_journal_t *j) { 1523135446Strhodes isc_result_t result; 1524135446Strhodes 1525135446Strhodes /* 1526135446Strhodes * Seek to the beginning of the first transaction we are 1527135446Strhodes * interested in. 1528135446Strhodes */ 1529135446Strhodes CHECK(journal_seek(j, j->it.bpos.offset)); 1530135446Strhodes j->it.current_serial = j->it.bpos.serial; 1531135446Strhodes 1532135446Strhodes j->it.xsize = 0; /* We have no transaction data yet... */ 1533135446Strhodes j->it.xpos = 0; /* ...and haven't used any of it. */ 1534135446Strhodes 1535135446Strhodes return (read_one_rr(j)); 1536135446Strhodes 1537135446Strhodes failure: 1538135446Strhodes return (result); 1539135446Strhodes} 1540135446Strhodes 1541135446Strhodesstatic isc_result_t 1542135446Strhodesread_one_rr(dns_journal_t *j) { 1543135446Strhodes isc_result_t result; 1544135446Strhodes 1545135446Strhodes dns_rdatatype_t rdtype; 1546135446Strhodes dns_rdataclass_t rdclass; 1547135446Strhodes unsigned int rdlen; 1548135446Strhodes isc_uint32_t ttl; 1549135446Strhodes journal_xhdr_t xhdr; 1550135446Strhodes journal_rrhdr_t rrhdr; 1551135446Strhodes 1552135446Strhodes INSIST(j->offset <= j->it.epos.offset); 1553135446Strhodes if (j->offset == j->it.epos.offset) 1554135446Strhodes return (ISC_R_NOMORE); 1555135446Strhodes if (j->it.xpos == j->it.xsize) { 1556135446Strhodes /* 1557135446Strhodes * We are at a transaction boundary. 1558135446Strhodes * Read another transaction header. 1559135446Strhodes */ 1560135446Strhodes CHECK(journal_read_xhdr(j, &xhdr)); 1561135446Strhodes if (xhdr.size == 0) { 1562135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 1563143731Sdougb "%s: journal corrupt: empty transaction", 1564143731Sdougb j->filename); 1565135446Strhodes FAIL(ISC_R_UNEXPECTED); 1566135446Strhodes } 1567135446Strhodes if (xhdr.serial0 != j->it.current_serial) { 1568135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 1569135446Strhodes "%s: journal file corrupt: " 1570135446Strhodes "expected serial %u, got %u", 1571135446Strhodes j->filename, 1572135446Strhodes j->it.current_serial, xhdr.serial0); 1573135446Strhodes FAIL(ISC_R_UNEXPECTED); 1574135446Strhodes } 1575135446Strhodes j->it.xsize = xhdr.size; 1576135446Strhodes j->it.xpos = 0; 1577135446Strhodes } 1578135446Strhodes /* 1579135446Strhodes * Read an RR. 1580135446Strhodes */ 1581153816Sdougb CHECK(journal_read_rrhdr(j, &rrhdr)); 1582135446Strhodes /* 1583135446Strhodes * Perform a sanity check on the journal RR size. 1584135446Strhodes * The smallest possible RR has a 1-byte owner name 1585135446Strhodes * and a 10-byte header. The largest possible 1586135446Strhodes * RR has 65535 bytes of data, a header, and a maximum- 1587135446Strhodes * size owner name, well below 70 k total. 1588135446Strhodes */ 1589135446Strhodes if (rrhdr.size < 1+10 || rrhdr.size > 70000) { 1590135446Strhodes isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, 1591135446Strhodes "%s: journal corrupt: impossible RR size " 1592135446Strhodes "(%d bytes)", j->filename, rrhdr.size); 1593135446Strhodes FAIL(ISC_R_UNEXPECTED); 1594135446Strhodes } 1595135446Strhodes 1596135446Strhodes CHECK(size_buffer(j->mctx, &j->it.source, rrhdr.size)); 1597135446Strhodes CHECK(journal_read(j, j->it.source.base, rrhdr.size)); 1598135446Strhodes isc_buffer_add(&j->it.source, rrhdr.size); 1599135446Strhodes 1600135446Strhodes /* 1601135446Strhodes * The target buffer is made the same size 1602135446Strhodes * as the source buffer, with the assumption that when 1603135446Strhodes * no compression in present, the output of dns_*_fromwire() 1604135446Strhodes * is no larger than the input. 1605135446Strhodes */ 1606135446Strhodes CHECK(size_buffer(j->mctx, &j->it.target, rrhdr.size)); 1607135446Strhodes 1608135446Strhodes /* 1609135446Strhodes * Parse the owner name. We don't know where it 1610135446Strhodes * ends yet, so we make the entire "remaining" 1611135446Strhodes * part of the buffer "active". 1612135446Strhodes */ 1613135446Strhodes isc_buffer_setactive(&j->it.source, 1614135446Strhodes j->it.source.used - j->it.source.current); 1615135446Strhodes CHECK(dns_name_fromwire(&j->it.name, &j->it.source, 1616135446Strhodes &j->it.dctx, 0, &j->it.target)); 1617135446Strhodes 1618135446Strhodes /* 1619135446Strhodes * Check that the RR header is there, and parse it. 1620135446Strhodes */ 1621135446Strhodes if (isc_buffer_remaininglength(&j->it.source) < 10) 1622135446Strhodes FAIL(DNS_R_FORMERR); 1623135446Strhodes 1624135446Strhodes rdtype = isc_buffer_getuint16(&j->it.source); 1625135446Strhodes rdclass = isc_buffer_getuint16(&j->it.source); 1626135446Strhodes ttl = isc_buffer_getuint32(&j->it.source); 1627135446Strhodes rdlen = isc_buffer_getuint16(&j->it.source); 1628135446Strhodes 1629135446Strhodes /* 1630135446Strhodes * Parse the rdata. 1631135446Strhodes */ 1632174187Sdougb if (isc_buffer_remaininglength(&j->it.source) != rdlen) 1633174187Sdougb FAIL(DNS_R_FORMERR); 1634135446Strhodes isc_buffer_setactive(&j->it.source, rdlen); 1635135446Strhodes dns_rdata_reset(&j->it.rdata); 1636135446Strhodes CHECK(dns_rdata_fromwire(&j->it.rdata, rdclass, 1637135446Strhodes rdtype, &j->it.source, &j->it.dctx, 1638135446Strhodes 0, &j->it.target)); 1639135446Strhodes j->it.ttl = ttl; 1640135446Strhodes 1641135446Strhodes j->it.xpos += sizeof(journal_rawrrhdr_t) + rrhdr.size; 1642135446Strhodes if (rdtype == dns_rdatatype_soa) { 1643135446Strhodes /* XXX could do additional consistency checks here */ 1644135446Strhodes j->it.current_serial = dns_soa_getserial(&j->it.rdata); 1645135446Strhodes } 1646135446Strhodes 1647135446Strhodes result = ISC_R_SUCCESS; 1648135446Strhodes 1649135446Strhodes failure: 1650135446Strhodes j->it.result = result; 1651135446Strhodes return (result); 1652135446Strhodes} 1653135446Strhodes 1654135446Strhodesisc_result_t 1655135446Strhodesdns_journal_next_rr(dns_journal_t *j) { 1656135446Strhodes j->it.result = read_one_rr(j); 1657135446Strhodes return (j->it.result); 1658135446Strhodes} 1659135446Strhodes 1660135446Strhodesvoid 1661135446Strhodesdns_journal_current_rr(dns_journal_t *j, dns_name_t **name, isc_uint32_t *ttl, 1662135446Strhodes dns_rdata_t **rdata) 1663135446Strhodes{ 1664135446Strhodes REQUIRE(j->it.result == ISC_R_SUCCESS); 1665135446Strhodes *name = &j->it.name; 1666135446Strhodes *ttl = j->it.ttl; 1667135446Strhodes *rdata = &j->it.rdata; 1668135446Strhodes} 1669135446Strhodes 1670135446Strhodes/**************************************************************************/ 1671135446Strhodes/* 1672135446Strhodes * Generating diffs from databases 1673135446Strhodes */ 1674135446Strhodes 1675135446Strhodes/* 1676135446Strhodes * Construct a diff containing all the RRs at the current name of the 1677135446Strhodes * database iterator 'dbit' in database 'db', version 'ver'. 1678135446Strhodes * Set '*name' to the current name, and append the diff to 'diff'. 1679135446Strhodes * All new tuples will have the operation 'op'. 1680135446Strhodes * 1681135446Strhodes * Requires: 'name' must have buffer large enough to hold the name. 1682135446Strhodes * Typically, a dns_fixedname_t would be used. 1683135446Strhodes */ 1684135446Strhodesstatic isc_result_t 1685135446Strhodesget_name_diff(dns_db_t *db, dns_dbversion_t *ver, isc_stdtime_t now, 1686135446Strhodes dns_dbiterator_t *dbit, dns_name_t *name, dns_diffop_t op, 1687135446Strhodes dns_diff_t *diff) 1688135446Strhodes{ 1689135446Strhodes isc_result_t result; 1690135446Strhodes dns_dbnode_t *node = NULL; 1691135446Strhodes dns_rdatasetiter_t *rdsiter = NULL; 1692135446Strhodes dns_difftuple_t *tuple = NULL; 1693135446Strhodes 1694135446Strhodes result = dns_dbiterator_current(dbit, &node, name); 1695135446Strhodes if (result != ISC_R_SUCCESS) 1696135446Strhodes return (result); 1697135446Strhodes 1698135446Strhodes result = dns_db_allrdatasets(db, node, ver, now, &rdsiter); 1699135446Strhodes if (result != ISC_R_SUCCESS) 1700135446Strhodes goto cleanup_node; 1701135446Strhodes 1702135446Strhodes for (result = dns_rdatasetiter_first(rdsiter); 1703135446Strhodes result == ISC_R_SUCCESS; 1704135446Strhodes result = dns_rdatasetiter_next(rdsiter)) 1705135446Strhodes { 1706135446Strhodes dns_rdataset_t rdataset; 1707135446Strhodes 1708135446Strhodes dns_rdataset_init(&rdataset); 1709135446Strhodes dns_rdatasetiter_current(rdsiter, &rdataset); 1710135446Strhodes 1711135446Strhodes for (result = dns_rdataset_first(&rdataset); 1712135446Strhodes result == ISC_R_SUCCESS; 1713135446Strhodes result = dns_rdataset_next(&rdataset)) 1714135446Strhodes { 1715135446Strhodes dns_rdata_t rdata = DNS_RDATA_INIT; 1716135446Strhodes dns_rdataset_current(&rdataset, &rdata); 1717135446Strhodes result = dns_difftuple_create(diff->mctx, op, name, 1718135446Strhodes rdataset.ttl, &rdata, 1719135446Strhodes &tuple); 1720135446Strhodes if (result != ISC_R_SUCCESS) { 1721135446Strhodes dns_rdataset_disassociate(&rdataset); 1722135446Strhodes goto cleanup_iterator; 1723135446Strhodes } 1724135446Strhodes dns_diff_append(diff, &tuple); 1725135446Strhodes } 1726135446Strhodes dns_rdataset_disassociate(&rdataset); 1727135446Strhodes if (result != ISC_R_NOMORE) 1728135446Strhodes goto cleanup_iterator; 1729135446Strhodes } 1730135446Strhodes if (result != ISC_R_NOMORE) 1731135446Strhodes goto cleanup_iterator; 1732135446Strhodes 1733135446Strhodes result = ISC_R_SUCCESS; 1734135446Strhodes 1735135446Strhodes cleanup_iterator: 1736135446Strhodes dns_rdatasetiter_destroy(&rdsiter); 1737135446Strhodes 1738135446Strhodes cleanup_node: 1739135446Strhodes dns_db_detachnode(db, &node); 1740135446Strhodes 1741135446Strhodes return (result); 1742135446Strhodes} 1743135446Strhodes 1744135446Strhodes/* 1745135446Strhodes * Comparison function for use by dns_diff_subtract when sorting 1746135446Strhodes * the diffs to be subtracted. The sort keys are the rdata type 1747135446Strhodes * and the rdata itself. The owner name is ignored, because 1748135446Strhodes * it is known to be the same for all tuples. 1749135446Strhodes */ 1750135446Strhodesstatic int 1751135446Strhodesrdata_order(const void *av, const void *bv) { 1752135446Strhodes dns_difftuple_t const * const *ap = av; 1753135446Strhodes dns_difftuple_t const * const *bp = bv; 1754135446Strhodes dns_difftuple_t const *a = *ap; 1755135446Strhodes dns_difftuple_t const *b = *bp; 1756135446Strhodes int r; 1757135446Strhodes r = (b->rdata.type - a->rdata.type); 1758135446Strhodes if (r != 0) 1759135446Strhodes return (r); 1760135446Strhodes r = dns_rdata_compare(&a->rdata, &b->rdata); 1761135446Strhodes return (r); 1762135446Strhodes} 1763135446Strhodes 1764135446Strhodesstatic isc_result_t 1765135446Strhodesdns_diff_subtract(dns_diff_t diff[2], dns_diff_t *r) { 1766135446Strhodes isc_result_t result; 1767135446Strhodes dns_difftuple_t *p[2]; 1768135446Strhodes int i, t; 1769153816Sdougb isc_boolean_t append; 1770153816Sdougb 1771135446Strhodes CHECK(dns_diff_sort(&diff[0], rdata_order)); 1772135446Strhodes CHECK(dns_diff_sort(&diff[1], rdata_order)); 1773135446Strhodes 1774135446Strhodes for (;;) { 1775135446Strhodes p[0] = ISC_LIST_HEAD(diff[0].tuples); 1776135446Strhodes p[1] = ISC_LIST_HEAD(diff[1].tuples); 1777135446Strhodes if (p[0] == NULL && p[1] == NULL) 1778135446Strhodes break; 1779135446Strhodes 1780135446Strhodes for (i = 0; i < 2; i++) 1781135446Strhodes if (p[!i] == NULL) { 1782135446Strhodes ISC_LIST_UNLINK(diff[i].tuples, p[i], link); 1783135446Strhodes ISC_LIST_APPEND(r->tuples, p[i], link); 1784135446Strhodes goto next; 1785135446Strhodes } 1786135446Strhodes t = rdata_order(&p[0], &p[1]); 1787135446Strhodes if (t < 0) { 1788135446Strhodes ISC_LIST_UNLINK(diff[0].tuples, p[0], link); 1789135446Strhodes ISC_LIST_APPEND(r->tuples, p[0], link); 1790135446Strhodes goto next; 1791135446Strhodes } 1792135446Strhodes if (t > 0) { 1793135446Strhodes ISC_LIST_UNLINK(diff[1].tuples, p[1], link); 1794135446Strhodes ISC_LIST_APPEND(r->tuples, p[1], link); 1795135446Strhodes goto next; 1796135446Strhodes } 1797135446Strhodes INSIST(t == 0); 1798135446Strhodes /* 1799153816Sdougb * Identical RRs in both databases; skip them both 1800153816Sdougb * if the ttl differs. 1801135446Strhodes */ 1802153816Sdougb append = ISC_TF(p[0]->ttl != p[1]->ttl); 1803135446Strhodes for (i = 0; i < 2; i++) { 1804135446Strhodes ISC_LIST_UNLINK(diff[i].tuples, p[i], link); 1805153816Sdougb if (append) { 1806153816Sdougb ISC_LIST_APPEND(r->tuples, p[i], link); 1807153816Sdougb } else { 1808153816Sdougb dns_difftuple_free(&p[i]); 1809153816Sdougb } 1810135446Strhodes } 1811135446Strhodes next: ; 1812135446Strhodes } 1813135446Strhodes result = ISC_R_SUCCESS; 1814135446Strhodes failure: 1815135446Strhodes return (result); 1816135446Strhodes} 1817135446Strhodes 1818135446Strhodes/* 1819135446Strhodes * Compare the databases 'dba' and 'dbb' and generate a journal 1820135446Strhodes * entry containing the changes to make 'dba' from 'dbb' (note 1821135446Strhodes * the order). This journal entry will consist of a single, 1822135446Strhodes * possibly very large transaction. 1823135446Strhodes */ 1824135446Strhodes 1825135446Strhodesisc_result_t 1826135446Strhodesdns_db_diff(isc_mem_t *mctx, 1827135446Strhodes dns_db_t *dba, dns_dbversion_t *dbvera, 1828135446Strhodes dns_db_t *dbb, dns_dbversion_t *dbverb, 1829135446Strhodes const char *journal_filename) 1830135446Strhodes{ 1831135446Strhodes dns_db_t *db[2]; 1832135446Strhodes dns_dbversion_t *ver[2]; 1833135446Strhodes dns_dbiterator_t *dbit[2] = { NULL, NULL }; 1834135446Strhodes isc_boolean_t have[2] = { ISC_FALSE, ISC_FALSE }; 1835135446Strhodes dns_fixedname_t fixname[2]; 1836135446Strhodes isc_result_t result, itresult[2]; 1837135446Strhodes dns_diff_t diff[2], resultdiff; 1838135446Strhodes int i, t; 1839135446Strhodes dns_journal_t *journal = NULL; 1840135446Strhodes 1841135446Strhodes db[0] = dba, db[1] = dbb; 1842135446Strhodes ver[0] = dbvera, ver[1] = dbverb; 1843135446Strhodes 1844135446Strhodes dns_diff_init(mctx, &diff[0]); 1845135446Strhodes dns_diff_init(mctx, &diff[1]); 1846135446Strhodes dns_diff_init(mctx, &resultdiff); 1847135446Strhodes 1848135446Strhodes dns_fixedname_init(&fixname[0]); 1849135446Strhodes dns_fixedname_init(&fixname[1]); 1850135446Strhodes 1851135446Strhodes result = dns_journal_open(mctx, journal_filename, ISC_TRUE, &journal); 1852135446Strhodes if (result != ISC_R_SUCCESS) 1853135446Strhodes return (result); 1854135446Strhodes 1855135446Strhodes result = dns_db_createiterator(db[0], ISC_FALSE, &dbit[0]); 1856135446Strhodes if (result != ISC_R_SUCCESS) 1857135446Strhodes goto cleanup_journal; 1858135446Strhodes result = dns_db_createiterator(db[1], ISC_FALSE, &dbit[1]); 1859135446Strhodes if (result != ISC_R_SUCCESS) 1860135446Strhodes goto cleanup_interator0; 1861135446Strhodes 1862135446Strhodes itresult[0] = dns_dbiterator_first(dbit[0]); 1863135446Strhodes itresult[1] = dns_dbiterator_first(dbit[1]); 1864135446Strhodes 1865135446Strhodes for (;;) { 1866135446Strhodes for (i = 0; i < 2; i++) { 1867135446Strhodes if (! have[i] && itresult[i] == ISC_R_SUCCESS) { 1868135446Strhodes CHECK(get_name_diff(db[i], ver[i], 0, dbit[i], 1869135446Strhodes dns_fixedname_name(&fixname[i]), 1870135446Strhodes i == 0 ? 1871135446Strhodes DNS_DIFFOP_ADD : 1872135446Strhodes DNS_DIFFOP_DEL, 1873135446Strhodes &diff[i])); 1874135446Strhodes itresult[i] = dns_dbiterator_next(dbit[i]); 1875135446Strhodes have[i] = ISC_TRUE; 1876135446Strhodes } 1877135446Strhodes } 1878135446Strhodes 1879135446Strhodes if (! have[0] && ! have[1]) { 1880135446Strhodes INSIST(ISC_LIST_EMPTY(diff[0].tuples)); 1881135446Strhodes INSIST(ISC_LIST_EMPTY(diff[1].tuples)); 1882135446Strhodes break; 1883135446Strhodes } 1884135446Strhodes 1885135446Strhodes for (i = 0; i < 2; i++) { 1886135446Strhodes if (! have[!i]) { 1887135446Strhodes ISC_LIST_APPENDLIST(resultdiff.tuples, 1888135446Strhodes diff[i].tuples, link); 1889135446Strhodes INSIST(ISC_LIST_EMPTY(diff[i].tuples)); 1890135446Strhodes have[i] = ISC_FALSE; 1891135446Strhodes goto next; 1892135446Strhodes } 1893135446Strhodes } 1894135446Strhodes 1895135446Strhodes t = dns_name_compare(dns_fixedname_name(&fixname[0]), 1896135446Strhodes dns_fixedname_name(&fixname[1])); 1897135446Strhodes if (t < 0) { 1898135446Strhodes ISC_LIST_APPENDLIST(resultdiff.tuples, 1899135446Strhodes diff[0].tuples, link); 1900135446Strhodes INSIST(ISC_LIST_EMPTY(diff[0].tuples)); 1901135446Strhodes have[0] = ISC_FALSE; 1902135446Strhodes continue; 1903135446Strhodes } 1904135446Strhodes if (t > 0) { 1905135446Strhodes ISC_LIST_APPENDLIST(resultdiff.tuples, 1906135446Strhodes diff[1].tuples, link); 1907135446Strhodes INSIST(ISC_LIST_EMPTY(diff[1].tuples)); 1908135446Strhodes have[1] = ISC_FALSE; 1909135446Strhodes continue; 1910135446Strhodes } 1911135446Strhodes INSIST(t == 0); 1912135446Strhodes CHECK(dns_diff_subtract(diff, &resultdiff)); 1913135446Strhodes INSIST(ISC_LIST_EMPTY(diff[0].tuples)); 1914135446Strhodes INSIST(ISC_LIST_EMPTY(diff[1].tuples)); 1915135446Strhodes have[0] = have[1] = ISC_FALSE; 1916135446Strhodes next: ; 1917135446Strhodes } 1918135446Strhodes if (itresult[0] != ISC_R_NOMORE) 1919135446Strhodes FAIL(itresult[0]); 1920135446Strhodes if (itresult[1] != ISC_R_NOMORE) 1921135446Strhodes FAIL(itresult[1]); 1922135446Strhodes 1923135446Strhodes if (ISC_LIST_EMPTY(resultdiff.tuples)) { 1924135446Strhodes isc_log_write(JOURNAL_DEBUG_LOGARGS(3), "no changes"); 1925135446Strhodes } else { 1926135446Strhodes CHECK(dns_journal_write_transaction(journal, &resultdiff)); 1927135446Strhodes } 1928135446Strhodes INSIST(ISC_LIST_EMPTY(diff[0].tuples)); 1929135446Strhodes INSIST(ISC_LIST_EMPTY(diff[1].tuples)); 1930135446Strhodes 1931135446Strhodes failure: 1932135446Strhodes dns_diff_clear(&resultdiff); 1933135446Strhodes dns_dbiterator_destroy(&dbit[1]); 1934135446Strhodes cleanup_interator0: 1935135446Strhodes dns_dbiterator_destroy(&dbit[0]); 1936135446Strhodes cleanup_journal: 1937135446Strhodes dns_journal_destroy(&journal); 1938135446Strhodes return (result); 1939135446Strhodes} 1940135446Strhodes 1941135446Strhodesisc_result_t 1942135446Strhodesdns_journal_compact(isc_mem_t *mctx, char *filename, isc_uint32_t serial, 1943135446Strhodes isc_uint32_t target_size) 1944135446Strhodes{ 1945135446Strhodes unsigned int i; 1946135446Strhodes journal_pos_t best_guess; 1947135446Strhodes journal_pos_t current_pos; 1948135446Strhodes dns_journal_t *j = NULL; 1949174187Sdougb dns_journal_t *new = NULL; 1950135446Strhodes journal_rawheader_t rawheader; 1951135446Strhodes unsigned int copy_length; 1952174187Sdougb int namelen; 1953135446Strhodes char *buf = NULL; 1954135446Strhodes unsigned int size = 0; 1955135446Strhodes isc_result_t result; 1956135446Strhodes unsigned int indexend; 1957174187Sdougb char newname[1024]; 1958174187Sdougb char backup[1024]; 1959174187Sdougb isc_boolean_t is_backup = ISC_FALSE; 1960135446Strhodes 1961174187Sdougb namelen = strlen(filename); 1962174187Sdougb if (namelen > 4 && strcmp(filename + namelen - 4, ".jnl") == 0) 1963174187Sdougb namelen -= 4; 1964135446Strhodes 1965174187Sdougb result = isc_string_printf(newname, sizeof(newname), "%.*s.jnw", 1966174187Sdougb namelen, filename); 1967174187Sdougb if (result != ISC_R_SUCCESS) 1968174187Sdougb return (result); 1969174187Sdougb 1970174187Sdougb result = isc_string_printf(backup, sizeof(backup), "%.*s.jbk", 1971174187Sdougb namelen, filename); 1972174187Sdougb if (result != ISC_R_SUCCESS) 1973174187Sdougb return (result); 1974174187Sdougb 1975174187Sdougb result = journal_open(mctx, filename, ISC_FALSE, ISC_FALSE, &j); 1976174187Sdougb if (result == ISC_R_NOTFOUND) { 1977174187Sdougb is_backup = ISC_TRUE; 1978174187Sdougb result = journal_open(mctx, backup, ISC_FALSE, ISC_FALSE, &j); 1979174187Sdougb } 1980174187Sdougb if (result != ISC_R_SUCCESS) 1981174187Sdougb return (result); 1982174187Sdougb 1983135446Strhodes if (JOURNAL_EMPTY(&j->header)) { 1984135446Strhodes dns_journal_destroy(&j); 1985135446Strhodes return (ISC_R_SUCCESS); 1986135446Strhodes } 1987186462Sdougb 1988135446Strhodes if (DNS_SERIAL_GT(j->header.begin.serial, serial) || 1989135446Strhodes DNS_SERIAL_GT(serial, j->header.end.serial)) { 1990135446Strhodes dns_journal_destroy(&j); 1991135446Strhodes return (ISC_R_RANGE); 1992135446Strhodes } 1993135446Strhodes 1994135446Strhodes /* 1995135446Strhodes * Cope with very small target sizes. 1996135446Strhodes */ 1997135446Strhodes indexend = sizeof(journal_rawheader_t) + 1998135446Strhodes j->header.index_size * sizeof(journal_rawpos_t); 1999135446Strhodes if (target_size < indexend * 2) 2000135446Strhodes target_size = target_size/2 + indexend; 2001135446Strhodes 2002135446Strhodes /* 2003135446Strhodes * See if there is any work to do. 2004135446Strhodes */ 2005135446Strhodes if ((isc_uint32_t) j->header.end.offset < target_size) { 2006135446Strhodes dns_journal_destroy(&j); 2007135446Strhodes return (ISC_R_SUCCESS); 2008135446Strhodes } 2009174187Sdougb 2010174187Sdougb CHECK(journal_open(mctx, newname, ISC_TRUE, ISC_TRUE, &new)); 2011186462Sdougb 2012135446Strhodes /* 2013135446Strhodes * Remove overhead so space test below can succeed. 2014135446Strhodes */ 2015135446Strhodes if (target_size >= indexend) 2016135446Strhodes target_size -= indexend; 2017135446Strhodes 2018135446Strhodes /* 2019135446Strhodes * Find if we can create enough free space. 2020135446Strhodes */ 2021135446Strhodes best_guess = j->header.begin; 2022135446Strhodes for (i = 0; i < j->header.index_size; i++) { 2023135446Strhodes if (POS_VALID(j->index[i]) && 2024135446Strhodes DNS_SERIAL_GE(serial, j->index[i].serial) && 2025135446Strhodes ((isc_uint32_t)(j->header.end.offset - j->index[i].offset) 2026135446Strhodes >= target_size / 2) && 2027135446Strhodes j->index[i].offset > best_guess.offset) 2028135446Strhodes best_guess = j->index[i]; 2029135446Strhodes } 2030135446Strhodes 2031135446Strhodes current_pos = best_guess; 2032135446Strhodes while (current_pos.serial != serial) { 2033135446Strhodes CHECK(journal_next(j, ¤t_pos)); 2034135446Strhodes if (current_pos.serial == j->header.end.serial) 2035135446Strhodes break; 2036135446Strhodes 2037135446Strhodes if (DNS_SERIAL_GE(serial, current_pos.serial) && 2038135446Strhodes ((isc_uint32_t)(j->header.end.offset - current_pos.offset) 2039135446Strhodes >= (target_size / 2)) && 2040135446Strhodes current_pos.offset > best_guess.offset) 2041135446Strhodes best_guess = current_pos; 2042135446Strhodes else 2043135446Strhodes break; 2044135446Strhodes } 2045135446Strhodes 2046135446Strhodes INSIST(best_guess.serial != j->header.end.serial); 2047135446Strhodes if (best_guess.serial != serial) 2048135446Strhodes CHECK(journal_next(j, &best_guess)); 2049135446Strhodes 2050135446Strhodes /* 2051174187Sdougb * We should now be roughly half target_size provided 2052174187Sdougb * we did not reach 'serial'. If not we will just copy 2053174187Sdougb * all uncommitted deltas regardless of the size. 2054135446Strhodes */ 2055135446Strhodes copy_length = j->header.end.offset - best_guess.offset; 2056135446Strhodes 2057135446Strhodes if (copy_length != 0) { 2058135446Strhodes /* 2059135446Strhodes * Copy best_guess to end into space just freed. 2060135446Strhodes */ 2061135446Strhodes size = 64*1024; 2062135446Strhodes if (copy_length < size) 2063135446Strhodes size = copy_length; 2064135446Strhodes buf = isc_mem_get(mctx, size); 2065135446Strhodes if (buf == NULL) { 2066135446Strhodes result = ISC_R_NOMEMORY; 2067135446Strhodes goto failure; 2068135446Strhodes } 2069186462Sdougb 2070174187Sdougb CHECK(journal_seek(j, best_guess.offset)); 2071174187Sdougb CHECK(journal_seek(new, indexend)); 2072135446Strhodes for (i = 0; i < copy_length; i += size) { 2073174187Sdougb unsigned int len = (copy_length - i) > size ? size : 2074135446Strhodes (copy_length - i); 2075135446Strhodes CHECK(journal_read(j, buf, len)); 2076174187Sdougb CHECK(journal_write(new, buf, len)); 2077135446Strhodes } 2078135446Strhodes 2079174187Sdougb CHECK(journal_fsync(new)); 2080135446Strhodes 2081135446Strhodes /* 2082135446Strhodes * Compute new header. 2083135446Strhodes */ 2084174187Sdougb new->header.begin.serial = best_guess.serial; 2085174187Sdougb new->header.begin.offset = indexend; 2086174187Sdougb new->header.end.serial = j->header.end.serial; 2087174187Sdougb new->header.end.offset = indexend + copy_length; 2088174187Sdougb 2089135446Strhodes /* 2090135446Strhodes * Update the journal header. 2091135446Strhodes */ 2092174187Sdougb journal_header_encode(&new->header, &rawheader); 2093174187Sdougb CHECK(journal_seek(new, 0)); 2094174187Sdougb CHECK(journal_write(new, &rawheader, sizeof(rawheader))); 2095174187Sdougb CHECK(journal_fsync(new)); 2096135446Strhodes 2097135446Strhodes /* 2098135446Strhodes * Build new index. 2099135446Strhodes */ 2100174187Sdougb current_pos = new->header.begin; 2101174187Sdougb while (current_pos.serial != new->header.end.serial) { 2102174187Sdougb index_add(new, ¤t_pos); 2103174187Sdougb CHECK(journal_next(new, ¤t_pos)); 2104135446Strhodes } 2105135446Strhodes 2106135446Strhodes /* 2107135446Strhodes * Write index. 2108135446Strhodes */ 2109174187Sdougb CHECK(index_to_disk(new)); 2110174187Sdougb CHECK(journal_fsync(new)); 2111135446Strhodes 2112174187Sdougb indexend = new->header.end.offset; 2113135446Strhodes } 2114174187Sdougb dns_journal_destroy(&new); 2115174187Sdougb 2116174187Sdougb /* 2117174187Sdougb * With a UFS file system this should just succeed and be atomic. 2118174187Sdougb * Any IXFR outs will just continue and the old journal will be 2119174187Sdougb * removed on final close. 2120174187Sdougb * 2121174187Sdougb * With MSDOS / NTFS we need to do a two stage rename triggered 2122174187Sdougb * bu EEXISTS. Hopefully all IXFR's that were active at the last 2123174187Sdougb * rename are now complete. 2124174187Sdougb */ 2125174187Sdougb if (rename(newname, filename) == -1) { 2126174187Sdougb if (errno == EACCES && !is_backup) { 2127174187Sdougb result = isc_file_remove(backup); 2128174187Sdougb if (result != ISC_R_SUCCESS && 2129174187Sdougb result != ISC_R_FILENOTFOUND) 2130174187Sdougb goto failure; 2131174187Sdougb if (rename(filename, backup) == -1) 2132174187Sdougb goto maperrno; 2133174187Sdougb if (rename(newname, filename) == -1) 2134174187Sdougb goto maperrno; 2135174187Sdougb (void)isc_file_remove(backup); 2136174187Sdougb } else { 2137174187Sdougb maperrno: 2138174187Sdougb result = ISC_R_FAILURE; 2139174187Sdougb goto failure; 2140174187Sdougb } 2141174187Sdougb } 2142186462Sdougb 2143135446Strhodes dns_journal_destroy(&j); 2144135446Strhodes result = ISC_R_SUCCESS; 2145135446Strhodes 2146135446Strhodes failure: 2147174187Sdougb (void)isc_file_remove(newname); 2148135446Strhodes if (buf != NULL) 2149135446Strhodes isc_mem_put(mctx, buf, size); 2150135446Strhodes if (j != NULL) 2151135446Strhodes dns_journal_destroy(&j); 2152174187Sdougb if (new != NULL) 2153174187Sdougb dns_journal_destroy(&new); 2154135446Strhodes return (result); 2155135446Strhodes} 2156135446Strhodes 2157135446Strhodesstatic isc_result_t 2158135446Strhodesindex_to_disk(dns_journal_t *j) { 2159135446Strhodes isc_result_t result = ISC_R_SUCCESS; 2160135446Strhodes 2161135446Strhodes if (j->header.index_size != 0) { 2162135446Strhodes unsigned int i; 2163135446Strhodes unsigned char *p; 2164135446Strhodes unsigned int rawbytes; 2165135446Strhodes 2166135446Strhodes rawbytes = j->header.index_size * sizeof(journal_rawpos_t); 2167135446Strhodes 2168135446Strhodes p = j->rawindex; 2169135446Strhodes for (i = 0; i < j->header.index_size; i++) { 2170135446Strhodes encode_uint32(j->index[i].serial, p); 2171135446Strhodes p += 4; 2172135446Strhodes encode_uint32(j->index[i].offset, p); 2173135446Strhodes p += 4; 2174135446Strhodes } 2175135446Strhodes INSIST(p == j->rawindex + rawbytes); 2176135446Strhodes 2177135446Strhodes CHECK(journal_seek(j, sizeof(journal_rawheader_t))); 2178135446Strhodes CHECK(journal_write(j, j->rawindex, rawbytes)); 2179135446Strhodes } 2180135446Strhodesfailure: 2181135446Strhodes return (result); 2182135446Strhodes} 2183