Merge branch '2623-9-16-13-overwrites-master-files-if-old-format-jnl-files-are-present' into 'main'

Resolve "9.16.13 overwrites master files if old format .jnl files are present"

Closes #2623

See merge request isc-projects/bind9!4880
This commit is contained in:
Ondřej Surý 2021-04-16 11:12:19 +00:00
commit 1ab099aa3b
7 changed files with 130 additions and 97 deletions

View file

@ -1,3 +1,6 @@
5620. [bug] Named would overwrite a zone file unconditionally when
it recovered from a corrupted journal. [GL #2623]
5619. [protocol] Implement draft-vandijk-dnsop-nsec-ttl, updating the
protocol such that NSEC(3) TTL values are set to the
minimum of the SOA MINIMUM value and the SOA TTL.

View file

@ -28,7 +28,7 @@ ret=0
dig_with_opts changed soa > dig.out.test$n
grep 'status: NOERROR' dig.out.test$n > /dev/null || ret=1
grep '2012010902' dig.out.test$n > /dev/null || ret=1
grep 'zone changed/IN: retried using old journal format' ns1/named.run > /dev/null || ret=1
grep 'zone changed/IN: journal rollforward completed successfully using old journal format' ns1/named.run > /dev/null || ret=1
[ $ret -eq 0 ] || echo_i "failed"
status=`expr $status + $ret`
@ -62,7 +62,8 @@ ret=0
dig_with_opts changed2 soa > dig.out.test$n
grep 'status: NOERROR' dig.out.test$n > /dev/null || ret=1
grep '2012010902' dig.out.test$n > /dev/null || ret=1
grep 'zone changed2/IN: retried using old journal format' ns1/named.run > /dev/null && ret=1
grep 'zone changed2/IN: journal rollforward completed successfully: success' ns1/named.run > /dev/null || ret=1
grep 'zone changed2/IN: journal rollforward completed successfully using old journal format' ns1/named.run > /dev/null && ret=1
[ $ret -eq 0 ] || echo_i "failed"
status=`expr $status + $ret`
@ -72,7 +73,8 @@ ret=0
dig_with_opts unchanged2 soa > dig.out.test$n
grep 'status: NOERROR' dig.out.test$n > /dev/null || ret=1
grep '2012010901' dig.out.test$n > /dev/null || ret=1
grep 'zone unchanged2/IN: retried using old journal format' ns1/named.run > /dev/null && ret=1
grep 'zone unchanged2/IN: journal rollforward completed successfully' ns1/named.run > /dev/null && ret=1
grep 'zone unchanged2/IN: journal rollforward completed successfully using old journal format' ns1/named.run > /dev/null && ret=1
[ $ret -eq 0 ] || echo_i "failed"
status=`expr $status + $ret`
@ -90,7 +92,7 @@ ret=0
dig_with_opts -t soa ixfr > dig.out.test$n
grep 'status: NOERROR' dig.out.test$n > /dev/null || ret=1
grep '2012010902' dig.out.test$n > /dev/null || ret=1
grep 'zone ixfr/IN: journal rollforward completed successfully: recoverable' ns1/named.run > /dev/null || ret=1
grep 'zone ixfr/IN: journal rollforward completed successfully using old journal format: up to date' ns1/named.run > /dev/null || ret=1
[ $ret -eq 0 ] || echo_i "failed"
status=`expr $status + $ret`
@ -107,7 +109,9 @@ ret=0
dig_with_opts -t soa hdr1d1d2d1d2 > dig.out.test$n
grep 'status: NOERROR' dig.out.test$n > /dev/null || ret=1
grep '2012010905' dig.out.test$n > /dev/null || ret=1
grep 'zone hdr1d1d2d1d2/IN: journal rollforward completed successfully: recoverable' ns1/named.run > /dev/null || ret=1
grep 'zone hdr1d1d2d1d2/IN: journal rollforward completed successfully using old journal format: success' ns1/named.run > /dev/null || ret=1
grep 'zone_journal_compact: zone hdr1d1d2d1d2/IN: repair full journal' ns1/named.run > /dev/null || ret=1
grep 'hdr1d1d2d1d2/IN: dns_journal_compact: success' ns1/named.run > /dev/null || ret=1
[ $ret -eq 0 ] || echo_i "failed"
status=`expr $status + $ret`
@ -127,7 +131,9 @@ ret=0
dig_with_opts -t soa hdr1d2d1d2d1 > dig.out.test$n
grep 'status: NOERROR' dig.out.test$n > /dev/null || ret=1
grep '2012010905' dig.out.test$n > /dev/null || ret=1
grep 'zone hdr1d2d1d2d1/IN: journal rollforward completed successfully: recoverable' ns1/named.run > /dev/null || ret=1
grep 'zone hdr1d2d1d2d1/IN: journal rollforward completed successfully using old journal format: success' ns1/named.run > /dev/null || ret=1
grep 'zone_journal_compact: zone hdr1d2d1d2d1/IN: repair full journal' ns1/named.run > /dev/null || ret=1
grep 'zone hdr1d2d1d2d1/IN: dns_journal_compact: success' ns1/named.run > /dev/null || ret=1
[ $ret -eq 0 ] || echo_i "failed"
status=`expr $status + $ret`
@ -175,10 +181,15 @@ status=`expr $status + $ret`
n=`expr $n + 1`
echo_i "check max-journal-size works after journal update ($n)"
ret=0
# a dump should have been triggered by repairing the journal,
# which would have resulted in the journal already being
# compacted.
[ $(wc -c < ns1/maxjournal.db.jnl) -lt 4000 ] || ret=1
# journal was repaired, it should still be big
[ $(wc -c < ns1/maxjournal.db.jnl) -gt 12000 ] || ret=1
# the zone hasn't been dumped yet, so 'rndc sync' should work without
# needing a zone update first.
rndc_with_opts 10.53.0.1 sync maxjournal
check_size() (
[ $(wc -c < ns1/maxjournal.db.jnl) -lt 4000 ]
)
retry_quiet 10 check_size || ret=1
[ $ret -eq 0 ] || echo_i "failed"
status=`expr $status + $ret`

View file

@ -89,3 +89,6 @@ Bug Fixes
values if the RRset was still marked a stale but the ``max-stale-ttl`` has
passed (and is actually an RRset awaiting cleanup). Both issues have now
been fixed. [GL #389] [GL #2289]
- ``named`` would overwrite a zone file unconditionally when it recovered from
a corrupted journal. [GL #2623]

View file

@ -187,6 +187,12 @@ dns_journal_empty(dns_journal_t *j);
* Find out if a journal is empty.
*/
bool
dns_journal_recovered(dns_journal_t *j);
/*<
* Find out if the journal could be opened using old journal format
*/
uint32_t
dns_journal_first_serial(dns_journal_t *j);
uint32_t
@ -248,28 +254,23 @@ dns_journal_current_rr(dns_journal_t *j, dns_name_t **name, uint32_t *ttl,
*/
isc_result_t
dns_journal_rollforward(isc_mem_t *mctx, dns_db_t *db, unsigned int options,
const char *filename);
dns_journal_rollforward(dns_journal_t *j, dns_db_t *db, unsigned int options);
/*%<
* Roll forward (play back) the journal file "filename" into the
* database "db". This should be called when the server starts
* after a shutdown or crash.
*
* Requires:
*\li 'mctx' is a valid memory context.
*\li 'journal' is a valid journal
*\li 'db' is a valid database which does not have a version
* open for writing.
*\li 'filename' is the name of the journal file belonging to 'db'.
*
* Returns:
*\li DNS_R_NOJOURNAL when journal does not exist.
*\li ISC_R_NOTFOUND when current serial in not in journal.
*\li ISC_R_RANGE when current serial in not in journals range.
*\li DNS_R_UPTODATE when the database was already up to date.
*\li ISC_R_SUCCESS journal has been applied successfully to the
* database without any issues.
*\li DNS_R_RECOVERABLE if successful or up to date, but the journal
* was found to contain at least one outdated transaction header.
*
* others
*/

View file

@ -1450,8 +1450,8 @@ dns_journal_destroy(dns_journal_t **journalp) {
/* XXX Share code with incoming IXFR? */
static isc_result_t
roll_forward(dns_journal_t *j, dns_db_t *db, unsigned int options) {
isc_result_t
dns_journal_rollforward(dns_journal_t *j, dns_db_t *db, unsigned int options) {
isc_buffer_t source; /* Transaction data from disk */
isc_buffer_t target; /* Ditto after _fromwire check */
uint32_t db_serial; /* Database SOA serial */
@ -1597,40 +1597,6 @@ failure:
return (result);
}
isc_result_t
dns_journal_rollforward(isc_mem_t *mctx, dns_db_t *db, unsigned int options,
const char *filename) {
dns_journal_t *j = NULL;
isc_result_t result;
REQUIRE(DNS_DB_VALID(db));
REQUIRE(filename != 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 "
"that's OK");
return (DNS_R_NOJOURNAL);
}
if (result != ISC_R_SUCCESS) {
return (result);
}
if (JOURNAL_EMPTY(&j->header)) {
CHECK(DNS_R_UPTODATE);
}
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, uint32_t flags, const char *filename,
FILE *file) {
@ -1788,6 +1754,11 @@ dns_journal_empty(dns_journal_t *j) {
return (JOURNAL_EMPTY(&j->header));
}
bool
dns_journal_recovered(dns_journal_t *j) {
return (j->recovered);
}
uint32_t
dns_journal_first_serial(dns_journal_t *j) {
return (j->header.begin.serial);

View file

@ -415,6 +415,7 @@ dns_journal_last_serial
dns_journal_next_rr
dns_journal_open
dns_journal_print
dns_journal_recovered
dns_journal_rollforward
dns_journal_set_sourceserial
dns_journal_write_transaction

View file

@ -901,6 +901,9 @@ static void
setrl(isc_ratelimiter_t *rl, unsigned int *rate, unsigned int value);
static void
zone_journal_compact(dns_zone_t *zone, dns_db_t *db, uint32_t serial);
static isc_result_t
zone_journal_rollforward(dns_zone_t *zone, dns_db_t *db, bool *needdump,
bool *fixjournal);
#define ENTER zone_debuglog(zone, me, 1, "enter")
@ -4680,7 +4683,6 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime,
bool hasinclude = DNS_ZONE_FLAG(zone, DNS_ZONEFLG_HASINCLUDE);
bool nomaster = false;
bool had_db = false;
unsigned int options;
dns_include_t *inc;
bool is_dynamic = false;
@ -4768,46 +4770,11 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime,
!DNS_ZONE_OPTION(zone, DNS_ZONEOPT_NOMERGE) &&
!DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADED))
{
if (zone->type == dns_zone_master &&
(inline_secure(zone) ||
(zone->update_acl != NULL || zone->ssutable != NULL)))
{
options = DNS_JOURNALOPT_RESIGN;
} else {
options = 0;
}
result = dns_journal_rollforward(zone->mctx, db, options,
zone->journal);
if (result != ISC_R_SUCCESS && result != DNS_R_RECOVERABLE &&
result != ISC_R_NOTFOUND && result != DNS_R_UPTODATE &&
result != DNS_R_NOJOURNAL && result != ISC_R_RANGE)
{
dns_zone_logc(zone, DNS_LOGCATEGORY_ZONELOAD,
ISC_LOG_ERROR,
"journal rollforward failed: %s",
dns_result_totext(result));
result = zone_journal_rollforward(zone, db, &needdump,
&fixjournal);
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
if (result == ISC_R_NOTFOUND || result == ISC_R_RANGE) {
dns_zone_logc(zone, DNS_LOGCATEGORY_ZONELOAD,
ISC_LOG_ERROR,
"journal rollforward failed: "
"journal out of sync with zone");
goto cleanup;
}
dns_zone_logc(zone, DNS_LOGCATEGORY_ZONELOAD, ISC_LOG_DEBUG(1),
"journal rollforward completed "
"successfully: %s",
dns_result_totext(result));
if (result == ISC_R_SUCCESS) {
needdump = true;
} else if (result == DNS_R_RECOVERABLE) {
dns_zone_logc(zone, DNS_LOGCATEGORY_ZONELOAD,
ISC_LOG_ERROR,
"retried using old journal format");
needdump = true;
fixjournal = true;
}
}
/*
@ -5130,12 +5097,12 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime,
result = ISC_R_SUCCESS;
if (fixjournal) {
DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_FIXJOURNAL);
zone_journal_compact(zone, zone->db, 0);
}
if (needdump) {
if (fixjournal) {
DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_FIXJOURNAL);
zone_journal_compact(zone, zone->db, 0);
zone_needdump(zone, 0);
} else if (zone->type == dns_zone_key) {
if (zone->type == dns_zone_key) {
zone_needdump(zone, 30);
} else {
zone_needdump(zone, DNS_DUMP_DELAY);
@ -11336,6 +11303,82 @@ dns_zone_refresh(dns_zone_t *zone) {
UNLOCK_ZONE(zone);
}
static isc_result_t
zone_journal_rollforward(dns_zone_t *zone, dns_db_t *db, bool *needdump,
bool *fixjournal) {
dns_journal_t *journal = NULL;
unsigned int options;
isc_result_t result;
if (zone->type == dns_zone_master &&
(inline_secure(zone) ||
(zone->update_acl != NULL || zone->ssutable != NULL)))
{
options = DNS_JOURNALOPT_RESIGN;
} else {
options = 0;
}
result = dns_journal_open(zone->mctx, zone->journal, DNS_JOURNAL_READ,
&journal);
if (result == ISC_R_NOTFOUND) {
dns_zone_logc(zone, DNS_LOGCATEGORY_ZONELOAD, ISC_LOG_DEBUG(3),
"no journal file, but that's OK ");
return (ISC_R_SUCCESS);
} else if (result != ISC_R_SUCCESS) {
dns_zone_logc(zone, DNS_LOGCATEGORY_ZONELOAD, ISC_LOG_ERROR,
"journal open failed: %s",
dns_result_totext(result));
return (result);
}
if (dns_journal_empty(journal)) {
dns_zone_logc(zone, DNS_LOGCATEGORY_ZONELOAD, ISC_LOG_DEBUG(1),
"journal empty");
dns_journal_destroy(&journal);
return (ISC_R_SUCCESS);
}
result = dns_journal_rollforward(journal, db, options);
switch (result) {
case ISC_R_SUCCESS:
*needdump = true;
/* FALLTHROUGH */
case DNS_R_UPTODATE:
if (dns_journal_recovered(journal)) {
*fixjournal = true;
dns_zone_logc(
zone, DNS_LOGCATEGORY_ZONELOAD,
ISC_LOG_DEBUG(1),
"journal rollforward completed successfully "
"using old journal format: %s",
dns_result_totext(result));
} else {
dns_zone_logc(zone, DNS_LOGCATEGORY_ZONELOAD,
ISC_LOG_DEBUG(1),
"journal rollforward completed "
"successfully: %s",
dns_result_totext(result));
}
dns_journal_destroy(&journal);
return (ISC_R_SUCCESS);
case ISC_R_NOTFOUND:
case ISC_R_RANGE:
dns_zone_logc(zone, DNS_LOGCATEGORY_ZONELOAD, ISC_LOG_ERROR,
"journal rollforward failed: journal out of sync "
"with zone");
dns_journal_destroy(&journal);
return (result);
default:
dns_zone_logc(zone, DNS_LOGCATEGORY_ZONELOAD, ISC_LOG_ERROR,
"journal rollforward failed: %s",
dns_result_totext(result));
dns_journal_destroy(&journal);
return (result);
}
}
static void
zone_journal_compact(dns_zone_t *zone, dns_db_t *db, uint32_t serial) {
isc_result_t result;