]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
dns_journal_compact()
authorMark Andrews <marka@isc.org>
Thu, 30 Aug 2001 05:04:18 +0000 (05:04 +0000)
committerMark Andrews <marka@isc.org>
Thu, 30 Aug 2001 05:04:18 +0000 (05:04 +0000)
lib/dns/include/dns/journal.h
lib/dns/journal.c

index e28dc83ef48f99c50dee7d30be358b40fe478858..7d53ad40db9fc6b0944b6427c66d3c5b15dd5bf6 100644 (file)
@@ -15,7 +15,7 @@
  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: journal.h,v 1.23 2001/05/21 23:56:33 gson Exp $ */
+/* $Id: journal.h,v 1.24 2001/08/30 05:04:18 marka Exp $ */
 
 #ifndef DNS_JOURNAL_H
 #define DNS_JOURNAL_H 1
@@ -257,6 +257,14 @@ dns_db_diff(isc_mem_t *mctx,
  * entry to the journal file specified by 'journal_filename'.
  */
 
+isc_result_t
+dns_journal_compact(isc_mem_t *mctx, char *filename, isc_uint32_t serial,
+                    isc_uint32_t target_size);
+/*
+ * Attempt to compact the journal if it is greater that 'target_size'.
+ * Changes from 'serial' onwards will be preserved.  If the journal
+ * exists and is non-empty 'serial' must exist in the journal.
+ */
 
 ISC_LANG_ENDDECLS
 
index 54c4c4480cbf3f0d37efba6b3f8f6e94c7416830..fb1af8b1ec9ec4dbea8e7e0a1fc1f039ad1cde22 100644 (file)
@@ -15,7 +15,7 @@
  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: journal.c,v 1.77 2001/08/06 02:10:59 marka Exp $ */
+/* $Id: journal.c,v 1.78 2001/08/30 05:04:17 marka Exp $ */
 
 #include <config.h>
 
@@ -538,10 +538,9 @@ journal_file_create(isc_mem_t *mctx, const char *filename) {
        return (ISC_R_SUCCESS);
 }
 
-
-isc_result_t
-dns_journal_open(isc_mem_t *mctx, const char *filename, isc_boolean_t write,
-                dns_journal_t **journalp) {
+static isc_result_t
+journal_open(isc_mem_t *mctx, const char *filename, isc_boolean_t write,
+            isc_boolean_t create, dns_journal_t **journalp) {
        FILE *fp = NULL;
        isc_result_t result;
        journal_rawheader_t rawheader;
@@ -562,7 +561,7 @@ dns_journal_open(isc_mem_t *mctx, const char *filename, isc_boolean_t write,
        result = isc_stdio_open(j->filename, write ? "rb+" : "rb", &fp);
 
        if (result == ISC_R_FILENOTFOUND) {
-               if (write) {
+               if (create) {
                        isc_log_write(JOURNAL_COMMON_LOGARGS,
                                      ISC_LOG_INFO,
                                      "journal file %s does not exist, "
@@ -669,6 +668,12 @@ dns_journal_open(isc_mem_t *mctx, const char *filename, isc_boolean_t write,
        return (result);
 }
 
+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));
+}
+
 /*
  * A comparison function defining the sorting order for
  * entries in the IXFR-style journal file.
@@ -728,6 +733,8 @@ journal_next(dns_journal_t *j, journal_pos_t *pos) {
        if (result != ISC_R_SUCCESS)
                return (result);
 
+       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.
@@ -1906,3 +1913,227 @@ dns_db_diff(isc_mem_t *mctx,
        dns_journal_destroy(&journal);
        return (result);
 }
+
+static isc_result_t index_to_disk(dns_journal_t *) ;
+
+isc_result_t
+dns_journal_compact(isc_mem_t *mctx, char *filename, isc_uint32_t serial,
+                   isc_uint32_t target_size)
+{
+       unsigned int i;
+       journal_pos_t best_guess;
+       journal_pos_t current_pos;
+       dns_journal_t *j = NULL;
+       journal_rawheader_t rawheader;
+       unsigned int copy_length;
+       unsigned int len;
+       char *buf = NULL;
+       unsigned int size = 0;
+       isc_result_t result;
+       unsigned int indexend;
+
+       CHECK(journal_open(mctx, filename, ISC_TRUE, ISC_FALSE, &j));
+
+       if (JOURNAL_EMPTY(&j->header)) {
+               dns_journal_destroy(&j);
+               return (ISC_R_SUCCESS);
+       }
+               
+       if (DNS_SERIAL_GT(j->header.begin.serial, serial) ||
+           DNS_SERIAL_GT(serial, j->header.end.serial)) {
+               dns_journal_destroy(&j);
+               return (ISC_R_RANGE);
+       }
+
+       /*
+        * Cope with very small target sizes.
+        */
+       indexend = sizeof(journal_rawheader_t) +
+                  j->header.index_size * sizeof(journal_rawpos_t);
+       if (target_size < indexend * 2)
+               target_size = target_size/2 + indexend;
+
+       /*
+        * See if there is any work to do.
+        */
+       if (j->header.end.offset < target_size) {
+               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.
+        */
+       best_guess = j->header.begin;
+       for (i = 0; i < j->header.index_size; i++) {
+               if (POS_VALID(j->index[i]) &&
+                   DNS_SERIAL_GE(serial, j->index[i].serial) &&
+                   (j->header.end.offset - j->index[i].offset >=
+                    target_size / 2) &&
+                   j->index[i].offset > best_guess.offset)
+                       best_guess = j->index[i];
+       }
+
+       current_pos = best_guess;
+       while (current_pos.serial != serial) {
+               CHECK(journal_next(j, &current_pos));
+               if (current_pos.serial == j->header.end.serial)
+                       break;
+
+               if (DNS_SERIAL_GE(serial, current_pos.serial) &&
+                   ((j->header.end.offset - current_pos.offset) >=
+                    (target_size / 2)) &&
+                   current_pos.offset > best_guess.offset)
+                       best_guess = current_pos;
+               else
+                       break;
+       }
+
+       INSIST(best_guess.serial != j->header.end.serial);
+       if (best_guess.serial != serial)
+               CHECK(journal_next(j, &best_guess));
+
+       /*
+        * Enough space to proceed?
+        */
+       if (j->header.end.offset - best_guess.offset >
+            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 to space just freed.
+                */
+               size = 64*1024;
+               if (copy_length < size)
+                       size = copy_length;
+               buf = isc_mem_get(mctx, size);
+               if (buf == NULL) {
+                       result = ISC_R_NOMEMORY;
+                       goto failure;
+               }
+       
+               for (i = 0; i < copy_length; i += size) {
+                       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));
+               }
+
+               /*
+                * Convert the index into on-disk format and write
+                * it to disk.
+                */
+               CHECK(index_to_disk(j));
+               CHECK(journal_fsync(j));
+
+               /*
+                * Compute new header.
+                */
+               j->header.begin.offset = indexend;
+               j->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));
+
+               /*
+                * Build new index.
+                */
+               current_pos = j->header.begin;
+               while (current_pos.serial != j->header.end.serial) {
+                       index_add(j, &current_pos);
+                       CHECK(journal_next(j, &current_pos));
+               }
+
+               /*
+                * Write index.
+                */
+               CHECK(index_to_disk(j));
+               CHECK(journal_fsync(j));
+
+               indexend = j->header.end.offset;
+       }
+       dns_journal_destroy(&j);
+       (void)isc_file_truncate(filename, (isc_offset_t)indexend);
+       result = ISC_R_SUCCESS;
+
+ failure:
+       if (buf != NULL)
+               isc_mem_put(mctx, buf, size);
+       if (j != NULL)
+               dns_journal_destroy(&j);
+       return (result);
+}
+
+static isc_result_t
+index_to_disk(dns_journal_t *j) {
+       isc_result_t result = ISC_R_SUCCESS;
+
+       if (j->header.index_size != 0) {
+               unsigned int i;
+               unsigned char *p;
+               unsigned int rawbytes;
+
+               rawbytes = j->header.index_size * sizeof(journal_rawpos_t);
+
+               p = j->rawindex;
+               for (i = 0; i < j->header.index_size; i++) {
+                       encode_uint32(j->index[i].serial, p);
+                       p += 4;
+                       encode_uint32(j->index[i].offset, p);
+                       p += 4;
+               }
+               INSIST(p == j->rawindex + rawbytes);
+
+               CHECK(journal_write(j, j->rawindex, rawbytes));
+       }
+failure:
+       return (result);
+}