#include <isc/file.h>
#include <isc/mem.h>
#include <isc/print.h>
+#include <isc/serial.h>
#include <isc/stdio.h>
#include <isc/string.h>
#include <isc/util.h>
*/
#define JOURNAL_HEADER_SIZE 64 /* Bytes. */
+typedef enum {
+ XHDR_VERSION1 = 1,
+ XHDR_VERSION2 = 2,
+} xhdr_version_t;
+
/*%
* The on-disk representation of the journal header.
* All numbers are stored in big-endian order.
} journal_rawheader_t;
/*%
- * The on-disk representation of the transaction header.
+ * The on-disk representation of the transaction header, version 2.
* There is one of these at the beginning of each transaction.
*/
typedef struct {
unsigned char serial1[4]; /*%< SOA serial after update. */
} journal_rawxhdr_t;
+/*%
+ * Old-style raw transaction header, version 1, used for backward
+ * compatibility mode.
+ */
+typedef struct {
+ unsigned char size[4];
+ unsigned char serial0[4];
+ unsigned char serial1[4];
+} journal_rawxhdr_ver1_t;
+
/*%
* The on-disk representation of the RR header.
* There is one of these at the beginning of each RR.
* Initial contents to store in the header of a newly created
* journal file.
*
- * The header starts with the magic string ";BIND LOG V9\n"
+ * The header starts with the magic string ";BIND LOG V9.2\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 = {
+static journal_header_t journal_header_ver1 = {
";BIND LOG V9\n", { 0, 0 }, { 0, 0 }, 0, 0, 0
};
+static journal_header_t initial_journal_header = {
+ ";BIND LOG V9.2\n", { 0, 0 }, { 0, 0 }, 0, 0, 0
+};
#define JOURNAL_EMPTY(h) ((h)->begin.offset == (h)->end.offset)
unsigned int magic; /*%< JOUR */
isc_mem_t *mctx; /*%< Memory context */
journal_state_t state;
- char *filename; /*%< Journal file name */
- FILE *fp; /*%< File handle */
- isc_offset_t offset; /*%< Current file offset */
- journal_header_t header; /*%< In-core journal header */
- unsigned char *rawindex; /*%< In-core buffer for journal index
- * in on-disk format */
- journal_pos_t *index; /*%< In-core journal index */
+ xhdr_version_t xhdr_version; /*%< Expected transaction header version */
+ bool header_ver1; /*%< Transaction header compatibility
+ * mode is allowed */
+ bool recovered; /*%< A recoverable error was found
+ * while reading the journal */
+ char *filename; /*%< Journal file name */
+ FILE *fp; /*%< File handle */
+ isc_offset_t offset; /*%< Current file offset */
+ journal_xhdr_t curxhdr; /*%< Current transaction header */
+ journal_header_t header; /*%< In-core journal header */
+ unsigned char *rawindex; /*%< In-core buffer for journal index
+ * in on-disk format */
+ journal_pos_t *index; /*%< In-core journal index */
/*% Current transaction state (when writing). */
struct {
journal_pos_t bpos; /*%< Position before first, */
journal_pos_t epos; /*%< and after last transaction */
/* The rest is iterator state. */
- uint32_t current_serial; /*%< Current SOA serial
- * */
+ uint32_t current_serial; /*%< Current SOA serial */
isc_buffer_t source; /*%< Data from disk */
isc_buffer_t target; /*%< Data from _fromwire check */
dns_decompress_t dctx; /*%< Dummy decompression ctx */
static void
journal_header_decode(journal_rawheader_t *raw, journal_header_t *cooked) {
INSIST(sizeof(cooked->format) == sizeof(raw->h.format));
+
memmove(cooked->format, raw->h.format, sizeof(cooked->format));
journal_pos_decode(&raw->h.begin, &cooked->begin);
journal_pos_decode(&raw->h.end, &cooked->end);
unsigned char flags = 0;
INSIST(sizeof(cooked->format) == sizeof(raw->h.format));
+
memset(raw->pad, 0, sizeof(raw->pad));
memmove(raw->h.format, cooked->format, sizeof(raw->h.format));
journal_pos_encode(&raw->h.begin, &cooked->begin);
static isc_result_t
journal_fsync(dns_journal_t *j) {
isc_result_t result;
+
result = isc_stdio_flush(j->fp);
if (result != ISC_R_SUCCESS) {
isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR,
/*
* Read/write a transaction header at the current file position.
*/
-
static isc_result_t
journal_read_xhdr(dns_journal_t *j, journal_xhdr_t *xhdr) {
- journal_rawxhdr_t raw;
isc_result_t result;
- result = journal_read(j, &raw, sizeof(raw));
- if (result != ISC_R_SUCCESS) {
- return (result);
+
+ switch (j->xhdr_version) {
+ case XHDR_VERSION1: {
+ journal_rawxhdr_ver1_t raw;
+ result = journal_read(j, &raw, sizeof(raw));
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ xhdr->size = decode_uint32(raw.size);
+ xhdr->count = 0;
+ xhdr->serial0 = decode_uint32(raw.serial0);
+ xhdr->serial1 = decode_uint32(raw.serial1);
+ j->curxhdr = *xhdr;
+ return (ISC_R_SUCCESS);
+ }
+
+ case XHDR_VERSION2: {
+ journal_rawxhdr_t raw;
+ result = journal_read(j, &raw, sizeof(raw));
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+ xhdr->size = decode_uint32(raw.size);
+ xhdr->count = decode_uint32(raw.count);
+ xhdr->serial0 = decode_uint32(raw.serial0);
+ xhdr->serial1 = decode_uint32(raw.serial1);
+ j->curxhdr = *xhdr;
+ return (ISC_R_SUCCESS);
+ }
+
+ default:
+ return (ISC_R_NOTIMPLEMENTED);
}
- xhdr->size = decode_uint32(raw.size);
- xhdr->count = decode_uint32(raw.count);
- xhdr->serial0 = decode_uint32(raw.serial0);
- xhdr->serial1 = decode_uint32(raw.serial1);
- return (ISC_R_SUCCESS);
}
static isc_result_t
journal_write_xhdr(dns_journal_t *j, uint32_t size, uint32_t count,
uint32_t serial0, uint32_t serial1) {
journal_rawxhdr_t raw;
+
encode_uint32(size, raw.size);
encode_uint32(count, raw.count);
encode_uint32(serial0, raw.serial0);
journal_read_rrhdr(dns_journal_t *j, journal_rrhdr_t *rrhdr) {
journal_rawrrhdr_t raw;
isc_result_t result;
+
result = journal_read(j, &raw, sizeof(raw));
if (result != ISC_R_SUCCESS) {
return (result);
journal_rawheader_t rawheader;
int index_size = 56; /* XXX configurable */
int size;
- void *mem; /* Memory for temporary index image. */
+ void *mem = NULL; /* Memory for temporary index image. */
INSIST(sizeof(journal_rawheader_t) == JOURNAL_HEADER_SIZE);
journal_rawheader_t rawheader;
dns_journal_t *j;
- INSIST(journalp != NULL && *journalp == NULL);
- j = isc_mem_get(mctx, sizeof(*j));
+ REQUIRE(journalp != NULL && *journalp == NULL);
- j->mctx = NULL;
+ j = isc_mem_get(mctx, sizeof(*j));
+ *j = (dns_journal_t){ .state = JOURNAL_STATE_INVALID,
+ .filename = isc_mem_strdup(mctx, filename),
+ .xhdr_version = XHDR_VERSION2 };
isc_mem_attach(mctx, &j->mctx);
- j->state = JOURNAL_STATE_INVALID;
- j->fp = NULL;
- j->filename = isc_mem_strdup(mctx, filename);
- j->index = NULL;
- j->rawindex = NULL;
-
- if (j->filename == NULL) {
- FAIL(ISC_R_NOMEMORY);
- }
result = isc_stdio_open(j->filename, writable ? "rb+" : "rb", &fp);
-
if (result == ISC_R_FILENOTFOUND) {
if (create) {
isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_DEBUG(1),
CHECK(journal_seek(j, 0));
CHECK(journal_read(j, &rawheader, sizeof(rawheader)));
- if (memcmp(rawheader.h.format, initial_journal_header.format,
- sizeof(initial_journal_header.format)) != 0)
+ if (memcmp(rawheader.h.format, journal_header_ver1.format,
+ sizeof(journal_header_ver1.format)) == 0)
{
+ /*
+ * The file header says it's the old format, but it
+ * still might have the new xhdr format because we
+ * forgot to change the format string when we introduced
+ * the new xhdr. When we first try to read it, we assume
+ * it uses the new xhdr format. If that fails, we'll be
+ * called a second time with compat set to true, in which
+ * case we can lower xhdr_version to 1 if we find a
+ * corrupt transaction.
+ */
+ j->header_ver1 = true;
+ } else if (memcmp(rawheader.h.format, initial_journal_header.format,
+ sizeof(initial_journal_header.format)) == 0)
+ {
+ /*
+ * File header says this is format version 2; all
+ * transactions have to match.
+ */
+ j->header_ver1 = false;
+ } else {
isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR,
"%s: journal format not recognized", j->filename);
FAIL(ISC_R_UNEXPECTED);
* Other results due to file errors are possible.
*/
static isc_result_t
-journal_next(dns_journal_t *j, journal_pos_t *pos) {
+journal_next(dns_journal_t *j, journal_pos_t *pos, bool retry) {
isc_result_t result;
journal_xhdr_t xhdr;
+ size_t hdrsize;
+
REQUIRE(DNS_JOURNAL_VALID(j));
result = journal_seek(j, pos->offset);
if (pos->serial == j->header.end.serial) {
return (ISC_R_NOMORE);
}
+
/*
* Read the header of the current transaction.
* This will return ISC_R_NOMORE if we are at EOF.
/*
* Check serial number consistency.
*/
- if (xhdr.serial0 != pos->serial) {
- isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR,
- "%s: journal file corrupt: "
- "expected serial %u, got %u",
- j->filename, pos->serial, xhdr.serial0);
- return (ISC_R_UNEXPECTED);
+ if (xhdr.serial0 != pos->serial ||
+ isc_serial_le(xhdr.serial1, xhdr.serial0)) {
+ if (j->header_ver1 && j->xhdr_version == XHDR_VERSION1 &&
+ xhdr.serial1 == pos->serial && !retry)
+ {
+ /* XHDR_VERSION1 -> XHDR_VERSION2 */
+ isc_log_write(
+ JOURNAL_COMMON_LOGARGS, ISC_LOG_DEBUG(3),
+ "%s: XHDR_VERSION1 -> XHDR_VERSION2 at %u\n",
+ j->filename, pos->serial);
+ j->xhdr_version = XHDR_VERSION2;
+ result = journal_next(j, pos, true);
+ if (result == ISC_R_SUCCESS) {
+ j->recovered = true;
+ }
+ return (result);
+ } else if (j->header_ver1 && j->xhdr_version == XHDR_VERSION2 &&
+ xhdr.count == pos->serial && !retry)
+ {
+ /* XHDR_VERSION2 -> XHDR_VERSION1 */
+ isc_log_write(
+ JOURNAL_COMMON_LOGARGS, ISC_LOG_DEBUG(3),
+ "%s: XHDR_VERSION2 -> XHDR_VERSION1 at %u\n",
+ j->filename, pos->serial);
+ j->xhdr_version = XHDR_VERSION1;
+ result = journal_next(j, pos, true);
+ if (result == ISC_R_SUCCESS) {
+ j->recovered = true;
+ }
+ return (result);
+ } else {
+ isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR,
+ "%s: journal file corrupt: "
+ "expected serial %u, got %u",
+ j->filename, pos->serial, xhdr.serial0);
+ return (ISC_R_UNEXPECTED);
+ }
}
/*
* Check for offset wraparound.
*/
- if ((isc_offset_t)(pos->offset + sizeof(journal_rawxhdr_t) +
- xhdr.size) < pos->offset)
- {
+ hdrsize = (j->xhdr_version == XHDR_VERSION2)
+ ? sizeof(journal_rawxhdr_t)
+ : sizeof(journal_rawxhdr_ver1_t);
+
+ if ((isc_offset_t)(pos->offset + hdrsize + xhdr.size) < pos->offset) {
isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR,
"%s: offset too large", j->filename);
return (ISC_R_UNEXPECTED);
}
- pos->offset += sizeof(journal_rawxhdr_t) + xhdr.size;
+ pos->offset += hdrsize + xhdr.size;
pos->serial = xhdr.serial1;
return (ISC_R_SUCCESS);
}
static void
index_add(dns_journal_t *j, journal_pos_t *pos) {
unsigned int i;
+
if (j->index == NULL) {
return;
}
+
/*
* Search for a vacant position.
*/
journal_find(dns_journal_t *j, uint32_t serial, journal_pos_t *pos) {
isc_result_t result;
journal_pos_t current_pos;
+
REQUIRE(DNS_JOURNAL_VALID(j));
if (DNS_SERIAL_GT(j->header.begin.serial, serial)) {
if (DNS_SERIAL_GT(current_pos.serial, serial)) {
return (ISC_R_NOTFOUND);
}
- result = journal_next(j, ¤t_pos);
+ result = journal_next(j, ¤t_pos, false);
if (result != ISC_R_SUCCESS) {
return (result);
}
if (!JOURNAL_EMPTY(&j->header)) {
while (!DNS_SERIAL_GT(j->x.pos[1].serial,
j->header.begin.serial)) {
- CHECK(journal_next(j, &j->header.begin));
+ CHECK(journal_next(j, &j->header.begin, false));
}
index_invalidate(j, j->x.pos[1].serial);
}
isc_result_t
dns_journal_write_transaction(dns_journal_t *j, dns_diff_t *diff) {
isc_result_t result;
+
CHECK(dns_diff_sort(diff, ixfr_order));
CHECK(dns_journal_begin_transaction(j));
CHECK(dns_journal_writediff(j, diff));
void
dns_journal_destroy(dns_journal_t **journalp) {
- dns_journal_t *j = *journalp;
+ dns_journal_t *j = NULL;
+
+ REQUIRE(journalp != NULL);
+ REQUIRE(DNS_JOURNAL_VALID(*journalp));
+
+ j = *journalp;
*journalp = NULL;
- REQUIRE(DNS_JOURNAL_VALID(j));
j->it.result = ISC_R_FAILURE;
dns_name_invalidate(&j->it.name);
* Locate a journal entry for the current database serial.
*/
CHECK(journal_find(j, db_serial, &pos));
+
+ end_serial = dns_journal_last_serial(j);
+
/*
- * 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 we're reading a version 1 file, scan all the transactions
+ * to see if the journal needs rewriting: if any outdated
+ * transaction headers are found, j->recovered will be set.
*/
+ if (j->header_ver1) {
+ uint32_t start_serial = dns_journal_first_serial(j);
+
+ CHECK(dns_journal_iter_init(j, start_serial, db_serial, NULL));
+ for (result = dns_journal_first_rr(j); result == ISC_R_SUCCESS;
+ result = dns_journal_next_rr(j))
+ {
+ continue;
+ }
+ }
- end_serial = dns_journal_last_serial(j);
if (db_serial == end_serial) {
CHECK(DNS_R_UPTODATE);
}
CHECK(dns_journal_iter_init(j, db_serial, end_serial, NULL));
-
for (result = dns_journal_first_rr(j); result == ISC_R_SUCCESS;
result = dns_journal_next_rr(j))
{
- dns_name_t *name;
- uint32_t ttl;
- dns_rdata_t *rdata;
+ dns_name_t *name = NULL;
+ dns_rdata_t *rdata = NULL;
dns_difftuple_t *tuple = NULL;
+ uint32_t ttl;
- name = NULL;
- rdata = NULL;
dns_journal_current_rr(j, &name, &ttl, &rdata);
if (rdata->type == dns_rdatatype_soa) {
isc_result_t
dns_journal_rollforward(isc_mem_t *mctx, dns_db_t *db, unsigned int options,
const char *filename) {
- dns_journal_t *j;
+ dns_journal_t *j = NULL;
isc_result_t result;
REQUIRE(DNS_DB_VALID(db));
REQUIRE(filename != NULL);
- j = NULL;
result = dns_journal_open(mctx, filename, DNS_JOURNAL_READ, &j);
if (result == ISC_R_NOTFOUND) {
isc_log_write(JOURNAL_DEBUG_LOGARGS(3), "no journal file, but "
if (result != ISC_R_SUCCESS) {
return (result);
}
+
if (JOURNAL_EMPTY(&j->header)) {
- result = DNS_R_UPTODATE;
- } else {
- result = roll_forward(j, db, options);
+ CHECK(DNS_R_UPTODATE);
}
- dns_journal_destroy(&j);
+ result = roll_forward(j, db, options);
+ if ((result == ISC_R_SUCCESS || result == DNS_R_UPTODATE) &&
+ j->recovered) {
+ result = DNS_R_RECOVERABLE;
+ }
+failure:
+ dns_journal_destroy(&j);
return (result);
}
isc_result_t
-dns_journal_print(isc_mem_t *mctx, const char *filename, FILE *file) {
- dns_journal_t *j;
+dns_journal_print(isc_mem_t *mctx, uint32_t flags, const char *filename,
+ FILE *file) {
+ dns_journal_t *j = NULL;
isc_buffer_t source; /* Transaction data from disk */
isc_buffer_t target; /* Ditto after _fromwire check */
uint32_t start_serial; /* Database SOA serial */
dns_diff_t diff;
unsigned int n_soa = 0;
unsigned int n_put = 0;
+ bool printxhdr = ((flags & DNS_JOURNAL_PRINTXHDR) != 0);
REQUIRE(filename != NULL);
- j = NULL;
result = dns_journal_open(mctx, filename, DNS_JOURNAL_READ, &j);
if (result == ISC_R_NOTFOUND) {
isc_log_write(JOURNAL_DEBUG_LOGARGS(3), "no journal file");
return (DNS_R_NOJOURNAL);
- }
-
- if (result != ISC_R_SUCCESS) {
+ } else if (result != ISC_R_SUCCESS) {
isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR,
"journal open failure: %s: %s",
isc_result_totext(result), filename);
for (result = dns_journal_first_rr(j); result == ISC_R_SUCCESS;
result = dns_journal_next_rr(j))
{
- dns_name_t *name;
- uint32_t ttl;
- dns_rdata_t *rdata;
+ dns_name_t *name = NULL;
+ dns_rdata_t *rdata = NULL;
dns_difftuple_t *tuple = NULL;
+ uint32_t ttl;
- name = NULL;
- rdata = NULL;
dns_journal_current_rr(j, &name, &ttl, &rdata);
if (rdata->type == dns_rdatatype_soa) {
j->filename);
FAIL(ISC_R_UNEXPECTED);
}
+
+ if (printxhdr && n_soa == 1) {
+ fprintf(file,
+ "Transaction: version %d size %u rrcount %u "
+ "startserial %u endserial %u\n",
+ j->xhdr_version, j->curxhdr.size,
+ j->curxhdr.count, j->curxhdr.serial0,
+ j->curxhdr.serial1);
+ }
CHECK(dns_difftuple_create(
diff.mctx, n_soa == 1 ? DNS_DIFFOP_DEL : DNS_DIFFOP_ADD,
name, ttl, rdata, &tuple));
dns_diff_append(&diff, &tuple);
- if (++n_put > 100) {
+ if (++n_put != 0 || printxhdr) {
result = dns_diff_print(&diff, file);
dns_diff_clear(&diff);
n_put = 0;
}
CHECK(result);
- if (n_put != 0) {
+ if (!printxhdr && n_put != 0) {
result = dns_diff_print(&diff, file);
dns_diff_clear(&diff);
}
*/
static isc_result_t
-read_one_rr(dns_journal_t *j);
+read_one_rr(dns_journal_t *j, bool retry);
/*
* Make sure the buffer 'b' is has at least 'size' bytes
size += xhdr.size;
count += xhdr.count;
- result = journal_next(j, &pos);
+ result = journal_next(j, &pos, false);
if (result == ISC_R_NOMORE) {
result = ISC_R_SUCCESS;
}
j->it.xsize = 0; /* We have no transaction data yet... */
j->it.xpos = 0; /* ...and haven't used any of it. */
- return (read_one_rr(j));
+ return (read_one_rr(j, false));
failure:
return (result);
}
static isc_result_t
-read_one_rr(dns_journal_t *j) {
+read_one_rr(dns_journal_t *j, bool retry) {
isc_result_t result;
-
dns_rdatatype_t rdtype;
dns_rdataclass_t rdclass;
unsigned int rdlen;
uint32_t ttl;
journal_xhdr_t xhdr;
journal_rrhdr_t rrhdr;
+ dns_journal_t save = *j;
if (j->offset > j->it.epos.offset) {
isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR,
j->filename);
FAIL(ISC_R_UNEXPECTED);
}
- if (xhdr.serial0 != j->it.current_serial) {
+ if (xhdr.serial0 != j->it.current_serial ||
+ isc_serial_le(xhdr.serial1, xhdr.serial0))
+ {
+ if (!retry && j->header_ver1 &&
+ j->xhdr_version == XHDR_VERSION2 &&
+ xhdr.count == j->it.current_serial)
+ {
+ /* XHDR_VERSION2 -> XHDR_VERSION1 */
+ j->xhdr_version = XHDR_VERSION1;
+ CHECK(journal_seek(j, save.offset));
+ result = read_one_rr(j, true);
+ if (result == ISC_R_SUCCESS) {
+ j->recovered = true;
+ }
+ return (result);
+ } else if (!retry && j->header_ver1 &&
+ j->xhdr_version == XHDR_VERSION1 &&
+ xhdr.serial1 == j->it.current_serial)
+ {
+ /* XHDR_VERSION1 -> XHDR_VERSION2 */
+ j->xhdr_version = XHDR_VERSION2;
+ CHECK(journal_seek(j, save.offset));
+ result = read_one_rr(j, true);
+ if (result == ISC_R_SUCCESS) {
+ j->recovered = true;
+ }
+ return (result);
+ }
isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR,
"%s: journal file corrupt: "
"expected serial %u, got %u",
isc_result_t
dns_journal_next_rr(dns_journal_t *j) {
- j->it.result = read_one_rr(j);
+ j->it.result = read_one_rr(j, false);
return (j->it.result);
}
return (result);
}
+static uint32_t
+rrcount(char *buf, unsigned int size) {
+ isc_buffer_t b;
+ uint32_t rrsize, count = 0;
+
+ isc_buffer_init(&b, buf, size);
+ isc_buffer_add(&b, size);
+ while (isc_buffer_remaininglength(&b) > 0) {
+ rrsize = isc_buffer_getuint32(&b);
+ INSIST(isc_buffer_remaininglength(&b) >= rrsize);
+ isc_buffer_forward(&b, rrsize);
+ count++;
+ }
+
+ return (count);
+}
+
isc_result_t
dns_journal_compact(isc_mem_t *mctx, char *filename, uint32_t serial,
- uint32_t target_size) {
+ uint32_t flags, uint32_t target_size) {
unsigned int i;
journal_pos_t best_guess;
journal_pos_t current_pos;
dns_journal_t *j1 = NULL;
dns_journal_t *j2 = NULL;
journal_rawheader_t rawheader;
- unsigned int copy_length;
+ unsigned int len;
size_t namelen;
char *buf = NULL;
unsigned int size = 0;
char newname[PATH_MAX];
char backup[PATH_MAX];
bool is_backup = false;
+ bool rewrite = false;
REQUIRE(filename != NULL);
return (result);
}
- if (JOURNAL_EMPTY(&j1->header)) {
+ /*
+ * Check whether we need to rewrite the whole journal
+ * file (for example, to upversion it).
+ */
+ if ((flags & DNS_JOURNAL_COMPACTALL) != 0) {
+ rewrite = true;
+ serial = dns_journal_first_serial(j1);
+ } else if (JOURNAL_EMPTY(&j1->header)) {
dns_journal_destroy(&j1);
return (ISC_R_SUCCESS);
}
/*
* See if there is any work to do.
*/
- if ((uint32_t)j1->header.end.offset < target_size) {
+ if (!rewrite && (uint32_t)j1->header.end.offset < target_size) {
dns_journal_destroy(&j1);
return (ISC_R_SUCCESS);
}
CHECK(journal_open(mctx, newname, true, true, &j2));
+ CHECK(journal_seek(j2, indexend));
/*
* Remove overhead so space test below can succeed.
current_pos = best_guess;
while (current_pos.serial != serial) {
- CHECK(journal_next(j1, ¤t_pos));
+ CHECK(journal_next(j1, ¤t_pos, false));
if (current_pos.serial == j1->header.end.serial) {
break;
}
INSIST(best_guess.serial != j1->header.end.serial);
if (best_guess.serial != serial) {
- CHECK(journal_next(j1, &best_guess));
+ CHECK(journal_next(j1, &best_guess, false));
}
/*
* we did not reach 'serial'. If not we will just copy
* all uncommitted deltas regardless of the size.
*/
- copy_length = j1->header.end.offset - best_guess.offset;
+ len = j1->header.end.offset - best_guess.offset;
+ if (len != 0) {
+ CHECK(journal_seek(j1, best_guess.offset));
+
+ /* Prepare new header */
+ j2->header.begin.serial = best_guess.serial;
+ j2->header.begin.offset = indexend;
+ j2->header.sourceserial = j1->header.sourceserial;
+ j2->header.serialset = j1->header.serialset;
+ j2->header.end.serial = j1->header.end.serial;
- if (copy_length != 0) {
/*
- * Copy best_guess to end into space just freed.
+ * Only use this method if we're rewriting the
+ * journal to fix outdated transaction headers;
+ * otherwise we'll copy the whole journal without
+ * parsing individual deltas below.
*/
- size = 64 * 1024;
- if (copy_length < size) {
- size = copy_length;
- }
- buf = isc_mem_get(mctx, size);
+ while (rewrite && len > 0) {
+ journal_xhdr_t xhdr;
+ isc_offset_t offset = j1->offset;
+ uint32_t count;
- CHECK(journal_seek(j1, best_guess.offset));
- CHECK(journal_seek(j2, indexend));
- for (i = 0; i < copy_length; i += size) {
- unsigned int len = (copy_length - i) > size
- ? size
- : (copy_length - i);
- CHECK(journal_read(j1, buf, len));
- CHECK(journal_write(j2, buf, len));
- }
+ result = journal_read_xhdr(j1, &xhdr);
+ if (rewrite && result == ISC_R_NOMORE) {
+ break;
+ }
+ CHECK(result);
- CHECK(journal_fsync(j2));
+ /*
+ * If we're repairing an outdated journal, the
+ * xhdr format may be wrong.
+ */
+ if (rewrite &&
+ (xhdr.serial0 != serial ||
+ isc_serial_le(xhdr.serial1, xhdr.serial0)))
+ {
+ if (j1->xhdr_version == XHDR_VERSION2 &&
+ xhdr.count == serial) {
+ /* XHDR_VERSION2 -> XHDR_VERSION1 */
+ j1->xhdr_version = XHDR_VERSION1;
+ CHECK(journal_seek(j1, offset));
+ CHECK(journal_read_xhdr(j1, &xhdr));
+ } else if (j1->xhdr_version == XHDR_VERSION1 &&
+ xhdr.serial1 == serial) {
+ /* XHDR_VERSION1 -> XHDR_VERSION2 */
+ j1->xhdr_version = XHDR_VERSION2;
+ CHECK(journal_seek(j1, offset));
+ CHECK(journal_read_xhdr(j1, &xhdr));
+ }
+
+ /* Check again */
+ if (xhdr.serial0 != serial ||
+ isc_serial_le(xhdr.serial1, xhdr.serial0)) {
+ CHECK(ISC_R_UNEXPECTED);
+ }
+ }
+
+ size = xhdr.size;
+ buf = isc_mem_get(mctx, size);
+ CHECK(journal_read(j1, buf, size));
+
+ count = rrcount(buf, size);
+ CHECK(journal_write_xhdr(j2, xhdr.size, count,
+ xhdr.serial0, xhdr.serial1));
+ CHECK(journal_write(j2, buf, size));
+
+ j2->header.end.offset = j2->offset;
+
+ serial = xhdr.serial1;
+
+ len = j1->header.end.offset - j1->offset;
+ isc_mem_put(mctx, buf, size);
+ }
/*
- * Compute new header.
+ * If we're not rewriting transaction headers, we can use
+ * this faster method instead.
*/
- j2->header.begin.serial = best_guess.serial;
- j2->header.begin.offset = indexend;
- j2->header.end.serial = j1->header.end.serial;
- j2->header.end.offset = indexend + copy_length;
- j2->header.sourceserial = j1->header.sourceserial;
- j2->header.serialset = j1->header.serialset;
+ if (!rewrite) {
+ size = ISC_MIN(64 * 1024, len);
+ buf = isc_mem_get(mctx, size);
+ for (i = 0; i < len; i += size) {
+ unsigned int blob = ISC_MIN(size, len - i);
+ CHECK(journal_read(j1, buf, blob));
+ CHECK(journal_write(j2, buf, blob));
+ }
+
+ j2->header.end.offset = indexend + len;
+ }
+
+ CHECK(journal_fsync(j2));
/*
* Update the journal header.
current_pos = j2->header.begin;
while (current_pos.serial != j2->header.end.serial) {
index_add(j2, ¤t_pos);
- CHECK(journal_next(j2, ¤t_pos));
+ CHECK(journal_next(j2, ¤t_pos, false));
}
/*