diff --git a/lib/dns/include/dns/journal.h b/lib/dns/include/dns/journal.h index f05e6ce3cb..34ba91ac3e 100644 --- a/lib/dns/include/dns/journal.h +++ b/lib/dns/include/dns/journal.h @@ -190,11 +190,17 @@ dns_journal_last_serial(dns_journal_t *j); isc_result_t dns_journal_iter_init(dns_journal_t *j, uint32_t begin_serial, - uint32_t end_serial); + uint32_t end_serial, size_t *xfrsizep); /*%< * Prepare to iterate over the transactions that will bring the database * from SOA serial number 'begin_serial' to 'end_serial'. * + * If 'xfrsizep' is not NULL, then on success it will be set to the + * total size of all records in the iteration (excluding headers). This + * is meant to be a rough approximation of the size of an incremental + * zone transfer, though it does not account for DNS message overhead + * or name compression.) + * * Returns: *\li ISC_R_SUCCESS *\li ISC_R_RANGE begin_serial is outside the addressable range. diff --git a/lib/dns/journal.c b/lib/dns/journal.c index e28dd61031..d864010c7d 100644 --- a/lib/dns/journal.c +++ b/lib/dns/journal.c @@ -221,6 +221,7 @@ typedef union { */ typedef struct { unsigned char size[4]; /*%< In bytes, excluding header. */ + unsigned char count[4]; /*%< Number of records in transaction */ unsigned char serial0[4]; /*%< SOA serial before update. */ unsigned char serial1[4]; /*%< SOA serial after update. */ } journal_rawxhdr_t; @@ -256,9 +257,9 @@ typedef struct { /*% * The in-core representation of the transaction header. */ - typedef struct { uint32_t size; + uint32_t count; uint32_t serial0; uint32_t serial1; } journal_xhdr_t; @@ -304,13 +305,13 @@ struct dns_journal { 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 */ + * in on-disk format */ journal_pos_t *index; /*%< In-core journal index */ /*% Current transaction state (when writing). */ struct { unsigned int n_soa; /*%< Number of SOAs seen */ + unsigned int n_rr; /*%< Number of RRs to write */ journal_pos_t pos[2]; /*%< Begin/end position */ } x; @@ -323,8 +324,7 @@ struct dns_journal { uint32_t current_serial; /*%< Current SOA serial * */ isc_buffer_t source; /*%< Data from disk */ - isc_buffer_t target; /*%< Data from _fromwire check - * */ + isc_buffer_t target; /*%< Data from _fromwire check */ dns_decompress_t dctx; /*%< Dummy decompression ctx */ dns_name_t name; /*%< Current domain name */ dns_rdata_t rdata; /*%< Current rdata */ @@ -462,16 +462,18 @@ journal_read_xhdr(dns_journal_t *j, journal_xhdr_t *xhdr) { 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); return (ISC_R_SUCCESS); } static isc_result_t -journal_write_xhdr(dns_journal_t *j, uint32_t size, uint32_t serial0, - uint32_t serial1) { +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); encode_uint32(serial1, raw.serial1); return (journal_write(j, &raw, sizeof(raw))); @@ -1026,7 +1028,8 @@ dns_journal_writediff(dns_journal_t *j, dns_diff_t *diff) { dns_difftuple_t *t; isc_buffer_t buffer; void *mem = NULL; - uint64_t size; + uint64_t size = 0; + uint32_t rrcount = 0; isc_result_t result; isc_region_t used; @@ -1040,7 +1043,6 @@ dns_journal_writediff(dns_journal_t *j, dns_diff_t *diff) { * Pass 1: determine the buffer size needed, and * keep track of SOA serial numbers. */ - size = 0; for (t = ISC_LIST_HEAD(diff->tuples); t != NULL; t = ISC_LIST_NEXT(t, link)) { if (t->rdata.type == dns_rdatatype_soa) { @@ -1089,12 +1091,15 @@ dns_journal_writediff(dns_journal_t *j, dns_diff_t *diff) { isc_buffer_putuint16(&buffer, (uint16_t)t->rdata.length); INSIST(isc_buffer_availablelength(&buffer) >= t->rdata.length); isc_buffer_putmem(&buffer, t->rdata.data, t->rdata.length); + + rrcount++; } isc_buffer_usedregion(&buffer, &used); INSIST(used.length == size); j->x.pos[1].offset += used.length; + j->x.n_rr = rrcount; /* * Write the buffer contents to the journal file. @@ -1205,7 +1210,8 @@ dns_journal_commit(dns_journal_t *j) { * Update the transaction header. */ CHECK(journal_seek(j, j->x.pos[0].offset)); - CHECK(journal_write_xhdr(j, offset, j->x.pos[0].serial, + CHECK(journal_write_xhdr(j, offset, j->x.n_rr, + j->x.pos[0].serial, j->x.pos[1].serial)); } @@ -1355,7 +1361,7 @@ roll_forward(dns_journal_t *j, dns_db_t *db, unsigned int options) { CHECK(DNS_R_UPTODATE); } - CHECK(dns_journal_iter_init(j, db_serial, end_serial)); + 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)) @@ -1515,7 +1521,7 @@ dns_journal_print(isc_mem_t *mctx, const char *filename, FILE *file) { start_serial = dns_journal_first_serial(j); end_serial = dns_journal_last_serial(j); - CHECK(dns_journal_iter_init(j, start_serial, end_serial)); + CHECK(dns_journal_iter_init(j, start_serial, end_serial, NULL)); for (result = dns_journal_first_rr(j); result == ISC_R_SUCCESS; result = dns_journal_next_rr(j)) @@ -1672,7 +1678,7 @@ size_buffer(isc_mem_t *mctx, isc_buffer_t *b, unsigned size) { isc_result_t dns_journal_iter_init(dns_journal_t *j, uint32_t begin_serial, - uint32_t end_serial) { + uint32_t end_serial, size_t *xfrsizep) { isc_result_t result; CHECK(journal_find(j, begin_serial, &j->it.bpos)); @@ -1681,6 +1687,41 @@ dns_journal_iter_init(dns_journal_t *j, uint32_t begin_serial, CHECK(journal_find(j, end_serial, &j->it.epos)); INSIST(j->it.epos.serial == end_serial); + if (xfrsizep != NULL) { + journal_pos_t pos = j->it.bpos; + journal_xhdr_t xhdr; + uint64_t size = 0; + uint32_t count = 0; + + /* + * We already know the beginning and ending serial + * numbers are in the journal. Scan through them, + * adding up sizes and RR counts so we can calculate + * the IXFR size. + */ + CHECK(journal_seek(j, pos.offset)); + do { + CHECK(journal_read_xhdr(j, &xhdr)); + + size += xhdr.size; + count += xhdr.count; + + result = journal_next(j, &pos); + if (result == ISC_R_NOMORE) { + result = ISC_R_SUCCESS; + } + CHECK(result); + } while (pos.serial != end_serial); + + /* + * For each RR, subtract the length of the RR header, + * as this would not be present in IXFR messages. + * (We don't need to worry about the transaction header + * because that was already excluded from xdr.size.) + */ + *xfrsizep = size - (count * sizeof(journal_rawrrhdr_t)); + } + result = ISC_R_SUCCESS; failure: j->it.result = result; diff --git a/lib/dns/zone.c b/lib/dns/zone.c index e408e7ff4d..17cbdeff24 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -15299,7 +15299,7 @@ sync_secure_journal(dns_zone_t *zone, dns_zone_t *raw, dns_journal_t *journal, return (DNS_R_UNCHANGED); } - CHECK(dns_journal_iter_init(journal, start, end)); + CHECK(dns_journal_iter_init(journal, start, end, NULL)); for (result = dns_journal_first_rr(journal); result == ISC_R_SUCCESS; result = dns_journal_next_rr(journal)) { diff --git a/lib/ns/xfrout.c b/lib/ns/xfrout.c index a92783cf1b..eb8b491033 100644 --- a/lib/ns/xfrout.c +++ b/lib/ns/xfrout.c @@ -242,7 +242,8 @@ ixfr_rrstream_create(isc_mem_t *mctx, const char *journal_filename, CHECK(dns_journal_open(mctx, journal_filename, DNS_JOURNAL_READ, &s->journal)); - CHECK(dns_journal_iter_init(s->journal, begin_serial, end_serial)); + CHECK(dns_journal_iter_init(s->journal, begin_serial, end_serial, + NULL)); *sp = (rrstream_t *)s; return (ISC_R_SUCCESS);