journal.c revision 368b37b616234fce3d23099eb180f1dd38e1fb62
/*
* Copyright (C) 1999, 2000 Internet Software Consortium.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
* DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
* INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
* FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
* WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: journal.c,v 1.62 2000/10/31 03:21:52 marka Exp $ */
#include <config.h>
#include <stdlib.h>
#include <dns/compress.h>
#include <dns/dbiterator.h>
#include <dns/fixedname.h>
#include <dns/rdatalist.h>
#include <dns/rdataset.h>
#include <dns/rdatasetiter.h>
/*
* When true, accept IXFR difference sequences where the
* SOA serial number does not change (BIND 8 sends such
* sequences).
*/
/**************************************************************************/
/*
* Miscellaneous utilities.
*/
#define JOURNAL_COMMON_LOGARGS \
#define JOURNAL_DEBUG_LOGARGS(n) \
/*
* It would be non-sensical (or at least obtuse) to use FAIL() with an
* ISC_R_SUCCESS code, but the test is there to keep the Solaris compiler
* from complaining about "end-of-loop code not reached".
*/
} while (0)
} while (0)
static inline isc_uint32_t
decode_uint32(unsigned char *p) {
return ((p[0] << 24) +
(p[1] << 16) +
(p[2] << 8) +
(p[3] << 0));
}
static inline void
}
static dns_rdatatype_t
dns_rdata_covers(rdata) : 0);
}
/*
* Locate the serial number within the SOA RDATA based
* on its position relative to the end of the data.
* (it starts 20 bytes from the end). This is a bit of
* a kludge, but the alternative approach of using
* dns_rdata_tostruct() and dns_rdata_fromstruct()
* would involve a lot of unnecessary work (like
* building domain names and allocating temporary memory)
* when all we really want to do is to change 32 bits of
* fixed-sized data. Besides, fromstruct_soa() is not
* implemented yet.
*/
}
void
}
{
if (result != ISC_R_SUCCESS)
goto nonode;
if (result != ISC_R_SUCCESS)
goto freenode;
if (result != ISC_R_SUCCESS)
goto freenode;
return (ISC_R_SUCCESS);
return (result);
}
/**************************************************************************/
/*
* Diffs, aka Pending Journal Entries.
*/
{
dns_difftuple_t *t;
unsigned int size;
unsigned char *datap;
/*
* Create a new tuple. The variable-size wire-format name data and
* rdata immediately follow the dns_difftuple_t structure
* in memory.
*/
if (t == NULL)
return (ISC_R_NOMEMORY);
datap = (unsigned char *)(t + 1);
dns_rdata_init(&t->rdata);
ISC_LINK_INIT(t, link);
t->magic = DNS_DIFFTUPLE_MAGIC;
*tp = t;
return (ISC_R_SUCCESS);
}
void
dns_difftuple_t *t = *tp;
dns_name_invalidate(&t->name);
t->magic = 0;
isc_mem_free(t->mctx, t);
}
}
void
}
void
dns_difftuple_t *t;
dns_difftuple_free(&t);
}
}
void
{
}
/* XXX this is O(N) */
void
{
/*
* Look for an existing tuple with the same owner name,
* rdata, and TTL. If we are doing an addition and find a
* deletion or vice versa, remove both the old and the
* new tuple since they cancel each other out (assuming
* that we never delete nonexistent data or add existing
* data).
*
* If we find an old update of the same kind as
* the one we are doing, there must be a programming
* error. We report it but try to continue anyway.
*/
{
{
"unexpected non-minimal diff");
} else {
}
break;
}
}
}
}
{
dns_difftuple_t *t;
while (t != NULL) {
/*
* Find the node.
* We create the node if it does not exist.
* This will cause an empty node to be created if the diff
* contains a deletion of an RR at a nonexistent name,
* but such diffs should never be created in the first
* place.
*/
/*
* Collect a contiguous set of updates with
* into a single rdatalist so that the
* database rrset merging/subtraction code
* can work more efficiently than if each
* RR were merged into / subtracted from
* the database separately.
*
* This is done by linking rdata structures from the
* diff into "rdatalist". This uses the rdata link
* field, not the diff link field, so the structure
* of the diff itself is not affected.
*/
while (t != NULL &&
{
"TTL differs in rdataset, "
"adjusting %lu -> %lu",
(unsigned long) t->ttl,
}
t = ISC_LIST_NEXT(t, link);
}
/*
* Convert the rdatalist into a rdataset.
*/
/*
* Merge the rdataset into the database.
*/
if (op == DNS_DIFFOP_ADD) {
0, &rds,
NULL);
} else if (op == DNS_DIFFOP_DEL) {
&rds,
NULL);
} else {
INSIST(0);
}
if (result == DNS_R_UNCHANGED) {
/*
* This will not happen when executing a
* dynamic update, because that code will
* generate strictly minimal diffs.
* It may happen when receiving an IXFR
* from a server that is not as careful.
* Issue a warning and continue.
*/
"update with no effect");
} else if (result == ISC_R_SUCCESS ||
result == DNS_R_NXRRSET) {
/*
* OK.
*/
} else {
}
}
}
return (ISC_R_SUCCESS);
return (result);
}
/* XXX this duplicates lots of code in dns_diff_apply(). */
void *add_private)
{
dns_difftuple_t *t;
while (t != NULL) {
{
t = ISC_LIST_NEXT(t, link);
}
/*
* Convert the rdatalist into a rdataset.
*/
if (result == DNS_R_UNCHANGED) {
"update with no effect");
} else if (result == ISC_R_SUCCESS ||
result == DNS_R_NXRRSET) {
/*
* OK.
*/
} else {
}
}
}
return (result);
}
/*
* XXX uses qsort(); a merge sort would be more natural for lists,
* and perhaps safer wrt thread stack overflow.
*/
unsigned int length = 0;
unsigned int i;
dns_difftuple_t **v;
dns_difftuple_t *p;
p != NULL;
p = ISC_LIST_NEXT(p, link))
length++;
if (length == 0)
return (ISC_R_SUCCESS);
if (v == NULL)
return (ISC_R_NOMEMORY);
i = 0;
for (i = 0; i < length; i++) {
v[i] = p;
}
for (i = 0; i < length; i++) {
}
return (ISC_R_SUCCESS);
}
/*
* Create an rdataset containing the single RR of the given
* tuple. The caller must allocate the the rdata, rdataset and
* an rdatalist structure for it to refer to.
*/
static isc_result_t
{
}
dns_difftuple_t *t;
unsigned int size = 2048;
return (ISC_R_NOMEMORY);
t = ISC_LIST_NEXT(t, link))
{
isc_region_t r;
if (result != ISC_R_SUCCESS) {
"diff_tuple_tordataset failed: %s",
goto cleanup;
}
/*
* Get rid of final newline.
*/
if (result == ISC_R_NOSPACE) {
size += 1024;
goto cleanup;
}
goto again;
}
if (result == ISC_R_SUCCESS) {
isc_buffer_usedregion(&buf, &r);
t->op == DNS_DIFFOP_ADD ?
(char *) r.base);
else
ISC_LOG_DEBUG(7),
"%s %.*s",
t->op == DNS_DIFFOP_ADD ?
"add" : "del",
} else
goto cleanup;
}
return (result);
}
/**************************************************************************/
/*
* Journalling.
*/
/*
* A journal file consists of
*
* - A fixed-size header of type journal_rawheader_t.
*
* - The index. This is an unordered array of index entries
* of type journal_rawpos_t giving the locations
* of some arbitrary subset of the journal's addressable
* transactions. The index entries are used as hints to
* speed up the process of locating a transaction with a given
* serial number. Unused index entries have an "offset"
* field of zero. The size of the index can vary between
* journal files, but does not change during the lifetime
* of a file. The size can be zero.
*
* - The journal data. This consists of one or more transactions.
* Each transaction begins with a transaction header of type
* journal_rawxhdr_t. The transaction header is followed by a
* sequence of RRs, similar in structure to an IXFR difference
* sequence (RFC1995). That is, the pre-transaction SOA,
* zero or more other deleted RRs, the post-transaction SOA,
* and zero or more other added RRs. Unlike in IXFR, each RR
* is prefixed with a 32-bit length.
*
* The journal data part grows as new transactions are
* appended to the file. Only those transactions
* whose serial number is current-(2^31-1) to current
* are considered "addressable" and may be pointed
* to from the header or index. They may be preceded
* by old transactions that are no longer addressable,
* and they may be followed by transactions that were
* appended to the journal but never committed by updating
* the "end" position in the header. The latter will
* be overwritten when new transactions are added.
*/
/*
* On-disk representation of a "pointer" to a journal entry.
* These are used in the journal header to locate the beginning
* and end of the journal, and in the journal index to locate
* other transactions.
*/
typedef struct {
/*
* XXXRTH Should offset be 8 bytes?
* XXXDCL ... probably, since isc_offset_t is 8 bytes on many OSs.
* XXXAG ... but we will not be able to seek >2G anyway on many
* platforms as long as we are using fseek() rather
* than lseek().
*/
/*
* The on-disk representation of the journal header.
* All numbers are stored in big-endian order.
*/
/*
* The header is of a fixed size, with some spare room for future
* extensions.
*/
typedef union {
struct {
/* File format version ID. */
unsigned char format[16];
/* Position of the first addressable transaction */
/* Position of the next (yet nonexistent) transaction. */
/* Number of index entries following the header. */
unsigned char index_size[4];
} h;
/* Pad the header to a fixed size. */
unsigned char pad[JOURNAL_HEADER_SIZE];
/*
* The on-disk representation of the transaction header.
* There is one of these at the beginning of each transaction.
*/
typedef struct {
/*
* The on-disk representation of the RR header.
* There is one of these at the beginning of each RR.
*/
typedef struct {
/*
* The in-core representation of the journal header.
*/
typedef struct {
typedef struct {
unsigned char format[16];
/*
* The in-core representation of the transaction header.
*/
typedef struct {
/*
* The in-core representation of the RR header.
*/
typedef struct {
/*
* Initial contents to store in the header of a newly created
* journal file.
*
* The header starts with the magic string ";BIND LOG V9\n"
* to identify the file as a BIND 9 journal file. An ASCII
* identification string is used rather than a binary magic
* number to be consistent with BIND 8 (BIND 8 journal files
* are ASCII text files).
*/
static journal_header_t
initial_journal_header = { ";BIND LOG V9\n", { 0, 0 }, { 0, 0 }, 0 };
typedef enum {
struct dns_journal {
unsigned int magic; /* JOUR */
const char *filename; /* Journal file name */
unsigned char *rawindex; /* In-core buffer for journal
index in on-disk format */
/* Current transaction state (when writing). */
struct {
unsigned int n_soa; /* Number of SOAs seen */
} x;
/* Iteration state (when reading). */
struct {
/* These define the part of the journal we iterate over. */
transaction */
/* The rest is iterator state. */
unsigned int xsize; /* Size of transaction data */
unsigned int xpos; /* Current position in it */
} it;
};
static void
}
static void
}
static void
}
static void
}
/*
* Journal file I/O subroutines, with error checking and reporting.
*/
static isc_result_t
if (result != ISC_R_SUCCESS) {
"%s: seek: %s", j->filename,
return (ISC_R_UNEXPECTED);
}
return (ISC_R_SUCCESS);
}
static isc_result_t
if (result != ISC_R_SUCCESS) {
return (ISC_R_NOMORE);
"%s: read: %s",
return (ISC_R_UNEXPECTED);
}
return (ISC_R_SUCCESS);
}
static isc_result_t
if (result != ISC_R_SUCCESS) {
"%s: write: %s",
return (ISC_R_UNEXPECTED);
}
return (ISC_R_SUCCESS);
}
static isc_result_t
journal_fsync(dns_journal_t *j) {
if (result != ISC_R_SUCCESS) {
"%s: flush: %s",
return (ISC_R_UNEXPECTED);
}
if (result != ISC_R_SUCCESS) {
"%s: fsync: %s",
return (ISC_R_UNEXPECTED);
}
return (ISC_R_SUCCESS);
}
/*
*/
static isc_result_t
if (result != ISC_R_SUCCESS)
return (result);
return (ISC_R_SUCCESS);
}
static isc_result_t
{
}
/*
* Read an RR header at the current file position.
*/
static isc_result_t
if (result != ISC_R_SUCCESS)
return (result);
return (ISC_R_SUCCESS);
}
static isc_result_t
int size;
void *mem; /* Memory for temporary index image. */
if (result != ISC_R_SUCCESS) {
"%s: create: %s",
return (ISC_R_UNEXPECTED);
}
size = sizeof(journal_rawheader_t) +
index_size * sizeof(journal_rawpos_t);
(void)isc_stdio_close(fp);
(void)isc_file_remove(filename);
return (ISC_R_NOMEMORY);
}
if (result != ISC_R_SUCCESS) {
"%s: write: %s",
(void)isc_stdio_close(fp);
(void)isc_file_remove(filename);
return (ISC_R_UNEXPECTED);
}
if (result != ISC_R_SUCCESS) {
"%s: close: %s",
(void)isc_file_remove(filename);
return (ISC_R_UNEXPECTED);
}
return (ISC_R_SUCCESS);
}
dns_journal_t **journalp) {
dns_journal_t *j;
j = isc_mem_get(mctx, sizeof(*j));
if (j == NULL)
return (ISC_R_NOMEMORY);
j->state = JOURNAL_STATE_INVALID;
if (result == ISC_R_FILENOTFOUND) {
if (write) {
"journal file %s does not exist, "
"creating it",
j->filename);
/*
* Retry.
*/
} else {
}
}
if (result != ISC_R_SUCCESS) {
"%s: open: %s",
}
/*
*/
j->magic = DNS_JOURNAL_MAGIC;
CHECK(journal_seek(j, 0));
sizeof(initial_journal_header.format)) != 0) {
"%s: journal format not recognized",
j->filename);
}
/*
* When opening a journal for reading, it is not supposed to
* be empty - if there are no transactions, the file should
* not exist.
*/
"%s: journal unexpectedly empty",
j->filename);
}
/*
* If there is an index, read the raw index into a dynamically
* allocated buffer and then convert it into a cooked index.
*/
if (j->header.index_size != 0) {
unsigned int i;
unsigned int rawbytes;
unsigned char *p;
sizeof(journal_pos_t));
p = j->rawindex;
for (i = 0; i < j->header.index_size; i++) {
p += 4;
p += 4;
}
}
/*
* Initialize the iterator.
*/
/*
* Set up empty initial buffers for uncheched and checked
* wire format RR data. They will be reallocated
* later.
*/
j->state =
*journalp = j;
return (ISC_R_SUCCESS);
j->magic = 0;
sizeof(journal_rawpos_t));
}
(void)isc_stdio_close(j->fp);
isc_mem_put(j->mctx, j, sizeof(*j));
return (result);
}
/*
* A comparison function defining the sorting order for
* entries in the IXFR-style journal file.
*
* The IXFR format requires that deletions are sorted before
* additions, and within either one, SOA records are sorted
* before others.
*
* Also sort the non-SOA records by type as a courtesy to the
* server receiving the IXFR - it may help reduce the amount of
* rdataset merging it has to do.
*/
static int
dns_difftuple_t *a = *ap;
dns_difftuple_t *b = *bp;
int r;
if (r != 0)
return (r);
if (r != 0)
return (r);
return (r);
}
/*
* Advance '*pos' to the next journal transaction.
*
* Requires:
* *pos refers to a valid journal transaction.
*
* Ensures:
* When ISC_R_SUCCESS is returned,
* *pos refers to the next journal transaction.
*
* Returns one of:
*
* ISC_R_SUCCESS
* ISC_R_NOMORE *pos pointed at the last transaction
* Other results due to file errors are possible.
*/
static isc_result_t
REQUIRE(DNS_JOURNAL_VALID(j));
if (result != ISC_R_SUCCESS)
return (result);
/*
* Read the header of the current transaction.
* This will return ISC_R_NOMORE if we are at EOF.
*/
if (result != ISC_R_SUCCESS)
return (result);
/*
* Check serial number consistency.
*/
"%s: journal file corrupt: "
"expected serial %u, got %u",
return (ISC_R_UNEXPECTED);
}
/*
* Check for offset wraparound.
*/
"%s: offset too large", j->filename);
return (ISC_R_UNEXPECTED);
}
return (ISC_R_SUCCESS);
}
/*
* If the index of the journal 'j' contains an entry "better"
* than '*best_guess', replace '*best_guess' with it.
*
* "Better" means having a serial number closer to 'serial'
* but not greater than 'serial'.
*/
static void
unsigned int i;
return;
for (i = 0; i < j->header.index_size; i++) {
*best_guess = j->index[i];
}
}
/*
* Add a new index entry. If there is no room, make room by removing
* the odd-numbered entries and compacting the others into the first
* half of the index. This decimates old index entries exponentially
* over time, so that the index always contains a much larger fraction
* of recent serial numbers than of old ones. This is deliberate -
* most index searches are for outgoing IXFR, and IXFR tends to request
* recent versions more often than old ones.
*/
static void
unsigned int i;
return;
/*
* Search for a vacant position.
*/
for (i = 0; i < j->header.index_size; i++) {
break;
}
if (i == j->header.index_size) {
unsigned int k = 0;
/*
* Found no vacant position. Make some room.
*/
}
i = k; /* 'i' identifies the first vacant position. */
while (k < j->header.index_size) {
POS_INVALIDATE(j->index[k]);
k++;
}
}
/*
* Store the new index entry.
*/
}
/*
* Invalidate any existing index entries that could become
* ambiguous when a new transaction with number 'serial' is added.
*/
static void
unsigned int i;
return;
for (i = 0; i < j->header.index_size; i++) {
POS_INVALIDATE(j->index[i]);
}
}
/*
* Try to find a transaction with initial serial number 'serial'
* in the journal 'j'.
*
* If found, store its position at '*pos' and return ISC_R_SUCCESS.
*
* If 'serial' is current (= the ending serial number of the
* last transaction in the journal), set '*pos' to
* the position immediately following the last transaction and
* return ISC_R_SUCCESS.
*
* If 'serial' is within the range of addressable serial numbers
* covered by the journal but that particular serial number is missing
* (from the journal, not just from the index), return ISC_R_NOTFOUND.
*
* If 'serial' is outside the range of addressable serial numbers
* covered by the journal, return ISC_R_RANGE.
*
*/
static isc_result_t
REQUIRE(DNS_JOURNAL_VALID(j));
return (ISC_R_RANGE);
return (ISC_R_RANGE);
return (ISC_R_SUCCESS);
}
return (ISC_R_NOTFOUND);
if (result != ISC_R_SUCCESS)
return (result);
}
*pos = current_pos;
return (ISC_R_SUCCESS);
}
REQUIRE(DNS_JOURNAL_VALID(j));
/*
* Find the file offset where the new transaction should
* be written, and seek there.
*/
if (JOURNAL_EMPTY(&j->header)) {
offset = sizeof(journal_rawheader_t) +
} else {
}
j->x.n_soa = 0;
/*
* Write a dummy transaction header of all zeroes to reserve
* space. It will be filled in when the transaction is
* finished.
*/
return (result);
}
dns_difftuple_t *t;
unsigned int size;
/*
* Pass 1: determine the buffer size needed, and
* keep track of SOA serial numbers.
*/
size = 0;
t = ISC_LIST_NEXT(t, link))
{
if (j->x.n_soa < 2)
dns_soa_getserial(&t->rdata);
j->x.n_soa++;
}
size += sizeof(journal_rawrrhdr_t);
size += 10;
}
return (ISC_R_NOMEMORY);
/*
* Pass 2. Write RRs to buffer.
*/
t = ISC_LIST_NEXT(t, link))
{
/*
* Write the RR header.
*/
/*
* Write the owner name, RR header, and RR data.
*/
}
/*
* Write the buffer contents to the journal file.
*/
return (result);
}
REQUIRE(DNS_JOURNAL_VALID(j));
/*
* Perform some basic consistency checks.
*/
if (j->x.n_soa != 2) {
"malformed transaction: %d SOAs",
j->x.n_soa);
return (ISC_R_UNEXPECTED);
}
(bind8_compat &&
{
"malformed transaction: serial number "
"would decrease");
return (ISC_R_UNEXPECTED);
}
if (! JOURNAL_EMPTY(&j->header)) {
"malformed transaction: "
"%s last serial %u != "
"transaction first serial %u",
j->filename,
return (ISC_R_UNEXPECTED);
}
}
/*
* Some old journal entries may become non-addressable
* when we increment the current serial number. Purge them
* by stepping header.begin forward to the first addressable
* transaction. Also purge them from the index.
*/
if (! JOURNAL_EMPTY(&j->header)) {
}
}
#ifdef notyet
force_dump(...);
}
#endif
/*
* Commit the transaction data to stable storage.
*/
CHECK(journal_fsync(j));
/*
* Update the transaction header.
*/
sizeof(journal_rawxhdr_t),
/*
* Update the journal header.
*/
if (JOURNAL_EMPTY(&j->header)) {
}
CHECK(journal_seek(j, 0));
/*
* Update the index.
*/
/*
* Convert the index into on-disk format and write
* it to disk.
*/
if (j->header.index_size != 0) {
unsigned int i;
unsigned char *p;
unsigned int rawbytes;
p = j->rawindex;
for (i = 0; i < j->header.index_size; i++) {
p += 4;
p += 4;
}
}
/*
* Commit the header to stable storage.
*/
CHECK(journal_fsync(j));
/*
* We no longer have a transaction open.
*/
j->state = JOURNAL_STATE_WRITE;
return (result);
}
CHECK(dns_journal_commit(j));
return (result);
}
void
dns_journal_t *j = *journalp;
REQUIRE(DNS_JOURNAL_VALID(j));
sizeof(journal_rawpos_t));
sizeof(journal_pos_t));
(void)isc_stdio_close(j->fp);
j->magic = 0;
isc_mem_put(j->mctx, j, sizeof(*j));
}
/*
* Roll the open journal 'j' into the database 'db'.
* A new database version will be created.
*/
/* XXX Share code with incoming IXFR? */
static isc_result_t
unsigned int n_soa = 0;
unsigned int n_put = 0;
REQUIRE(DNS_JOURNAL_VALID(j));
/*
* Set up empty initial buffers for uncheched and checked
* wire format transaction data. They will be reallocated
* later.
*/
/*
* Create the new database version.
*/
/*
* Get the current database SOA serial number.
*/
/*
* Locate a journal entry for the current database serial.
*/
/*
* XXX do more drastic things, like marking zone stale,
* if this fails?
*/
/*
* XXXRTH The zone code should probably mark the zone as bad and
* scream loudly into the log if this is a dynamic update
* log reply that failed.
*/
if (db_serial == end_serial)
for (result = dns_journal_first_rr(j);
result == ISC_R_SUCCESS;
result = dns_journal_next_rr(j))
{
n_soa++;
if (n_soa == 3)
n_soa = 1;
if (n_soa == 0) {
"%s: journal file corrupt: missing "
"initial SOA", j->filename);
}
if (++n_put > 100) {
"applying diff to database");
n_put = 0;
}
}
if (result == ISC_R_NOMORE)
if (n_put != 0) {
"applying final diff to database");
}
return (result);
}
dns_journal_t *j;
j = NULL;
if (result == ISC_R_NOTFOUND) {
"no journal file, but that's OK");
return (DNS_R_NOJOURNAL);
}
if (result != ISC_R_SUCCESS)
return (result);
dns_journal_destroy(&j);
return (result);
}
dns_journal_t *j;
unsigned int n_soa = 0;
unsigned int n_put = 0;
j = NULL;
if (result == ISC_R_NOTFOUND) {
return (DNS_R_NOJOURNAL);
}
if (result != ISC_R_SUCCESS) {
"journal open failure");
return (result);
}
/*
* Set up empty initial buffers for uncheched and checked
* wire format transaction data. They will be reallocated
* later.
*/
for (result = dns_journal_first_rr(j);
result == ISC_R_SUCCESS;
result = dns_journal_next_rr(j))
{
n_soa++;
if (n_soa == 3)
n_soa = 1;
if (n_soa == 0) {
"%s: journal file corrupt: missing "
"initial SOA", j->filename);
}
if (++n_put > 100) {
n_put = 0;
if (result != ISC_R_SUCCESS)
break;
}
}
if (result == ISC_R_NOMORE)
if (n_put != 0) {
}
goto cleanup;
"%s: cannot print: journal file corrupt", j->filename);
dns_journal_destroy(&j);
return (result);
}
/**************************************************************************/
/*
* Miscellaneous accessors.
*/
}
}
/**************************************************************************/
/*
* Iteration support.
*
* When serving an outgoing IXFR, we transmit a part the journal starting
* at the serial number in the IXFR request and ending at the serial
* number that is current when the IXFR request arrives. The ending
* serial number is not necessarily at the end of the journal:
* the journal may grow while the IXFR is in progress, but we stop
* when we reach the serial number that was current when the IXFR started.
*/
/*
* Make sure the buffer 'b' is has at least 'size' bytes
* allocated, and clear it.
*
* Requires:
* Either b->base is NULL, or it points to b->length bytes of memory
* previously allocated by isc_mem_get().
*/
static isc_result_t
return (ISC_R_NOMEMORY);
}
isc_buffer_clear(b);
return (ISC_R_SUCCESS);
}
{
}
/*
* Seek to the beginning of the first transaction we are
* interested in.
*/
return (read_one_rr(j));
return (result);
}
static isc_result_t
read_one_rr(dns_journal_t *j) {
unsigned int rdlen;
return (ISC_R_NOMORE);
/*
* We are at a transaction boundary.
* Read another transaction header.
*/
"journal corrupt: empty transaction");
}
"%s: journal file corrupt: "
"expected serial %u, got %u",
j->filename,
}
}
/*
* Read an RR.
*/
/*
* Perform a sanity check on the journal RR size.
* The smallest possible RR has a 1-byte owner name
* and a 10-byte header. The largest possible
* RR has 65535 bytes of data, a header, and a maximum-
* size owner name, well below 70 k total.
*/
"%s: journal corrupt: impossible RR size "
}
/*
* The target buffer is made the same size
* as the source buffer, with the assumption that when
* no compression in present, the output of dns_*_fromwire()
* is no larger than the input.
*/
/*
* Parse the owner name. We don't know where it
* ends yet, so we make the entire "remaining"
* part of the buffer "active".
*/
/*
* Check that the RR header is there, and parse it.
*/
/*
* Parse the rdata.
*/
if (rdtype == dns_rdatatype_soa) {
/* XXX could do additional consistency checks here */
}
return (result);
}
}
void
dns_rdata_t **rdata)
{
}
/**************************************************************************/
/*
* Generating diffs from databases
*/
/*
* Construct a diff containing all the RRs at the current name of the
* database iterator 'dbit' in database 'db', version 'ver'.
* Set '*name' to the current name, and append the diff to 'diff'.
* All new tuples will have the operation 'op'.
*
* Requires: 'name' must have buffer large enough to hold the name.
* Typically, a dns_fixedname_t would be used.
*/
static isc_result_t
{
if (result != ISC_R_SUCCESS)
return (result);
if (result != ISC_R_SUCCESS)
goto cleanup_node;
result == ISC_R_SUCCESS;
{
result == ISC_R_SUCCESS;
{
&tuple);
if (result != ISC_R_SUCCESS) {
goto cleanup_iterator;
}
}
if (result != ISC_R_NOMORE)
goto cleanup_iterator;
}
if (result != ISC_R_NOMORE)
goto cleanup_iterator;
return (result);
}
/*
* Comparison function for use by dns_diff_subtract when sorting
* the diffs to be subtracted. The sort keys are the rdata type
* and the rdata itself. The owner name is ignored, because
* it is known to be the same for all tuples.
*/
static int
dns_difftuple_t *a = *ap;
dns_difftuple_t *b = *bp;
int r;
if (r != 0)
return (r);
return (r);
}
static isc_result_t
dns_difftuple_t *p[2];
int i, t;
for (;;) {
break;
for (i = 0; i < 2; i++)
if (p[!i] == NULL) {
goto next;
}
t = rdata_order(&p[0], &p[1]);
if (t < 0) {
goto next;
}
if (t > 0) {
goto next;
}
INSIST(t == 0);
/*
* Identical RRs in both databases; skip them both.
*/
for (i = 0; i < 2; i++) {
dns_difftuple_free(&p[i]);
}
next: ;
}
return (result);
}
/*
* Compare the databases 'dba' and 'dbb' and generate a journal
* entry containing the changes to make 'dba' from 'dbb' (note
* the order). This journal entry will consist of a single,
* possibly very large transaction.
*/
const char *journal_filename)
{
int i, t;
dns_fixedname_init(&fixname[0]);
for (;;) {
for (i = 0; i < 2; i++) {
dns_fixedname_name(&fixname[i]),
i == 0 ?
&diff[i]));
}
}
break;
}
for (i = 0; i < 2; i++) {
if (! have[!i]) {
goto next;
}
}
if (t < 0) {
continue;
}
if (t > 0) {
continue;
}
INSIST(t == 0);
next: ;
}
if (itresult[0] != ISC_R_NOMORE)
} else {
}
dns_dbiterator_destroy(&dbit[0]);
return (result);
}