[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

Re: [Freeipa-devel] [PATCH 0192-0196] Write all changes to journal



On 23.10.2013 17:20, Petr Spacek wrote:
On 23.10.2013 17:12, Tomas Hozza wrote:
On 10/10/2013 07:05 PM, Petr Spacek wrote:
Hello,

this patch set adds journaling to bind-dyndb-ldap.

Journaling requires proper SOA serial maintenance, so from now
SOA serial auto-incrementation is mandatory.

Journal file is deleted on each start, so IXFR is limited to time frame
from last BIND start.

https://fedorahosted.org/bind-dyndb-ldap/ticket/64


You can use my personal branch for testing:
https://github.com/spacekpe/bind-dyndb-ldap/tree/rbtdb.v7

It contains all unpushed patches. Basically, next master should be identical
to this branch.

Thank you for your time!

-- Petr^2 Spacek

ACK.

IXFR works as should. Also tested that journal is cleared after BIND
restart, so server responds with AXFR.

Patches look good, I have only one small thing to patch 0193:

diff --git a/src/ldap_helper.c b/src/ldap_helper.c
index
0786979a1970f4b61ac5b92dd5554bf87032d1ff..89acbe610519bbe0610a07d5fa5d4ffceddc71cd
100644

--- a/src/ldap_helper.c

+++ b/src/ldap_helper.c

...

@@ -1401,7 +1405,155 @@

cleanup:
return result;
}
+/**
+ * Process strictly minimal diff and detect if data were changed
+ * and return latest SOA RR.
+ *
+ * @pre Input diff has to be minimal, i.e. it can't contain DEL & ADD
operation
+ * for the same data under the same name and TTL.
+ *
+ * @pre If the tuple list contains SOA RR, then exactly one SOA RR
deletion
+ * has to precede exactly one SOA RR addition.
+ * (Each SOA RR deletion has to have matching addition.)
+ *
+ * @param[in] diff Input diff. List of tuples can be empty.
+ * @param[out] soa_latest Pointer to last added SOA RR from tuple list.
+ * Result can be NULL if there is no added SOA RR
+ * in the tuple list.
+ * @param[out] data_changed ISC_TRUE if any data other than SOA serial
were
+ * changed. ISC_FALSE if nothing (except SOA
+ * serial) was changed.
+ *
+ */
+static isc_result_t ATTR_NONNULLS
+diff_analyze_serial(dns_diff_t *diff, dns_difftuple_t **soa_latest,
+ isc_boolean_t *data_changed) {
+ dns_difftuple_t *t = NULL;
+ dns_rdata_t *del_soa = NULL; /* last seen SOA with op == DEL */
+ dns_difftuple_t *tmp_tuple = NULL; /* tuple used for SOA comparison */
+ isc_result_t result = ISC_R_SUCCESS;
+ int ret;
+
+ REQUIRE(DNS_DIFF_VALID(diff));
+ REQUIRE(soa_latest != NULL && *soa_latest == NULL);
+ REQUIRE(data_changed != NULL);
+
+ *data_changed = ISC_FALSE;
+ for (t = HEAD(diff->tuples);
+ t != NULL;
+ t = NEXT(t, link)) {
+ INSIST(tmp_tuple == NULL);
after this ^^^ line tmp_tuple is NULL in the current for loop cycle.
+ if (t->rdata.type != dns_rdatatype_soa)
+ *data_changed = ISC_TRUE;
+ else { /* SOA is always special case */
+ if (t->op == DNS_DIFFOP_DEL ||
+ t->op == DNS_DIFFOP_DELRESIGN) {
+ /* delete operation has to precede add */
+ INSIST(del_soa == NULL);
+ INSIST(tmp_tuple == NULL);
so this ^^^ check is duplicit
+ del_soa = &t->rdata;
+ } else if (t->op == DNS_DIFFOP_ADD ||
+ t->op == DNS_DIFFOP_ADDRESIGN) {
+ /* add operation has to follow a delete */
+ INSIST(del_soa != NULL);
+ INSIST(tmp_tuple == NULL);
and also this ^^^ check is duplicit
+ *soa_latest = t;
+
+ /* detect if fields other than serial
+ * were changed (compute only if necessary) */
+ if (*data_changed == ISC_FALSE) {
+ CHECK(dns_difftuple_copy(t, &tmp_tuple));
+ dns_soa_setserial(dns_soa_getserial(del_soa),
+ &tmp_tuple->rdata);
+ ret = dns_rdata_compare(del_soa,
+ &tmp_tuple->rdata);
+ *data_changed = ISC_TF(ret != 0);
+ }
+ if (tmp_tuple != NULL)
+ dns_difftuple_free(&tmp_tuple);
+ /* re-start the SOA delete-add search cycle */
+ del_soa = NULL;
+ } else {
+ INSIST("unexpected diff: op != ADD || DEL"
+ == NULL);
+ }
+ }
+ }
+ /* SOA deletions & additions has to create self-contained couples */
+ INSIST(del_soa == NULL && tmp_tuple == NULL);
+
+cleanup:
+ if (tmp_tuple != NULL)
+ dns_difftuple_free(&tmp_tuple);
+ return result;
+}
+
...

Sorry for the formatting, obviously my email client is not perfect.
Hope you can understand it...

I will fix it before push.

Patch 192 was unchanged.

Patch 193 v2
- redundant INSIST calls were removed
- patch was rebased

Patch 194 v2
- copyright header was fixed

Patch 196 v2
- README was updated
- missing header dns/journal.h was added
- fixes in error handling: journal is correctly closed as necessary
- patch was rebased

--
Petr^2 Spacek

From 4a16a72257d46b298ef099c959806364ca0572c0 Mon Sep 17 00:00:00 2001
From: Petr Spacek <pspacek redhat com>
Date: Thu, 3 Oct 2013 13:16:30 +0200
Subject: [PATCH] Create working directory for each LDAP instance.

Signed-off-by: Petr Spacek <pspacek redhat com>
---
 src/Makefile.am   |  2 ++
 src/fs.c          | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/fs.h          | 29 ++++++++++++++++++++++++
 src/ldap_helper.c | 27 ++++++++++++++++++++++
 src/settings.c    |  1 +
 5 files changed, 127 insertions(+)
 create mode 100644 src/fs.c
 create mode 100644 src/fs.h

diff --git a/src/Makefile.am b/src/Makefile.am
index 344acdac164d2c31fb8df28275f19633a49031ae..cff074eabbe513fe7a7483fab7b5555eec0c8471 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -4,6 +4,7 @@ bindplugindir=$(libdir)/bind
 HDRS =				\
 	acl.h			\
 	compat.h		\
+	fs.h			\
 	fwd_register.h		\
 	krb5_helper.h		\
 	ldap_convert.h		\
@@ -25,6 +26,7 @@ ldap_la_SOURCES =		\
 	$(HDRS)			\
 	acl.c			\
 	fwd_register.c		\
+	fs.c			\
 	krb5_helper.c		\
 	ldap_convert.c		\
 	ldap_driver.c		\
diff --git a/src/fs.c b/src/fs.c
new file mode 100644
index 0000000000000000000000000000000000000000..97588f46aa698cff33079082e6ba772b92caa97f
--- /dev/null
+++ b/src/fs.c
@@ -0,0 +1,68 @@
+/*
+ * Authors: Petr Spacek <pspacek redhat com>
+ *
+ * Copyright (C) 2013 Red Hat
+ * see file 'COPYING' for use and warranty information
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 or later
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/stat.h>
+
+#include <isc/dir.h>
+#include <isc/errno2result.h>
+#include <isc/result.h>
+#include <isc/util.h>
+
+#include "log.h"
+
+isc_result_t
+fs_dir_create(const char *dir_name)
+{
+	isc_result_t result;
+	char dir_curr[PATH_MAX];
+	isc_dir_t dir_handle;
+	int ret;
+
+	REQUIRE(dir_name != NULL);
+
+	strncpy(dir_curr, "<getcwd() failed>", sizeof(dir_curr));
+	getcwd(dir_curr, sizeof(dir_curr));
+	ret = mkdir(dir_name, 0700);
+	if (ret == 0)
+		result = ISC_R_SUCCESS;
+	else
+		result = isc__errno2result(errno);
+
+	if (result != ISC_R_SUCCESS && result != ISC_R_FILEEXISTS) {
+		log_error_r("unable to create directory '%s', working directory "
+			    "is '%s'", dir_name, dir_curr);
+		return result;
+	}
+
+	/* Verify that the directory is accessible */
+	isc_dir_init(&dir_handle);
+	result = isc_dir_open(&dir_handle, dir_name);
+	if (result == ISC_R_SUCCESS)
+		isc_dir_close(&dir_handle);
+	else
+		log_error_r("unable to open directory '%s', working directory "
+			    "is '%s'", dir_name, dir_curr);
+
+	return result;
+}
diff --git a/src/fs.h b/src/fs.h
new file mode 100644
index 0000000000000000000000000000000000000000..951e04537ffc7fc909b365a0459572f5734cb7eb
--- /dev/null
+++ b/src/fs.h
@@ -0,0 +1,29 @@
+/*
+ * Authors: Petr Spacek <pspacek redhat com>
+ *
+ * Copyright (C) 2013 Red Hat
+ * see file 'COPYING' for use and warranty information
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 or later
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef FS_H_
+#define FS_H_
+
+#include "util.h"
+
+isc_result_t ATTR_NONNULLS
+fs_dir_create(const char *dir_name);
+
+#endif /* FS_H_ */
diff --git a/src/ldap_helper.c b/src/ldap_helper.c
index ce00de85501400245cf1c8dc78163a47ee45c5ba..7db10a84ce776c3451c15b521055fca62b334f44 100644
--- a/src/ldap_helper.c
+++ b/src/ldap_helper.c
@@ -40,6 +40,7 @@
 #include <dns/update.h>
 
 #include <isc/buffer.h>
+#include <isc/dir.h>
 #include <isc/mem.h>
 #include <isc/mutex.h>
 #include <isc/region.h>
@@ -68,6 +69,7 @@
 #include <netdb.h>
 
 #include "acl.h"
+#include "fs.h"
 #include "krb5_helper.h"
 #include "ldap_convert.h"
 #include "ldap_driver.h"
@@ -255,6 +257,7 @@ static const setting_t settings_local_default[] = {
 	{ "dyn_update",			no_default_boolean	},
 	{ "serial_autoincrement",	no_default_string	},
 	{ "verbose_checks",		no_default_boolean	},
+	{ "directory",			no_default_string	},
 	end_of_settings
 };
 
@@ -342,6 +345,8 @@ validate_local_instance_settings(ldap_instance_t *inst, settings_set_t *set) {
 	const char *krb5_principal = NULL;
 	const char *bind_dn = NULL;
 	const char *password = NULL;
+	const char *dir_name = NULL;
+	isc_boolean_t dir_default;
 	ld_string_t *buff = NULL;
 
 	/* handle cache_ttl, psearch, serial_autoincrement, and zone_refresh
@@ -360,6 +365,28 @@ validate_local_instance_settings(ldap_instance_t *inst, settings_set_t *set) {
 		CLEANUP_WITH(ISC_R_UNEXPECTEDEND);
 	}
 
+	/* Use instance name as default working directory */
+	CHECK(str_new(inst->mctx, &buff));
+	CHECK(setting_get_str("directory", inst->local_settings, &dir_name));
+	dir_default = (strcmp(dir_name, "") == 0);
+	if (dir_default == ISC_TRUE)
+		CHECK(str_cat_char(buff, inst->db_name));
+	else
+		CHECK(str_cat_char(buff, dir_name));
+
+	if (str_buf(buff)[str_len(buff) - 1] != '/')
+		CHECK(str_cat_char(buff, "/"));
+
+	if (strcmp(dir_name, str_buf(buff)) != 0)
+		CHECK(setting_set("directory", inst->local_settings,
+				  str_buf(buff), inst->task));
+	str_destroy(&buff);
+	dir_name = NULL;
+	CHECK(setting_get_str("directory", inst->local_settings, &dir_name));
+
+	/* Make sure that working directory exists */
+	CHECK(fs_dir_create(dir_name));
+
 	/* Set timer for deadlock detection inside semaphore_wait_timed . */
 	CHECK(setting_get_uint("timeout", set, &uint));
 	if (semaphore_wait_timeout.seconds < uint*SEM_WAIT_TIMEOUT_MUL)
diff --git a/src/settings.c b/src/settings.c
index 4a1043c78b2de2ed7b7db1e51073b797e8f0750b..fd58bc9ff7d21cad2d4a6f2496325f71cf496f2c 100644
--- a/src/settings.c
+++ b/src/settings.c
@@ -76,6 +76,7 @@ static const setting_t settings_default[] = {
 	{ "update_policy",		default_string("")		},
 	{ "serial_autoincrement",	default_string("")		},
 	{ "verbose_checks",		default_boolean(ISC_FALSE)	},
+	{ "directory",			default_string("")		},
 	end_of_settings
 };
 
-- 
1.8.3.1

From f15ebb6795254a0751f2aa198dd8633a238085fe Mon Sep 17 00:00:00 2001
From: Petr Spacek <pspacek redhat com>
Date: Wed, 9 Oct 2013 15:26:10 +0200
Subject: [PATCH] Rework SOA serial auto-incrementation and make it mandatory.

Journaling requires proper SOA serial maintenance, so from now
the SOA serial auto-incrementation is mandatory.

Signed-off-by: Petr Spacek <pspacek redhat com>
---
 README            |  35 ++++---
 src/ldap_driver.c |  36 -------
 src/ldap_driver.h |   4 -
 src/ldap_helper.c | 298 ++++++++++++++++++++++++++++++++++++++++++++----------
 src/settings.c    |   2 +-
 5 files changed, 265 insertions(+), 110 deletions(-)

diff --git a/README b/README
index d1db893e7260a85f796420eaa6c75d6a88c1212b..4972cf28e5f9f0c38591239eccdd7a63c2bff0a6 100644
--- a/README
+++ b/README
@@ -112,6 +112,23 @@ example zone ldif is available in the doc directory.
 	than "none" are considered as "forward" zones. All records in LDAP
 	belonging to those zones are ignored and all queries are forwarded.
 
+* idnsSOAserial
+	SOA serial number. It is automatically incremented after each change
+	in LDAP. External changes done by other LDAP clients are detected via
+	RFC 4533 (so-called syncrepl).
+
+	If serial number is lower than current UNIX timestamp, then
+	it is set to the timestamp value. If SOA serial is greater or equal
+	to current timestamp, then the serial is incremented by one.
+	(This is equivalent to BIND option 'serial-update-method unix'.)
+
+	In multi-master LDAP environments it is recommended to make
+	idnsSOAserial attribute non-replicated (locally significant).
+	It is recommended not to use multiple masters for single slave zone
+	if SOA serial is locally significant because serial numbers between
+	masters	aren't synchronized. It will cause problems with zone
+	transfers from multiple masters to single slave.
+
 
 5. Configuration
 ================
@@ -214,24 +231,6 @@ ldap_hostname (default "")
 	is used and named service has Kerberos principal different from
 	/bin/hostname output.
 
-serial_autoincrement (default no)
-	Automatically increment SOA serial number after each change in LDAP.
-	External changes done by other LDAP clients are detected with
-	persistent search.
-
-	If serial number is lower than current UNIX timestamp, then
-	it is set to the timestamp value. If SOA serial is greater or equal
-	to current timestamp, then the serial is incremented by one.
-
-	In multi-master LDAP environments it is recommended to make
-	idnsSOAserial attribute non-replicated (locally significant).
-	It is recommended to not use multiple masters for single slave zone
-	if SOA serial is locally significant. Serial numbers between masters
-	aren't synchronized. It will cause problems with zone transfers from
-	multiple masters.
-
-	This function requires persistent search and 4 connections at least.
-
 sync_ptr (default no)
 	Set this option to "yes" if you would like to keep PTR record 
 	synchronized with coresponding A/AAAA record for all zones.
diff --git a/src/ldap_driver.c b/src/ldap_driver.c
index 14520bb7d9f78a03dc376b12d27e56b5a6a9acdd..056af1f63682b778f8bfd1820551639c49091cd4 100644
--- a/src/ldap_driver.c
+++ b/src/ldap_driver.c
@@ -43,7 +43,6 @@
 #include <dns/result.h>
 #include <dns/soa.h>
 #include <dns/types.h>
-#include <dns/update.h>
 
 #include <string.h> /* For memcpy */
 
@@ -252,41 +251,6 @@ attachversion(dns_db_t *db, dns_dbversion_t *source,
 	dns_db_attachversion(ldapdb->rbtdb, source, targetp);
 }
 
-isc_result_t
-update_soa_serial(isc_mem_t *mctx, dns_updatemethod_t method, dns_db_t *db,
-		  dns_dbversion_t *ver, dns_diff_t *diff) {
-	dns_difftuple_t *deltuple = NULL;
-	dns_difftuple_t *addtuple = NULL;
-	isc_uint32_t serial;
-	isc_result_t result;
-	dns_name_t *name = NULL;
-	char buff[DNS_NAME_FORMATSIZE];
-
-	CHECK(dns_db_createsoatuple(db, ver, mctx, DNS_DIFFOP_DEL, &deltuple));
-	CHECK(dns_difftuple_copy(deltuple, &addtuple));
-	addtuple->op = DNS_DIFFOP_ADD;
-
-	serial = dns_soa_getserial(&addtuple->rdata);
-	serial = dns_update_soaserial(serial, method);
-	dns_soa_setserial(serial, &addtuple->rdata);
-	dns_diff_append(diff, &deltuple);
-	dns_diff_append(diff, &addtuple);
-	result = ISC_R_SUCCESS;
-
-cleanup:
-	if (addtuple != NULL)
-		dns_difftuple_free(&addtuple);
-	if (deltuple != NULL)
-		dns_difftuple_free(&deltuple);
-	if (result != ISC_R_SUCCESS) {
-		name = dns_db_origin(db);
-		dns_name_format(name, buff, DNS_NAME_FORMATSIZE);
-		log_error_r("cannot prepare SOA serial incrementation "
-			    "for zone '%s'", buff);
-	}
-	return result;
-}
-
 static void
 closeversion(dns_db_t *db, dns_dbversion_t **versionp, isc_boolean_t commit)
 {
diff --git a/src/ldap_driver.h b/src/ldap_driver.h
index f0f7f97d256344e66731ce7539a069f4759dafff..8cd026c823324dd450c8e738891310a3445dbb2c 100644
--- a/src/ldap_driver.h
+++ b/src/ldap_driver.h
@@ -27,8 +27,4 @@
 
 dns_db_t * ldapdb_get_rbtdb(dns_db_t *db) ATTR_NONNULLS;
 
-isc_result_t
-update_soa_serial(isc_mem_t *mctx, dns_updatemethod_t method, dns_db_t *db,
-		  dns_dbversion_t *ver, dns_diff_t *diff) ATTR_NONNULLS;
-
 #endif /* LDAP_DRIVER_H_ */
diff --git a/src/ldap_helper.c b/src/ldap_helper.c
index c6f98d393cd0c207824953957406b239b717fbc1..ce00de85501400245cf1c8dc78163a47ee45c5ba 100644
--- a/src/ldap_helper.c
+++ b/src/ldap_helper.c
@@ -37,13 +37,14 @@
 #include <dns/byaddr.h>
 #include <dns/forward.h>
 #include <dns/soa.h>
-#include <isc/serial.h>
+#include <dns/update.h>
 
 #include <isc/buffer.h>
 #include <isc/mem.h>
 #include <isc/mutex.h>
 #include <isc/region.h>
 #include <isc/rwlock.h>
+#include <isc/serial.h>
 #include <isc/task.h>
 #include <isc/thread.h>
 #include <isc/time.h>
@@ -252,7 +253,7 @@ static const setting_t settings_local_default[] = {
 	{ "ldap_hostname",		no_default_string	},
 	{ "sync_ptr",			no_default_boolean	},
 	{ "dyn_update",			no_default_boolean	},
-	{ "serial_autoincrement",	no_default_boolean	},
+	{ "serial_autoincrement",	no_default_string	},
 	{ "verbose_checks",		no_default_boolean	},
 	end_of_settings
 };
@@ -343,9 +344,11 @@ validate_local_instance_settings(ldap_instance_t *inst, settings_set_t *set) {
 	const char *password = NULL;
 	ld_string_t *buff = NULL;
 
-	/* handle cache_ttl, psearch and zone_refresh in special way */
+	/* handle cache_ttl, psearch, serial_autoincrement, and zone_refresh
+	 * in special way */
 	const char *obsolete_value = NULL;
-	char *obsolete_options[] = {"cache_ttl", "psearch", "zone_refresh",
+	char *obsolete_options[] = {"cache_ttl", "psearch",
+				    "serial_autoincrement", "zone_refresh",
 				    NULL};
 
 	char print_buff[PRINT_BUFF_SIZE];
@@ -1401,6 +1404,152 @@ cleanup:
 	return result;
 }
 
+/**
+ * Process strictly minimal diff and detect if data were changed
+ * and return latest SOA RR.
+ *
+ * @pre Input diff has to be minimal, i.e. it can't contain DEL & ADD operation
+ *      for the same data under the same name and TTL.
+ *
+ * @pre If the tuple list contains SOA RR, then exactly one SOA RR deletion
+ *      has to precede exactly one SOA RR addition.
+ *      (Each SOA RR deletion has to have matching addition.)
+ *
+ * @param[in]	diff		Input diff. List of tuples can be empty.
+ * @param[out]	soa_latest	Pointer to last added SOA RR from tuple list.
+ *				Result can be NULL if there is no added SOA RR
+ *				in the tuple list.
+ * @param[out]	data_changed	ISC_TRUE if any data other than SOA serial were
+ * 				changed. ISC_FALSE if nothing (except SOA
+ * 				serial) was changed.
+ *
+ */
+static isc_result_t ATTR_NONNULLS
+diff_analyze_serial(dns_diff_t *diff, dns_difftuple_t **soa_latest,
+		    isc_boolean_t *data_changed) {
+	dns_difftuple_t *t = NULL;
+	dns_rdata_t *del_soa = NULL; /* last seen SOA with op == DEL */
+	dns_difftuple_t *tmp_tuple = NULL; /* tuple used for SOA comparison */
+	isc_result_t result = ISC_R_SUCCESS;
+	int ret;
+
+	REQUIRE(DNS_DIFF_VALID(diff));
+	REQUIRE(soa_latest != NULL && *soa_latest == NULL);
+	REQUIRE(data_changed != NULL);
+
+	*data_changed = ISC_FALSE;
+	for (t = HEAD(diff->tuples);
+	     t != NULL;
+	     t = NEXT(t, link)) {
+		INSIST(tmp_tuple == NULL);
+		if (t->rdata.type != dns_rdatatype_soa)
+			*data_changed = ISC_TRUE;
+		else { /* SOA is always special case */
+			if (t->op == DNS_DIFFOP_DEL ||
+			    t->op == DNS_DIFFOP_DELRESIGN) {
+				/* delete operation has to precede add */
+				INSIST(del_soa == NULL);
+				del_soa = &t->rdata;
+			} else if (t->op == DNS_DIFFOP_ADD ||
+				   t->op == DNS_DIFFOP_ADDRESIGN) {
+				/* add operation has to follow a delete */
+				INSIST(del_soa != NULL);
+				*soa_latest = t;
+
+				/* detect if fields other than serial
+				 * were changed (compute only if necessary) */
+				if (*data_changed == ISC_FALSE) {
+					CHECK(dns_difftuple_copy(t, &tmp_tuple));
+					dns_soa_setserial(dns_soa_getserial(del_soa),
+							  &tmp_tuple->rdata);
+					ret = dns_rdata_compare(del_soa,
+								&tmp_tuple->rdata);
+					*data_changed = ISC_TF(ret != 0);
+				}
+				if (tmp_tuple != NULL)
+					dns_difftuple_free(&tmp_tuple);
+				/* re-start the SOA delete-add search cycle */
+				del_soa = NULL;
+			} else {
+				INSIST("unexpected diff: op != ADD || DEL"
+				       == NULL);
+			}
+		}
+	}
+	/* SOA deletions & additions has to create self-contained couples */
+	INSIST(del_soa == NULL && tmp_tuple == NULL);
+
+cleanup:
+	if (tmp_tuple != NULL)
+		dns_difftuple_free(&tmp_tuple);
+	return result;
+}
+
+/**
+ * Increment SOA serial in given diff tuple and return new numeric value.
+ *
+ * @pre Soa_tuple operation is ADD or ADDRESIGN and RR type is SOA.
+ *
+ * @param[in]		method
+ * @param[in,out]	soa_tuple	Latest SOA RR in diff.
+ * @param[out]		new_serial	SOA serial after incrementation.
+ */
+static isc_result_t
+update_soa_serial(dns_updatemethod_t method, dns_difftuple_t *soa_tuple,
+		  isc_uint32_t *new_serial) {
+	isc_uint32_t serial;
+
+	REQUIRE(DNS_DIFFTUPLE_VALID(soa_tuple));
+	REQUIRE(soa_tuple->op == DNS_DIFFOP_ADD ||
+		soa_tuple->op == DNS_DIFFOP_ADDRESIGN);
+	REQUIRE(soa_tuple->rdata.type == dns_rdatatype_soa);
+	REQUIRE(new_serial != NULL);
+
+	serial = dns_soa_getserial(&soa_tuple->rdata);
+	serial = dns_update_soaserial(serial, method);
+	dns_soa_setserial(serial, &soa_tuple->rdata);
+	*new_serial = serial;
+
+	return ISC_R_SUCCESS;
+}
+
+/**
+ * Replace SOA serial in LDAP for given zone.
+ *
+ * @param[in]	inst
+ * @param[in]	zone	Zone name.
+ * @param[in]	serial	New serial.
+ *
+ */
+static isc_result_t ATTR_NONNULLS
+ldap_replace_serial(ldap_instance_t *inst, dns_name_t *zone,
+		    isc_uint32_t serial) {
+	isc_result_t result;
+#define MAX_SERIAL_LENGTH sizeof("4294967295") /* SOA serial is isc_uint32_t */
+	char serial_char[MAX_SERIAL_LENGTH];
+	char *values[2] = { serial_char, NULL };
+	LDAPMod change;
+	LDAPMod *changep[2] = { &change, NULL };
+	ld_string_t *dn = NULL;
+
+	REQUIRE(inst != NULL);
+
+	str_new(inst->mctx, &dn);
+	CHECK(dnsname_to_dn(inst->zone_register, zone, dn));
+
+	change.mod_op = LDAP_MOD_REPLACE;
+	change.mod_type = "idnsSOAserial";
+	change.mod_values = values;
+	CHECK(isc_string_printf(serial_char, MAX_SERIAL_LENGTH, "%u", serial));
+
+	CHECK(ldap_modify_do(inst, str_buf(dn), changep, ISC_FALSE));
+
+cleanup:
+	str_destroy(&dn);
+	return result;
+#undef MAX_SERIAL_LENGTH
+}
+
 /* Parse the master zone entry */
 static isc_result_t ATTR_NONNULLS
 ldap_parse_master_zoneentry(ldap_entry_t *entry, ldap_instance_t *inst)
@@ -1419,19 +1568,21 @@ ldap_parse_master_zoneentry(ldap_entry_t *entry, ldap_instance_t *inst)
 	isc_boolean_t zone_dynamic = ISC_FALSE;
 	settings_set_t *zone_settings = NULL;
 	const char *fake_mname = NULL;
-	isc_boolean_t serial_autoincrement;
-	isc_uint32_t serial;
+	isc_boolean_t data_changed;
+	isc_boolean_t ldap_writeback;
+	isc_uint32_t curr_serial;
+	isc_uint32_t new_serial;
 
 	dns_db_t *rbtdb = NULL;
 	dns_db_t *ldapdb = NULL;
 	dns_diff_t diff;
-	dns_diff_t soa_diff;
 	dns_dbversion_t *version = NULL;
 	dns_dbnode_t *node = NULL;
 	dns_rdatasetiter_t *rbt_rds_iterator = NULL;
+	dns_difftuple_t *soa_tuple = NULL;
+	isc_boolean_t soa_tuple_alloc = ISC_FALSE;
 
 	dns_diff_init(inst->mctx, &diff);
-	dns_diff_init(inst->mctx, &soa_diff);
 
 	REQUIRE(entry != NULL);
 	REQUIRE(inst != NULL);
@@ -1559,6 +1710,7 @@ ldap_parse_master_zoneentry(ldap_entry_t *entry, ldap_instance_t *inst)
 			      &fake_mname));
 	CHECK(ldap_parse_rrentry(inst->mctx, entry, &name, fake_mname,
 				 &rdatalist));
+	CHECK(dns_zone_getserial2(zone, &curr_serial));
 
 	CHECK(zr_get_zone_dbs(inst->zone_register, &name, &ldapdb, &rbtdb));
 	CHECK(dns_db_newversion(rbtdb, &version));
@@ -1573,49 +1725,79 @@ ldap_parse_master_zoneentry(ldap_entry_t *entry, ldap_instance_t *inst)
 		goto cleanup;
 
 	dns_db_detachnode(rbtdb, &node);
-#if RBTDB_DEBUG >= 2
-	dns_diff_print(&diff, stdout);
-#endif
-	CHECK(setting_get_bool("serial_autoincrement", inst->local_settings,
-			       &serial_autoincrement));
-	/* No real change in RR data -> do not increment SOA serial. */
-	if (HEAD(diff.tuples) == NULL)
-		serial_autoincrement = ISC_FALSE;
 
 	/* Detect if SOA serial is affected by the update or not. */
-	CHECK(dns_zone_getserial2(zone, &serial));
-	for (dns_difftuple_t *tp = HEAD(diff.tuples);
-			      tp != NULL && serial_autoincrement == ISC_TRUE;
-			      tp = NEXT(tp, link)) {
-		if (tp->rdata.type == dns_rdatatype_soa) {
-			if (isc_serial_ne(dns_soa_getserial(&tp->rdata), serial)
-			    == ISC_TRUE) {
-				serial_autoincrement = ISC_FALSE;
-			}
+	CHECK(diff_analyze_serial(&diff, &soa_tuple, &data_changed));
+	if (data_changed == ISC_TRUE) {
+		if (soa_tuple == NULL) {
+			/* The diff doesn't contain new SOA serial
+			 * => generate new serial and write it back to LDAP. */
+			ldap_writeback = ISC_TRUE;
+			soa_tuple_alloc = ISC_TRUE;
+			CHECK(dns_db_createsoatuple(ldapdb, version, inst->mctx,
+						    DNS_DIFFOP_DEL, &soa_tuple));
+			dns_diff_appendminimal(&diff, &soa_tuple);
+			CHECK(dns_db_createsoatuple(ldapdb, version, inst->mctx,
+						    DNS_DIFFOP_ADD, &soa_tuple));
+			CHECK(update_soa_serial(dns_updatemethod_unixtime,
+						soa_tuple, &new_serial));
+			dns_diff_appendminimal(&diff, &soa_tuple);
+		} else if (isc_serial_le(dns_soa_getserial(&soa_tuple->rdata),
+					 curr_serial)) {
+			/* The diff tries to send SOA serial back!
+			 * => generate new serial and write it back to LDAP. */
+			ldap_writeback = ISC_TRUE;
+			CHECK(update_soa_serial(dns_updatemethod_unixtime,
+						soa_tuple, &new_serial));
+		} else {
+			/* The diff contains new serial already
+			 * => do nothing. */
+			ldap_writeback = ISC_FALSE;
 		}
+
+	} else {/* if (data_changed == ISC_FALSE) */
+		ldap_writeback = ISC_FALSE;
+		if (soa_tuple == NULL) {
+			/* The diff is empty => do nothing. */
+			INSIST(EMPTY(diff.tuples));
+		} else if (isc_serial_le(dns_soa_getserial(&soa_tuple->rdata),
+				       curr_serial)) {
+			/* Attempt to move serial backwards without any data
+			 * => ignore it. */
+			dns_diff_clear(&diff);
+		}/* else:
+		  * The diff contains new serial already
+		  * => do nothing. */
 	}
 
+#if RBTDB_DEBUG >= 2
+	dns_diff_print(&diff, stdout);
+#else
+	dns_diff_print(&diff, NULL);
+#endif
+	if (ldap_writeback == ISC_TRUE) {
+		dns_zone_log(zone, ISC_LOG_DEBUG(5), "writing new zone serial "
+			     "%u to LDAP", new_serial);
+		result = ldap_replace_serial(inst, &name, new_serial);
+		if (result != ISC_R_SUCCESS)
+			dns_zone_log(zone, ISC_LOG_ERROR,
+				     "serial (%u) write back to LDAP failed",
+				     new_serial);
+	}
 	CHECK(dns_diff_apply(&diff, rbtdb, version));
-	if (serial_autoincrement == ISC_TRUE) {
-		CHECK(update_soa_serial(inst->mctx, dns_updatemethod_unixtime,
-					ldapdb, version, &soa_diff));
-#if RBTDB_DEBUG == 2
-		CHECK(dns_diff_print(&soa_diff, stdout));
-#endif
-		CHECK(dns_diff_apply(&soa_diff, ldapdb, version));
-	}
 
 	dns_db_closeversion(rbtdb, &version, ISC_TRUE);
 
-	CHECK(dns_zone_getserial2(zone, &serial));
+	CHECK(dns_zone_getserial2(zone, &curr_serial));
 	if (publish)
-		dns_zone_log(zone, ISC_LOG_INFO, "loaded serial %u", serial);
+		dns_zone_log(zone, ISC_LOG_INFO, "loaded serial %u", curr_serial);
 	if (zone_dynamic)
 		dns_zone_notify(zone);
 
 cleanup:
 	dns_diff_clear(&diff);
-	dns_diff_clear(&soa_diff);
+	if (soa_tuple_alloc == ISC_TRUE && soa_tuple != NULL)
+		dns_difftuple_free(&soa_tuple);
 	if (rbt_rds_iterator != NULL)
 		dns_rdatasetiter_destroy(&rbt_rds_iterator);
 	if (node != NULL)
@@ -3542,7 +3724,6 @@ update_record(isc_task_t *task, isc_event_t *event)
 	ldap_syncreplevent_t *pevent = (ldap_syncreplevent_t *)event;
 	isc_result_t result;
 	ldap_instance_t *inst = NULL;
-	isc_boolean_t serial_autoincrement;
 	isc_mem_t *mctx;
 	dns_zone_t *zone_ptr = NULL;
 	isc_boolean_t zone_found = ISC_FALSE;
@@ -3554,14 +3735,14 @@ update_record(isc_task_t *task, isc_event_t *event)
 	dns_db_t *rbtdb = NULL;
 	dns_db_t *ldapdb = NULL;
 	dns_diff_t diff;
-	dns_diff_t soa_diff;
+	dns_difftuple_t *soa_tuple = NULL;
+
 	dns_dbversion_t *version = NULL; /* version is shared between rbtdb and ldapdb */
 	dns_dbnode_t *node = NULL; /* node is shared between rbtdb and ldapdb */
 	dns_rdatasetiter_t *rbt_rds_iterator = NULL;
 
 	mctx = pevent->mctx;
 	dns_diff_init(mctx, &diff);
-	dns_diff_init(mctx, &soa_diff);
 
 #ifdef RBTDB_DEBUG
 	static unsigned int count = 0;
@@ -3585,6 +3766,11 @@ update_record(isc_task_t *task, isc_event_t *event)
 	CHECK(zr_get_zone_ptr(inst->zone_register, &origin, &zone_ptr));
 	zone_found = ISC_TRUE;
 
+	/* TODO: Do not bump serial during initial database dump. */
+	/* This optimization is disabled because we don't have reliable
+	 * detection if the initial database dump is finished.
+	 * if (!SYNCREPL_ANY(pevent->chgtype)) ... */
+
 update_restart:
 	rbtdb = NULL;
 	ldapdb = NULL;
@@ -3654,18 +3840,28 @@ update_restart:
 
 	/* No real change in RR data -> do not increment SOA serial. */
 	if (HEAD(diff.tuples) != NULL) {
-#if RBTDB_DEBUG == 2
-		CHECK(dns_diff_print(&diff, stdout));
+		CHECK(dns_db_createsoatuple(ldapdb, version, mctx,
+					    DNS_DIFFOP_DEL, &soa_tuple));
+		dns_diff_append(&diff, &soa_tuple);
+		CHECK(dns_db_createsoatuple(ldapdb, version, mctx,
+					    DNS_DIFFOP_ADD, &soa_tuple));
+		CHECK(update_soa_serial(dns_updatemethod_unixtime,
+					soa_tuple, &serial));
+		dns_zone_log(zone_ptr, ISC_LOG_DEBUG(5),
+			     "writing new zone serial %u to LDAP", serial);
+		result = ldap_replace_serial(inst, &origin, serial);
+		if (result != ISC_R_SUCCESS)
+			dns_zone_log(zone_ptr, ISC_LOG_ERROR,
+				     "serial (%u) write back to LDAP failed",
+				     serial);
+		dns_diff_append(&diff, &soa_tuple);
+
+#if RBTDB_DEBUG >= 2
+		dns_diff_print(&diff, stdout);
+#else
+		dns_diff_print(&diff, NULL);
 #endif
 		CHECK(dns_diff_apply(&diff, rbtdb, version));
-		if (serial_autoincrement == ISC_TRUE) {
-			CHECK(update_soa_serial(mctx, dns_updatemethod_unixtime,
-						ldapdb, version, &soa_diff));
-#if RBTDB_DEBUG == 2
-			CHECK(dns_diff_print(&soa_diff, stdout));
-#endif
-			CHECK(dns_diff_apply(&soa_diff, ldapdb, version));
-		}
 
 		dns_db_closeversion(rbtdb, &version, ISC_TRUE);
 	}
@@ -3681,7 +3877,8 @@ cleanup:
 			 count, isc_mem_inuse(mctx));
 #endif
 	dns_diff_clear(&diff);
-	dns_diff_clear(&soa_diff);
+	if (soa_tuple != NULL)
+		dns_difftuple_free(&soa_tuple);
 	if (rbt_rds_iterator != NULL)
 		dns_rdatasetiter_destroy(&rbt_rds_iterator);
 	if (node != NULL)
@@ -3700,7 +3897,6 @@ cleanup:
 
 		if (zone_ptr != NULL)
 			result = dns_zone_load(zone_ptr);
-
 		if (result == ISC_R_SUCCESS || result == DNS_R_UPTODATE ||
 		    result == DNS_R_DYNAMIC || result == DNS_R_CONTINUE) {
 			/* zone reload succeeded, fire current event again */
diff --git a/src/settings.c b/src/settings.c
index 59909d04a9d9ac0a4a6a132020d3acf84cd1919e..4a1043c78b2de2ed7b7db1e51073b797e8f0750b 100644
--- a/src/settings.c
+++ b/src/settings.c
@@ -74,7 +74,7 @@ static const setting_t settings_default[] = {
 	 * SSU table defined by empty string contains no rules =>
 	 * dns_ssutable_checkrules() will return deny. */
 	{ "update_policy",		default_string("")		},
-	{ "serial_autoincrement",	default_boolean(ISC_FALSE)	},
+	{ "serial_autoincrement",	default_string("")		},
 	{ "verbose_checks",		default_boolean(ISC_FALSE)	},
 	end_of_settings
 };
-- 
1.8.3.1

From 1f3fe8b84c3d217cbd59a7a8ef5cf53e706f82bc Mon Sep 17 00:00:00 2001
From: Petr Spacek <pspacek redhat com>
Date: Fri, 6 Sep 2013 14:55:52 +0200
Subject: [PATCH] Record all changes in DNS zone to journal.

Journal is deleted on each start, so IXFR is limited to time frame
from last daemon start.

https://fedorahosted.org/bind-dyndb-ldap/ticket/64

Signed-off-by: Petr Spacek <pspacek redhat com>
---
 README            | 35 +++++++++++++++++++++++-----
 src/fs.c          | 23 ++++++++++++++++++-
 src/fs.h          |  3 +++
 src/ldap_driver.c | 26 ++++++++++++++-------
 src/ldap_helper.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++----
 5 files changed, 135 insertions(+), 20 deletions(-)

diff --git a/README b/README
index 4972cf28e5f9f0c38591239eccdd7a63c2bff0a6..25f516c9bed9aeea0c547666e75aec3afa5112c4 100644
--- a/README
+++ b/README
@@ -19,6 +19,7 @@ is required.
 * SASL authentication
 * SyncRepl (RFC 4533) for run-time synchronization with LDAP server
 * read-query performance nearly same as with plain BIND
+* AXFR and IXFR zone transfers are supported
 
 
 3. Installation
@@ -151,6 +152,8 @@ this string is the name of the setting and the rest is the value.
 -------------------------
 List of configuration options follows:
 
+5.1.1 LDAP connection
+---------------------
 uri
 	The Uniform Resource Identifier pointing to the LDAP server we
 	wish to connect to. This string is directly passed to the
@@ -213,12 +216,6 @@ timeout (default 10)
 	server don't respond before this timeout then lookup is aborted and
 	BIND returns SERVFAIL. Value "0" means infinite timeout (no timeout).
 
-fake_mname (default "")
-	Ignore value of the idnsSOAmName (primary master DNS name) attribute
-	and use this value instead. This allows multiple BIND processes to share
-	one LDAP database and every BIND reports itself as a primary master in
-	SOA record, for example.
-
 reconnect_interval (default 60)
 	Time (in seconds) after that the plugin should try to connect to LDAP 
 	server again in case connection is lost and immediate reconnection 
@@ -231,6 +228,15 @@ ldap_hostname (default "")
 	is used and named service has Kerberos principal different from
 	/bin/hostname output.
 
+
+5.1.2 Special DNS features
+--------------------------
+fake_mname (default "")
+	Ignore value of the idnsSOAmName (primary master DNS name) attribute
+	and use this value instead. This allows multiple BIND processes to share
+	one LDAP database and every BIND reports itself as a primary master in
+	SOA record, for example.
+
 sync_ptr (default no)
 	Set this option to "yes" if you would like to keep PTR record 
 	synchronized with coresponding A/AAAA record for all zones.
@@ -244,16 +250,30 @@ dyn_update (default no)
 	This setting can be overridden for each zone individually
 	by idnsAllowDynUpdate attribute.
 
+
+5.1.3 Plumbing
+--------------
 verbose_checks (default no)
 	Set this option to "yes" if you would like to log all failures
 	in internal CHECK() macros. This option is recommended only for
 	debugging purposes. It could produce huge amount of log messages
 	on a loaded system!
 
+directory (default is current instance name from dynamic-db directive)
+	Specifies working directory for plug-in. The path has to be writeable
+	by named because plug-in will create sub-directory for each zone.
+	These sub-directories will contain temporary files like zone dump, zone
+	journal, zone keys etc.
+	The path is relative to "directory" specified in BIND options.
+
 5.2 Sample configuration
 ------------------------
 Let's take a look at a sample configuration:
 
+options {
+	directory "/var/named/";
+};
+
 dynamic-db "my_db_name" {
 	library "ldap.so";
 	arg "uri ldap://ldap.example.com";;
@@ -268,6 +288,9 @@ base for entries with object class idnsZone, for which the
 idnsZoneActive attribute is set to True. For each entry it will find, it
 will register a new zone with BIND. The LDAP back-end will keep each
 record it gets from LDAP in its memory.
+Working directory for the plug-in will be "/var/named/my_db_name/",
+so hypothetical zone "example.com" will use sub-directory
+"/var/named/my_db_name/example.com/".
 
 5.3 Configuration in LDAP
 -------------------------
diff --git a/src/fs.c b/src/fs.c
index 97588f46aa698cff33079082e6ba772b92caa97f..ba7003ced895e26b134421c055049e8b9f049fb3 100644
--- a/src/fs.c
+++ b/src/fs.c
@@ -25,12 +25,15 @@
 #include <sys/stat.h>
 
 #include <isc/dir.h>
+#include <isc/file.h>
 #include <isc/errno2result.h>
 #include <isc/result.h>
 #include <isc/util.h>
 
 #include "log.h"
 
+static const char msg_getcwd_failed[PATH_MAX] = "<getcwd() failed>";
+
 isc_result_t
 fs_dir_create(const char *dir_name)
 {
@@ -41,7 +44,7 @@ fs_dir_create(const char *dir_name)
 
 	REQUIRE(dir_name != NULL);
 
-	strncpy(dir_curr, "<getcwd() failed>", sizeof(dir_curr));
+	strncpy(dir_curr, msg_getcwd_failed, sizeof(dir_curr));
 	getcwd(dir_curr, sizeof(dir_curr));
 	ret = mkdir(dir_name, 0700);
 	if (ret == 0)
@@ -66,3 +69,21 @@ fs_dir_create(const char *dir_name)
 
 	return result;
 }
+
+isc_result_t
+fs_file_remove(const char *file_name) {
+	isc_result_t result;
+	char dir_curr[PATH_MAX];
+
+	result = isc_file_remove(file_name);
+	if (result == ISC_R_FILENOTFOUND)
+		result = ISC_R_SUCCESS;
+	else if (result != ISC_R_SUCCESS) {
+		strncpy(dir_curr, msg_getcwd_failed, sizeof(dir_curr));
+		getcwd(dir_curr, sizeof(dir_curr));
+		log_error_r("unable to delete file '%s', working directory "
+			    "is '%s'", file_name, dir_curr);
+	}
+
+	return result;
+}
diff --git a/src/fs.h b/src/fs.h
index 951e04537ffc7fc909b365a0459572f5734cb7eb..b583d3be57b7e1582b61b2bf3554c5d5be805e18 100644
--- a/src/fs.h
+++ b/src/fs.h
@@ -26,4 +26,7 @@
 isc_result_t ATTR_NONNULLS
 fs_dir_create(const char *dir_name);
 
+isc_result_t ATTR_NONNULLS
+fs_file_remove(const char *file_name);
+
 #endif /* FS_H_ */
diff --git a/src/ldap_driver.c b/src/ldap_driver.c
index 056af1f63682b778f8bfd1820551639c49091cd4..ee12fdfa7ca1d6fdecb679da47fac00baea5ffc4 100644
--- a/src/ldap_driver.c
+++ b/src/ldap_driver.c
@@ -120,19 +120,29 @@ static void ATTR_NONNULLS
 free_ldapdb(ldapdb_t *ldapdb)
 {
 #ifdef RBTDB_DEBUG
-#define PATH "/var/named/dump/"
 	isc_result_t result;
 	dns_dbversion_t *version = NULL;
-	char filename[DNS_NAME_FORMATSIZE + sizeof(PATH)] = PATH;
+	dns_name_t *zone_name = dns_db_origin(&ldapdb->common);
+	ld_string_t *file_name = NULL;
 
-	dns_name_format(&ldapdb->common.origin, filename + sizeof(PATH) - 1,
-			DNS_NAME_FORMATSIZE);
+	CHECK(zr_get_zone_path(ldapdb->common.mctx,
+			       ldap_instance_getsettings_local(ldapdb->ldap_inst),
+			       zone_name, "ldapdb.dump", &file_name));
 	dns_db_currentversion(ldapdb->rbtdb, &version);
-	log_error("dump to '%s' started", filename);
-	result = dns_db_dump2(ldapdb->rbtdb, version, filename, dns_masterformat_text);
-	log_error_r("dump to '%s' finished", filename);
+	log_info("dump to '%s' started", str_buf(file_name));
+	result = dns_db_dump2(ldapdb->rbtdb, version, str_buf(file_name),
+			      dns_masterformat_text);
+	log_info("dump to '%s' finished: %s", str_buf(file_name),
+		 isc_result_totext(result));
 	dns_db_closeversion(ldapdb->rbtdb, &version, ISC_FALSE);
-#undef PATH
+
+cleanup:
+	if (result != ISC_R_SUCCESS) {
+		log_error_r("dump to '%s' failed",
+				(file_name && str_buf(file_name)) ?
+				str_buf(file_name) : "<NULL>");
+	}
+	str_destroy(&file_name);
 #endif
 	dns_db_detach(&ldapdb->rbtdb);
 	dns_name_free(&ldapdb->common.origin, ldapdb->common.mctx);
diff --git a/src/ldap_helper.c b/src/ldap_helper.c
index 6e77d79fd99e1f7417aa53de6231eb223c6d97e8..95e84b127b09d5df691cd956f44b46d05adb671c 100644
--- a/src/ldap_helper.c
+++ b/src/ldap_helper.c
@@ -22,6 +22,7 @@
 
 #include <dns/dynamic_db.h>
 #include <dns/diff.h>
+#include <dns/journal.h>
 #include <dns/rbt.h>
 #include <dns/rdata.h>
 #include <dns/rdataclass.h>
@@ -45,14 +46,14 @@
 #include <isc/mutex.h>
 #include <isc/region.h>
 #include <isc/rwlock.h>
-#include <isc/serial.h>
 #include <isc/task.h>
 #include <isc/thread.h>
 #include <isc/time.h>
 #include <isc/util.h>
 #include <isc/netaddr.h>
 #include <isc/parseint.h>
 #include <isc/timer.h>
+#include <isc/serial.h>
 #include <isc/string.h>
 
 #include <alloca.h>
@@ -255,7 +256,7 @@ static const setting_t settings_local_default[] = {
 	{ "ldap_hostname",		no_default_string	},
 	{ "sync_ptr",			no_default_boolean	},
 	{ "dyn_update",			no_default_boolean	},
-	{ "serial_autoincrement",	no_default_string	},
+	{ "serial_autoincrement",	no_default_string	}, /* No longer supported */
 	{ "verbose_checks",		no_default_boolean	},
 	{ "directory",			no_default_string	},
 	end_of_settings
@@ -1431,6 +1432,24 @@ cleanup:
 	return result;
 }
 
+static isc_result_t ATTR_NONNULLS
+configure_paths(isc_mem_t *mctx, ldap_instance_t *inst, dns_zone_t *zone,
+		isc_boolean_t issecure) {
+	isc_result_t result;
+	ld_string_t *file_name = NULL;
+
+	CHECK(zr_get_zone_path(mctx, ldap_instance_getsettings_local(inst),
+			       dns_zone_getorigin(zone),
+			       (issecure ? "signed" : "raw"), &file_name));
+	CHECK(dns_zone_setfile(zone, str_buf(file_name)));
+	CHECK(fs_file_remove(dns_zone_getfile(zone)));
+	CHECK(fs_file_remove(dns_zone_getjournal(zone)));
+
+cleanup:
+	str_destroy(&file_name);
+	return result;
+}
+
 /**
  * Process strictly minimal diff and detect if data were changed
  * and return latest SOA RR.
@@ -1585,6 +1604,7 @@ ldap_parse_master_zoneentry(ldap_entry_t *entry, ldap_instance_t *inst)
 	ldap_valuelist_t values;
 	dns_name_t name;
 	dns_zone_t *zone = NULL;
+	dns_zone_t *zone_raw = NULL;
 	isc_result_t result;
 	isc_boolean_t unlock = ISC_FALSE;
 	isc_boolean_t publish = ISC_FALSE;
@@ -1609,6 +1629,9 @@ ldap_parse_master_zoneentry(ldap_entry_t *entry, ldap_instance_t *inst)
 	dns_difftuple_t *soa_tuple = NULL;
 	isc_boolean_t soa_tuple_alloc = ISC_FALSE;
 
+	dns_journal_t *journal = NULL;
+	char *journal_filename = NULL;
+
 	dns_diff_init(inst->mctx, &diff);
 
 	REQUIRE(entry != NULL);
@@ -1650,6 +1673,7 @@ ldap_parse_master_zoneentry(ldap_entry_t *entry, ldap_instance_t *inst)
 	result = zr_get_zone_ptr(inst->zone_register, &name, &zone);
 	if (result == ISC_R_NOTFOUND || result == DNS_R_PARTIALMATCH) {
 		CHECK(create_zone(inst, &name, &zone));
+		CHECK(configure_paths(inst->mctx, inst, zone, ISC_FALSE));
 		CHECK(zr_add_zone(inst->zone_register, zone, dn));
 		publish = ISC_TRUE;
 		log_debug(2, "created zone %p: %s", zone, dn);
@@ -1811,9 +1835,22 @@ ldap_parse_master_zoneentry(ldap_entry_t *entry, ldap_instance_t *inst)
 				     "serial (%u) write back to LDAP failed",
 				     new_serial);
 	}
-	CHECK(dns_diff_apply(&diff, rbtdb, version));
 
-	dns_db_closeversion(rbtdb, &version, ISC_TRUE);
+	if (!EMPTY(diff.tuples)) {
+		/* write the transaction to journal */
+		dns_zone_getraw(zone, &zone_raw);
+		if (zone_raw == NULL)
+			journal_filename = dns_zone_getjournal(zone);
+		else
+			journal_filename = dns_zone_getjournal(zone_raw);
+		CHECK(dns_journal_open(inst->mctx, journal_filename,
+				       DNS_JOURNAL_CREATE, &journal));
+		CHECK(dns_journal_write_transaction(journal, &diff));
+
+		/* commit */
+		CHECK(dns_diff_apply(&diff, rbtdb, version));
+		dns_db_closeversion(rbtdb, &version, ISC_TRUE);
+	}
 
 	CHECK(dns_zone_getserial2(zone, &curr_serial));
 	if (publish)
@@ -1833,6 +1870,8 @@ cleanup:
 		dns_db_closeversion(rbtdb, &version, ISC_FALSE); /* rollback */
 	if (rbtdb != NULL)
 		dns_db_detach(&rbtdb);
+	if (journal != NULL)
+		dns_journal_destroy(&journal);
 	if (ldapdb != NULL)
 		dns_db_detach(&ldapdb);
 	if (publish && !published) { /* Failure in ACL parsing or so. */
@@ -3753,6 +3792,7 @@ update_record(isc_task_t *task, isc_event_t *event)
 	ldap_instance_t *inst = NULL;
 	isc_mem_t *mctx;
 	dns_zone_t *zone_ptr = NULL;
+	dns_zone_t *zone_raw = NULL;
 	isc_boolean_t zone_found = ISC_FALSE;
 	isc_boolean_t zone_reloaded = ISC_FALSE;
 	isc_uint32_t serial;
@@ -3768,6 +3808,9 @@ update_record(isc_task_t *task, isc_event_t *event)
 	dns_dbnode_t *node = NULL; /* node is shared between rbtdb and ldapdb */
 	dns_rdatasetiter_t *rbt_rds_iterator = NULL;
 
+	dns_journal_t *journal = NULL;
+	char *journal_filename = NULL;
+
 	mctx = pevent->mctx;
 	dns_diff_init(mctx, &diff);
 
@@ -3801,6 +3844,7 @@ update_record(isc_task_t *task, isc_event_t *event)
 update_restart:
 	rbtdb = NULL;
 	ldapdb = NULL;
+	journal = NULL;
 	ldapdb_rdatalist_destroy(mctx, &rdatalist);
 	CHECK(zr_get_zone_dbs(inst->zone_register, &name, &ldapdb, &rbtdb));
 	CHECK(dns_db_newversion(rbtdb, &version));
@@ -3888,6 +3932,17 @@ update_restart:
 #else
 		dns_diff_print(&diff, NULL);
 #endif
+		/* write the transaction to journal */
+		dns_zone_getraw(zone_ptr, &zone_raw);
+		if (zone_raw == NULL)
+			journal_filename = dns_zone_getjournal(zone_ptr);
+		else
+			journal_filename = dns_zone_getjournal(zone_raw);
+		CHECK(dns_journal_open(mctx, journal_filename,
+				       DNS_JOURNAL_CREATE, &journal));
+		CHECK(dns_journal_write_transaction(journal, &diff));
+
+		/* commit */
 		CHECK(dns_diff_apply(&diff, rbtdb, version));
 		dns_db_closeversion(rbtdb, &version, ISC_TRUE);
 	}
@@ -3909,10 +3964,13 @@ cleanup:
 		dns_rdatasetiter_destroy(&rbt_rds_iterator);
 	if (node != NULL)
 		dns_db_detachnode(rbtdb, &node);
+	/* rollback */
 	if (rbtdb != NULL && version != NULL)
-		dns_db_closeversion(rbtdb, &version, ISC_FALSE); /* rollback */
+		dns_db_closeversion(rbtdb, &version, ISC_FALSE);
 	if (rbtdb != NULL)
 		dns_db_detach(&rbtdb);
+	if (journal != NULL)
+		dns_journal_destroy(&journal);
 	if (ldapdb != NULL)
 		dns_db_detach(&ldapdb);
 	if (result != ISC_R_SUCCESS && zone_found && !zone_reloaded &&
-- 
1.8.3.1


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]