journal.c revision 0c27b3fe77ac1d5094ba3521e8142d9e7973133f
/*
* Copyright (C) 1999-2002, 2004, 2005, 2007-2011, 2013-2016 Internet Systems Consortium, Inc. ("ISC")
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/* $Id: journal.c,v 1.120 2011/12/22 07:32:41 each Exp $ */
#include <config.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <dns/compress.h>
#include <dns/dbiterator.h>
#include <dns/fixedname.h>
#include <dns/rdataset.h>
#include <dns/rdatasetiter.h>
/*! \file
* \brief Journaling.
*
* A journal file consists of
*
* \li A fixed-size header of type journal_rawheader_t.
*
* \li 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.
*
* \li 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.
*/
/*%
* 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)
#define JOURNAL_SERIALSET 0x01U
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
}
{
if (result != ISC_R_SUCCESS)
goto nonode;
if (result != ISC_R_SUCCESS)
goto freenode;
if (result != ISC_R_SUCCESS)
goto freenode;
return (result);
return (result);
}
/* Journaling */
/*%
* 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 header is of a fixed size, with some spare room for future
* extensions.
*/
/*%
* The on-disk representation of the journal header.
* All numbers are stored in big-endian order.
*/
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];
/*% Source serial number. */
unsigned char sourceserial[4];
unsigned char flags;
} 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, 0, 0 };
typedef enum {
struct dns_journal {
unsigned int magic; /*%< JOUR */
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. */
/* 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
unsigned char flags = 0;
}
/*
* 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);
}
static isc_result_t
{
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 (create) {
"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);
}
/*
* 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 unchecked 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));
sizeof(journal_pos_t));
(void)isc_stdio_close(j->fp);
isc_mem_putanddetach(&j->mctx, j, sizeof(*j));
return (result);
}
{
char backup[1024];
if (result == ISC_R_NOTFOUND) {
namelen -= 4;
if (result != ISC_R_SUCCESS)
return (result);
journalp);
}
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 const *a = *ap;
dns_difftuple_t const *b = *bp;
int r;
switch (a->op) {
case DNS_DIFFOP_DEL:
case DNS_DIFFOP_DELRESIGN:
aop = 1;
break;
case DNS_DIFFOP_ADD:
case DNS_DIFFOP_ADDRESIGN:
aop = 0;
break;
default:
INSIST(0);
}
switch (b->op) {
case DNS_DIFFOP_DEL:
case DNS_DIFFOP_DELRESIGN:
bop = 1;
break;
case DNS_DIFFOP_ADD:
case DNS_DIFFOP_ADDRESIGN:
bop = 0;
break;
default:
INSIST(0);
}
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);
return (ISC_R_NOMORE);
/*
* 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));
j->state == JOURNAL_STATE_INLINE);
/*
* 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));
j->state == JOURNAL_STATE_INLINE);
/*
* Just write out a updated header.
*/
if (j->state == JOURNAL_STATE_INLINE) {
CHECK(journal_fsync(j));
CHECK(journal_seek(j, 0));
CHECK(journal_fsync(j));
j->state = JOURNAL_STATE_WRITE;
return (ISC_R_SUCCESS);
}
/*
* Perform some basic consistency checks.
*/
if (j->x.n_soa != 2) {
"%s: malformed transaction: %d SOAs",
return (ISC_R_UNEXPECTED);
}
(bind8_compat &&
{
"%s: malformed transaction: serial number "
"would decrease", j->filename);
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));
if (j->state == JOURNAL_STATE_TRANSACTION) {
sizeof(journal_rawxhdr_t);
/*
* Update the transaction header.
*/
}
/*
* 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.
*/
CHECK(index_to_disk(j));
/*
* 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_putanddetach(&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 unchecked 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 == 2)
}
if (n_soa == 3)
n_soa = 1;
if (n_soa == 0) {
"%s: journal file corrupt: missing "
"initial SOA", j->filename);
}
if ((options & DNS_JOURNALOPT_RESIGN) != 0)
else
&tuple));
if (++n_put > 100) {
"%s: applying diff to database (%u)",
n_put = 0;
}
}
if (result == ISC_R_NOMORE)
if (n_put != 0) {
"%s: applying final diff to database (%u)",
}
return (result);
}
const char *filename)
{
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);
if (JOURNAL_EMPTY(&j->header))
else
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: %s: %s",
return (result);
}
/*
* Set up empty initial buffers for unchecked 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.
*/
}
}
void
j->state == JOURNAL_STATE_INLINE ||
j->state == JOURNAL_STATE_TRANSACTION);
if (j->state == JOURNAL_STATE_WRITE)
j->state = JOURNAL_STATE_INLINE;
}
return (ISC_FALSE);
return (ISC_TRUE);
}
/**************************************************************************/
/*
* 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.
*/
"%s: journal corrupt: empty transaction",
j->filename);
}
"%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 const *a = *ap;
dns_difftuple_t const *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
* if the ttl differs.
*/
for (i = 0; i < 2; i++) {
if (append) {
} else {
dns_difftuple_free(&p[i]);
}
}
next: ;
}
return (result);
}
static isc_result_t
{
int i, t;
dns_fixedname_init(&fixname[0]);
if (result != ISC_R_SUCCESS)
return (result);
if (result != ISC_R_SUCCESS)
goto cleanup_iterator;
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)
dns_dbiterator_destroy(&dbit[0]);
dns_diff_clear(&diff[0]);
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.
*/
{
return (result);
}
{
if (result != ISC_R_SUCCESS)
return (result);
}
else
}
return (result);
}
{
unsigned int i;
dns_journal_t *j = NULL;
unsigned int copy_length;
unsigned int size = 0;
unsigned int indexend;
char newname[1024];
char backup[1024];
namelen -= 4;
if (result != ISC_R_SUCCESS)
return (result);
if (result != ISC_R_SUCCESS)
return (result);
if (result == ISC_R_NOTFOUND) {
}
if (result != ISC_R_SUCCESS)
return (result);
if (JOURNAL_EMPTY(&j->header)) {
dns_journal_destroy(&j);
return (ISC_R_SUCCESS);
}
dns_journal_destroy(&j);
return (ISC_R_RANGE);
}
/*
* Cope with very small target sizes.
*/
indexend = sizeof(journal_rawheader_t) +
/*
* See if there is any work to do.
*/
dns_journal_destroy(&j);
return (ISC_R_SUCCESS);
}
/*
* Remove overhead so space test below can succeed.
*/
if (target_size >= indexend)
target_size -= indexend;
/*
* Find if we can create enough free space.
*/
for (i = 0; i < j->header.index_size; i++) {
>= target_size / 2) &&
best_guess = j->index[i];
}
break;
>= (target_size / 2)) &&
else
break;
}
/*
* We should now be roughly half target_size provided
* we did not reach 'serial'. If not we will just copy
* all uncommitted deltas regardless of the size.
*/
if (copy_length != 0) {
/*
* Copy best_guess to end into space just freed.
*/
if (copy_length < size)
size = copy_length;
goto failure;
}
for (i = 0; i < copy_length; i += size) {
(copy_length - i);
}
/*
* Compute new header.
*/
/*
* Update the journal header.
*/
/*
* Build new index.
*/
}
/*
* Write index.
*/
}
/*
* Close both journals before trying to rename files (this is
* necessary on WIN32).
*/
dns_journal_destroy(&j);
/*
* With a UFS file system this should just succeed and be atomic.
* Any IXFR outs will just continue and the old journal will be
* removed on final close.
*
* With MSDOS / NTFS we need to do a two stage rename, triggered
* by EEXIST. (If any IXFR's are running in other threads, however,
* this will fail, and the journal will not be compacted. But
* if so, hopefully they'll be finished by the next time we
* compact.)
*/
if (result != ISC_R_SUCCESS &&
goto failure;
goto maperrno;
goto maperrno;
(void)isc_file_remove(backup);
} else {
goto failure;
}
}
(void)isc_file_remove(newname);
if (j != NULL)
dns_journal_destroy(&j);
return (result);
}
static isc_result_t
index_to_disk(dns_journal_t *j) {
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;
}
}
return (result);
}