diff options
Diffstat (limited to 'lib/dns/journal.c')
-rw-r--r-- | lib/dns/journal.c | 166 |
1 files changed, 105 insertions, 61 deletions
diff --git a/lib/dns/journal.c b/lib/dns/journal.c index 83f1320e..05b73e48 100644 --- a/lib/dns/journal.c +++ b/lib/dns/journal.c @@ -15,12 +15,13 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: journal.c,v 1.96 2007/06/19 23:47:16 tbox Exp $ */ +/* $Id: journal.c,v 1.99 2007/09/07 05:14:33 marka Exp $ */ #include <config.h> #include <stdlib.h> #include <unistd.h> +#include <errno.h> #include <isc/file.h> #include <isc/mem.h> @@ -670,7 +671,23 @@ journal_open(isc_mem_t *mctx, const char *filename, isc_boolean_t write, isc_result_t dns_journal_open(isc_mem_t *mctx, const char *filename, isc_boolean_t write, dns_journal_t **journalp) { - return (journal_open(mctx, filename, write, write, journalp)); + isc_result_t result; + int namelen; + char backup[1024]; + + result = journal_open(mctx, filename, write, write, journalp); + if (result == ISC_R_NOTFOUND) { + namelen = strlen(filename); + if (namelen > 4 && strcmp(filename + namelen - 4, ".jnl") == 0) + namelen -= 4; + + result = isc_string_printf(backup, sizeof(backup), "%.*s.jbk", + namelen, filename); + if (result != ISC_R_SUCCESS) + return (result); + result = journal_open(mctx, backup, write, write, journalp); + } + return (result); } /* @@ -1612,6 +1629,8 @@ read_one_rr(dns_journal_t *j) { /* * Parse the rdata. */ + if (isc_buffer_remaininglength(&j->it.source) != rdlen) + FAIL(DNS_R_FORMERR); isc_buffer_setactive(&j->it.source, rdlen); dns_rdata_reset(&j->it.rdata); CHECK(dns_rdata_fromwire(&j->it.rdata, rdclass, @@ -1927,15 +1946,39 @@ dns_journal_compact(isc_mem_t *mctx, char *filename, isc_uint32_t serial, journal_pos_t best_guess; journal_pos_t current_pos; dns_journal_t *j = NULL; + dns_journal_t *new = NULL; journal_rawheader_t rawheader; unsigned int copy_length; - unsigned int len; + int namelen; char *buf = NULL; unsigned int size = 0; isc_result_t result; unsigned int indexend; + char newname[1024]; + char backup[1024]; + isc_boolean_t is_backup = ISC_FALSE; + + namelen = strlen(filename); + if (namelen > 4 && strcmp(filename + namelen - 4, ".jnl") == 0) + namelen -= 4; + + result = isc_string_printf(newname, sizeof(newname), "%.*s.jnw", + namelen, filename); + if (result != ISC_R_SUCCESS) + return (result); + + result = isc_string_printf(backup, sizeof(backup), "%.*s.jbk", + namelen, filename); + if (result != ISC_R_SUCCESS) + return (result); - CHECK(journal_open(mctx, filename, ISC_TRUE, ISC_FALSE, &j)); + result = journal_open(mctx, filename, ISC_FALSE, ISC_FALSE, &j); + if (result == ISC_R_NOTFOUND) { + is_backup = ISC_TRUE; + result = journal_open(mctx, backup, ISC_FALSE, ISC_FALSE, &j); + } + if (result != ISC_R_SUCCESS) + return (result); if (JOURNAL_EMPTY(&j->header)) { dns_journal_destroy(&j); @@ -1963,6 +2006,8 @@ dns_journal_compact(isc_mem_t *mctx, char *filename, isc_uint32_t serial, dns_journal_destroy(&j); return (ISC_R_SUCCESS); } + + CHECK(journal_open(mctx, newname, ISC_TRUE, ISC_TRUE, &new)); /* * Remove overhead so space test below can succeed. @@ -2003,47 +2048,12 @@ dns_journal_compact(isc_mem_t *mctx, char *filename, isc_uint32_t serial, CHECK(journal_next(j, &best_guess)); /* - * Enough space to proceed? + * 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 ((isc_uint32_t) (j->header.end.offset - best_guess.offset) > - (isc_uint32_t) (best_guess.offset - indexend)) { - dns_journal_destroy(&j); - return (ISC_R_NOSPACE); - } - copy_length = j->header.end.offset - best_guess.offset; - /* - * Invalidate entire index, will be rebuilt at end. - */ - for (i = 0; i < j->header.index_size; i++) { - if (POS_VALID(j->index[i])) - POS_INVALIDATE(j->index[i]); - } - - /* - * Convert the index into on-disk format and write - * it to disk. - */ - CHECK(index_to_disk(j)); - CHECK(journal_fsync(j)); - - /* - * Update the journal header. - */ - if (copy_length == 0) { - j->header.begin.serial = 0; - j->header.end.serial = 0; - j->header.begin.offset = 0; - j->header.end.offset = 0; - } else { - j->header.begin = best_guess; - } - journal_header_encode(&j->header, &rawheader); - CHECK(journal_seek(j, 0)); - CHECK(journal_write(j, &rawheader, sizeof(rawheader))); - CHECK(journal_fsync(j)); - if (copy_length != 0) { /* * Copy best_guess to end into space just freed. @@ -2057,56 +2067,90 @@ dns_journal_compact(isc_mem_t *mctx, char *filename, isc_uint32_t serial, goto failure; } + CHECK(journal_seek(j, best_guess.offset)); + CHECK(journal_seek(new, indexend)); for (i = 0; i < copy_length; i += size) { - len = (copy_length - i) > size ? size : + unsigned int len = (copy_length - i) > size ? size : (copy_length - i); - CHECK(journal_seek(j, best_guess.offset + i)); CHECK(journal_read(j, buf, len)); - CHECK(journal_seek(j, indexend + i)); - CHECK(journal_write(j, buf, len)); + CHECK(journal_write(new, buf, len)); } - CHECK(journal_fsync(j)); + CHECK(journal_fsync(new)); /* * Compute new header. */ - j->header.begin.offset = indexend; - j->header.end.offset = indexend + copy_length; + new->header.begin.serial = best_guess.serial; + new->header.begin.offset = indexend; + new->header.end.serial = j->header.end.serial; + new->header.end.offset = indexend + copy_length; + /* * Update the journal header. */ - journal_header_encode(&j->header, &rawheader); - CHECK(journal_seek(j, 0)); - CHECK(journal_write(j, &rawheader, sizeof(rawheader))); - CHECK(journal_fsync(j)); + journal_header_encode(&new->header, &rawheader); + CHECK(journal_seek(new, 0)); + CHECK(journal_write(new, &rawheader, sizeof(rawheader))); + CHECK(journal_fsync(new)); /* * Build new index. */ - current_pos = j->header.begin; - while (current_pos.serial != j->header.end.serial) { - index_add(j, ¤t_pos); - CHECK(journal_next(j, ¤t_pos)); + current_pos = new->header.begin; + while (current_pos.serial != new->header.end.serial) { + index_add(new, ¤t_pos); + CHECK(journal_next(new, ¤t_pos)); } /* * Write index. */ - CHECK(index_to_disk(j)); - CHECK(journal_fsync(j)); + CHECK(index_to_disk(new)); + CHECK(journal_fsync(new)); + + indexend = new->header.end.offset; + } + dns_journal_destroy(&new); - indexend = j->header.end.offset; + /* + * 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 + * bu EEXISTS. Hopefully all IXFR's that were active at the last + * rename are now complete. + */ + if (rename(newname, filename) == -1) { + if (errno == EACCES && !is_backup) { + result = isc_file_remove(backup); + if (result != ISC_R_SUCCESS && + result != ISC_R_FILENOTFOUND) + goto failure; + if (rename(filename, backup) == -1) + goto maperrno; + if (rename(newname, filename) == -1) + goto maperrno; + (void)isc_file_remove(backup); + } else { + maperrno: + result = ISC_R_FAILURE; + goto failure; + } } + dns_journal_destroy(&j); - (void)isc_file_truncate(filename, (isc_offset_t)indexend); result = ISC_R_SUCCESS; failure: + (void)isc_file_remove(newname); if (buf != NULL) isc_mem_put(mctx, buf, size); if (j != NULL) dns_journal_destroy(&j); + if (new != NULL) + dns_journal_destroy(&new); return (result); } |