]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Abstract updates into a vtable
authorAlessio Podda <alessio@isc.org>
Fri, 24 Oct 2025 22:47:42 +0000 (00:47 +0200)
committerAlessio Podda <alessio@isc.org>
Thu, 29 Jan 2026 08:13:02 +0000 (09:13 +0100)
This commit adds a layer of indirection to the apply_diff logic used by
IXFR and resigning by having the database updates go through a vtable.

We do this in three steps:
 - We extend dns_rdatacallbacks_t vtable to allow subtraction and
   resigning.
 - We add a new set of api (begin|commit|abort)update to the dbmethods
   vtable, that model an incremental update that can be aborted.
 - We extract the core logic of diff_apply into a function that
   satisfies the new interface.
 - We make diff_apply use this new function, and log the results.

The intent of this commit is to allow databases to expose a batch
incremental update implementation, just like they expose a custom
batch creation implementation through (begin|end)load.

(cherry picked from commit e36dc0ca761898ff951d101281bbf39bf2535ec5)

lib/dns/db.c
lib/dns/diff.c
lib/dns/include/dns/callbacks.h
lib/dns/include/dns/db.h
lib/dns/include/dns/diff.h
lib/dns/include/dns/types.h
lib/dns/master.c
lib/dns/qpzone.c
lib/dns/rbt-zonedb.c
lib/dns/xfrin.c

index 19417acbfaafd0fbe8c51b1b43318155de9edd87..987ddc7cebdbd750eee7581097124b81aec1f0d8 100644 (file)
@@ -315,6 +315,56 @@ dns_db_endload(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
        return ISC_R_NOTIMPLEMENTED;
 }
 
+isc_result_t
+dns_db_beginupdate(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
+       /*
+        * Begin updating 'db'.
+        */
+
+       REQUIRE(DNS_DB_VALID(db));
+       REQUIRE(dns_db_iszone(db));
+       REQUIRE(DNS_CALLBACK_VALID(callbacks));
+
+       if (db->methods->beginupdate != NULL) {
+               return (db->methods->beginupdate)(db, callbacks);
+       }
+       return ISC_R_NOTIMPLEMENTED;
+}
+
+isc_result_t
+dns_db_commitupdate(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
+       /*
+        * Commit the update to 'db'.
+        */
+
+       REQUIRE(DNS_DB_VALID(db));
+       REQUIRE(dns_db_iszone(db));
+       REQUIRE(DNS_CALLBACK_VALID(callbacks));
+
+       if (db->methods->commitupdate != NULL) {
+               return (db->methods->commitupdate)(db, callbacks);
+       }
+
+       return ISC_R_NOTIMPLEMENTED;
+}
+
+isc_result_t
+dns_db_abortupdate(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
+       /*
+        * Abort the update to 'db'.
+        */
+
+       REQUIRE(DNS_DB_VALID(db));
+       REQUIRE(dns_db_iszone(db));
+       REQUIRE(DNS_CALLBACK_VALID(callbacks));
+
+       if (db->methods->abortupdate != NULL) {
+               return (db->methods->abortupdate)(db, callbacks);
+       }
+
+       return ISC_R_NOTIMPLEMENTED;
+}
+
 isc_result_t
 dns_db_load(dns_db_t *db, const char *filename, dns_masterformat_t format,
            unsigned int options) {
index 88333abc0fe121d20728cc8fb05218b61a451ce6..30fbe6be66e231a3a5ed93957cfce73061925b7c 100644 (file)
@@ -211,7 +211,6 @@ dns_diff_appendminimal(dns_diff_t *diff, dns_difftuple_t **tuplep) {
        }
 }
 
-
 static void
 getownercase(dns_rdataset_t *rdataset, dns_name_t *name) {
        if (dns_rdataset_isassociated(rdataset)) {
@@ -226,6 +225,91 @@ setownercase(dns_rdataset_t *rdataset, const dns_name_t *name) {
        }
 }
 
+static isc_result_t
+update_rdataset(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
+               dns_rdataset_t *rds, dns_diffop_t op) {
+       isc_result_t result;
+       unsigned int options;
+       dns_rdataset_t ardataset;
+       dns_dbnode_t *node = NULL;
+       bool is_resign;
+
+       dns_rdataset_init(&ardataset);
+
+       is_resign = rds->type == dns_rdatatype_rrsig &&
+                   (op == DNS_DIFFOP_DELRESIGN || op == DNS_DIFFOP_ADDRESIGN);
+
+       if (rds->type != dns_rdatatype_nsec3 &&
+           rds->covers != dns_rdatatype_nsec3)
+       {
+               CHECK(dns_db_findnode(db, name, true, &node));
+       } else {
+               CHECK(dns_db_findnsec3node(db, name, true, &node));
+       }
+
+       switch (op) {
+       case DNS_DIFFOP_ADD:
+       case DNS_DIFFOP_ADDRESIGN:
+               options = DNS_DBADD_MERGE | DNS_DBADD_EXACT |
+                         DNS_DBADD_EXACTTTL;
+               CHECK(dns_db_addrdataset(db, node, ver, 0, rds, options,
+                                           &ardataset));
+               switch (result) {
+               case ISC_R_SUCCESS:
+               case DNS_R_UNCHANGED:
+               case DNS_R_NXRRSET:
+                       setownercase(&ardataset, name);
+                       CHECK(result);
+                       break;
+               default:
+                       CHECK(result);
+                       break;
+               }
+               break;
+       case DNS_DIFFOP_DEL:
+       case DNS_DIFFOP_DELRESIGN:
+               options = DNS_DBSUB_EXACT | DNS_DBSUB_WANTOLD;
+               result = dns_db_subtractrdataset(db, node, ver, rds, options,
+                                                &ardataset);
+               switch (result) {
+               case ISC_R_SUCCESS:
+               case DNS_R_UNCHANGED:
+               case DNS_R_NXRRSET:
+                       getownercase(&ardataset, name);
+                       CHECK(result);
+                       break;
+               default:
+                       CHECK(result);
+                       break;
+               }
+               break;
+       default:
+               UNREACHABLE();
+       }
+
+       if (is_resign) {
+               isc_stdtime_t resign;
+               resign = dns_rdataset_minresign(&ardataset);
+               dns_db_setsigningtime(db, &ardataset, resign);
+       }
+
+cleanup:
+       if (node != NULL) {
+               dns_db_detachnode(db, &node);
+       }
+       if (dns_rdataset_isassociated(&ardataset)) {
+               dns_rdataset_disassociate(&ardataset);
+       }
+       return result;
+}
+
+static isc_result_t
+update_callback(void *arg, const dns_name_t *name, dns_rdataset_t *rds,
+               dns_diffop_t op DNS__DB_FLARG) {
+       dns_updatectx_t *ctx = arg;
+       return update_rdataset(ctx->db, ctx->ver, (dns_name_t *)name, rds, op);
+}
+
 static const char *
 optotext(dns_diffop_t op) {
        switch (op) {
@@ -243,23 +327,24 @@ optotext(dns_diffop_t op) {
 }
 
 static isc_result_t
-diff_apply(const dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *ver,
-          bool warn) {
+diff_apply(const dns_diff_t *diff, dns_rdatacallbacks_t *callbacks) {
        dns_difftuple_t *t;
-       dns_dbnode_t *node = NULL;
        isc_result_t result;
        char namebuf[DNS_NAME_FORMATSIZE];
        char typebuf[DNS_RDATATYPE_FORMATSIZE];
        char classbuf[DNS_RDATACLASS_FORMATSIZE];
+       dns_updatectx_t *ctx;
 
        REQUIRE(DNS_DIFF_VALID(diff));
-       REQUIRE(DNS_DB_VALID(db));
+       REQUIRE(callbacks != NULL);
+       REQUIRE(callbacks->update != NULL);
+
+       ctx = callbacks->add_private;
 
        t = ISC_LIST_HEAD(diff->tuples);
        while (t != NULL) {
                dns_name_t *name;
 
-               INSIST(node == NULL);
                name = &t->name;
                /*
                 * Find the node.
@@ -276,8 +361,6 @@ diff_apply(const dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *ver,
                        dns_diffop_t op;
                        dns_rdatalist_t rdl;
                        dns_rdataset_t rds;
-                       dns_rdataset_t ardataset;
-                       unsigned int options;
 
                        op = t->op;
                        type = t->rdata.type;
@@ -305,16 +388,6 @@ diff_apply(const dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *ver,
                        rdl.rdclass = t->rdata.rdclass;
                        rdl.ttl = t->ttl;
 
-                       node = NULL;
-                       if (type != dns_rdatatype_nsec3 &&
-                           covers != dns_rdatatype_nsec3)
-                       {
-                               CHECK(dns_db_findnode(db, name, true, &node));
-                       } else {
-                               CHECK(dns_db_findnsec3node(db, name, true,
-                                                          &node));
-                       }
-
                        while (t != NULL && dns_name_equal(&t->name, name) &&
                               t->op == op && t->rdata.type == type &&
                               rdata_covers(&t->rdata) == covers)
@@ -324,7 +397,7 @@ diff_apply(const dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *ver,
                                 * dns_rdataset_setownercase.
                                 */
                                name = &t->name;
-                               if (t->ttl != rdl.ttl && warn) {
+                               if (t->ttl != rdl.ttl && ctx->warn) {
                                        dns_name_format(name, namebuf,
                                                        sizeof(namebuf));
                                        dns_rdatatype_format(t->rdata.type,
@@ -352,54 +425,22 @@ diff_apply(const dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *ver,
                         * Convert the rdatalist into a rdataset.
                         */
                        dns_rdataset_init(&rds);
-                       dns_rdataset_init(&ardataset);
                        dns_rdatalist_tordataset(&rdl, &rds);
                        rds.trust = dns_trust_ultimate;
 
                        /*
                         * Merge the rdataset into the database.
                         */
-                       switch (op) {
-                       case DNS_DIFFOP_ADD:
-                       case DNS_DIFFOP_ADDRESIGN:
-                               options = DNS_DBADD_MERGE | DNS_DBADD_EXACT |
-                                         DNS_DBADD_EXACTTTL;
-                               result = dns_db_addrdataset(db, node, ver, 0,
-                                                           &rds, options,
-                                                           &ardataset);
-                               break;
-                       case DNS_DIFFOP_DEL:
-                       case DNS_DIFFOP_DELRESIGN:
-                               options = DNS_DBSUB_EXACT | DNS_DBSUB_WANTOLD;
-                               result = dns_db_subtractrdataset(db, node, ver,
-                                                                &rds, options,
-                                                                &ardataset);
-                               break;
-                       default:
-                               UNREACHABLE();
-                       }
+                       result = callbacks->update(callbacks->add_private, name,
+                                                  &rds, op DNS__DB_FILELINE);
 
-                       if (result == ISC_R_SUCCESS) {
-                               if (rds.type == dns_rdatatype_rrsig &&
-                                   (op == DNS_DIFFOP_DELRESIGN ||
-                                    op == DNS_DIFFOP_ADDRESIGN))
-                               {
-                                       isc_stdtime_t resign;
-                                       resign = dns_rdataset_minresign(&ardataset);
-                                       dns_db_setsigningtime(db, &ardataset,
-                                                             resign);
-                               }
-                               if (op == DNS_DIFFOP_ADD ||
-                                   op == DNS_DIFFOP_ADDRESIGN)
-                               {
-                                       setownercase(&ardataset, name);
-                               }
-                               if (op == DNS_DIFFOP_DEL ||
-                                   op == DNS_DIFFOP_DELRESIGN)
-                               {
-                                       getownercase(&ardataset, name);
-                               }
-                       } else if (result == DNS_R_UNCHANGED) {
+                       switch (result) {
+                       case ISC_R_SUCCESS:
+                               /*
+                                * OK.
+                                */
+                               break;
+                       case DNS_R_UNCHANGED:
                                /*
                                 * This will not happen when executing a
                                 * dynamic update, because that code will
@@ -408,87 +449,83 @@ diff_apply(const dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *ver,
                                 * from a server that is not as careful.
                                 * Issue a warning and continue.
                                 */
-                               if (warn) {
-                                       dns_name_format(dns_db_origin(db),
+                               if (ctx->warn) {
+                                       dns_name_format(dns_db_origin(ctx->db),
                                                        namebuf,
                                                        sizeof(namebuf));
-                                       dns_rdataclass_format(dns_db_class(db),
-                                                             classbuf,
-                                                             sizeof(classbuf));
+                                       dns_rdataclass_format(
+                                               dns_db_class(ctx->db), classbuf,
+                                               sizeof(classbuf));
                                        isc_log_write(DIFF_COMMON_LOGARGS,
                                                      ISC_LOG_WARNING,
                                                      "%s/%s: dns_diff_apply: "
                                                      "update with no effect",
                                                      namebuf, classbuf);
                                }
-                               if (op == DNS_DIFFOP_ADD ||
-                                   op == DNS_DIFFOP_ADDRESIGN)
-                               {
-                                       setownercase(&ardataset, name);
-                               }
-                               if (op == DNS_DIFFOP_DEL ||
-                                   op == DNS_DIFFOP_DELRESIGN)
-                               {
-                                       getownercase(&ardataset, name);
-                               }
-                       } else if (result == DNS_R_NXRRSET) {
+                               result = ISC_R_SUCCESS;
+                               break;
+                       case DNS_R_NXRRSET:
                                /*
                                 * OK.
                                 */
-                               if (op == DNS_DIFFOP_DEL ||
-                                   op == DNS_DIFFOP_DELRESIGN)
-                               {
-                                       getownercase(&ardataset, name);
-                               }
-                               if (dns_rdataset_isassociated(&ardataset)) {
-                                       dns_rdataset_disassociate(&ardataset);
-                               }
-                       } else {
-                               if (result == DNS_R_NOTEXACT) {
-                                       dns_name_format(name, namebuf,
-                                                       sizeof(namebuf));
-                                       dns_rdatatype_format(type, typebuf,
-                                                            sizeof(typebuf));
-                                       dns_rdataclass_format(rdclass, classbuf,
-                                                             sizeof(classbuf));
-                                       isc_log_write(
-                                               DIFF_COMMON_LOGARGS,
-                                               ISC_LOG_ERROR,
-                                               "dns_diff_apply: %s/%s/%s: %s "
-                                               "%s",
-                                               namebuf, typebuf, classbuf,
-                                               optotext(op),
-                                               isc_result_totext(result));
-                               }
-                               if (dns_rdataset_isassociated(&ardataset)) {
-                                       dns_rdataset_disassociate(&ardataset);
-                               }
-                               CHECK(result);
-                       }
-                       dns_db_detachnode(db, &node);
-                       if (dns_rdataset_isassociated(&ardataset)) {
-                               dns_rdataset_disassociate(&ardataset);
+                               result = ISC_R_SUCCESS;
+                               break;
+                       case DNS_R_NOTEXACT:
+                               dns_name_format(name, namebuf, sizeof(namebuf));
+                               dns_rdatatype_format(type, typebuf,
+                                                    sizeof(typebuf));
+                               dns_rdataclass_format(rdclass, classbuf,
+                                                     sizeof(classbuf));
+                               isc_log_write(DIFF_COMMON_LOGARGS,
+                                             ISC_LOG_ERROR,
+                                             "dns_diff_apply: %s/%s/%s: %s "
+                                             "%s",
+                                             namebuf, typebuf, classbuf,
+                                             optotext(op),
+                                             isc_result_totext(result));
+                               break;
+                       default:
+                               break;
                        }
+
+                       CHECK(result);
                }
        }
        return ISC_R_SUCCESS;
 
 cleanup:
-       if (node != NULL) {
-               dns_db_detachnode(db, &node);
-       }
        return result;
 }
 
 isc_result_t
 dns_diff_apply(const dns_diff_t *diff, dns_db_t *db, dns_dbversion_t *ver) {
-       return diff_apply(diff, db, ver, true);
+       dns_updatectx_t ctx = { .db = db, .ver = ver, .warn = true };
+       dns_rdatacallbacks_t callbacks;
+       dns_rdatacallbacks_init(&callbacks);
+       callbacks.update = update_callback;
+       callbacks.add_private = &ctx;
+       return diff_apply(diff, &callbacks);
 }
 
 isc_result_t
 dns_diff_applysilently(const dns_diff_t *diff, dns_db_t *db,
                       dns_dbversion_t *ver) {
-       return diff_apply(diff, db, ver, false);
+       dns_updatectx_t ctx = { .db = db, .ver = ver, .warn = false };
+       dns_rdatacallbacks_t callbacks;
+       dns_rdatacallbacks_init(&callbacks);
+       callbacks.update = update_callback;
+       callbacks.add_private = &ctx;
+       return diff_apply(diff, &callbacks);
+}
+
+isc_result_t
+dns_diff_apply_with_callbacks(const dns_diff_t *diff,
+                             dns_rdatacallbacks_t *callbacks) {
+       REQUIRE(DNS_DIFF_VALID(diff));
+       REQUIRE(callbacks != NULL);
+       REQUIRE(callbacks->update != NULL);
+
+       return diff_apply(diff, callbacks);
 }
 
 /* XXX this duplicates lots of code in diff_apply(). */
@@ -542,8 +579,9 @@ dns_diff_load(const dns_diff_t *diff, dns_rdatacallbacks_t *callbacks) {
                        rds.trust = dns_trust_ultimate;
 
                        INSIST(op == DNS_DIFFOP_ADD);
-                       result = callbacks->add(callbacks->add_private, name,
-                                               &rds DNS__DB_FILELINE);
+                       result = callbacks->update(
+                               callbacks->add_private, name, &rds,
+                               DNS_DIFFOP_ADD DNS__DB_FILELINE);
                        if (result == DNS_R_UNCHANGED) {
                                isc_log_write(DIFF_COMMON_LOGARGS,
                                              ISC_LOG_WARNING,
index da15ec9255e87e4273f6a16663bfc41ff1d97751..ffe2d9ea6b1639b0d66342afaaeb5414309d8977 100644 (file)
@@ -37,18 +37,18 @@ struct dns_rdatacallbacks {
        unsigned int magic;
 
        /*%
-        * dns_load_master calls 'add' when it has an rdataset to add
-        * to the database. If defined, it calls 'setup' before and
-        * 'commit' after adding rdatasets.
+        * dns_load_master calls 'update' when it has an rdataset to update
+        * in the database. If defined, it calls 'setup' before and
+        * 'commit' after updating rdatasets.
         *
         * Some database implementations will commit each rdataset as
-        * soon as it's added, in which case 'setup' and 'commit' need
+        * soon as it's updated, in which case 'setup' and 'commit' need
         * not be defined.  However, other implementations can be
         * optimized by grouping rdatasets into a transaction; the
         * setup and commit functions allow this transaction to be
         * opened and committed.
         */
-       dns_addrdatasetfunc_t add;
+       dns_addrdatasetfunc_t update;
        dns_transactionfunc_t setup;
        dns_transactionfunc_t commit;
 
index 081c8ae8345ee8463b38f8177adfe9aeacc534f5..63fa66e799032330ef3e79bb90bcbc3baced9115 100644 (file)
@@ -83,6 +83,12 @@ typedef struct dns_dbmethods {
        isc_result_t (*beginload)(dns_db_t             *db,
                                  dns_rdatacallbacks_t *callbacks);
        isc_result_t (*endload)(dns_db_t *db, dns_rdatacallbacks_t *callbacks);
+       isc_result_t (*beginupdate)(dns_db_t           *db,
+                                   dns_rdatacallbacks_t *callbacks);
+       isc_result_t (*commitupdate)(dns_db_t          *db,
+                                    dns_rdatacallbacks_t *callbacks);
+       isc_result_t (*abortupdate)(dns_db_t          *db,
+                                   dns_rdatacallbacks_t *callbacks);
        void (*currentversion)(dns_db_t *db, dns_dbversion_t **versionp);
        isc_result_t (*newversion)(dns_db_t *db, dns_dbversion_t **versionp);
        void (*attachversion)(dns_db_t *db, dns_dbversion_t *source,
@@ -537,6 +543,85 @@ dns_db_endload(dns_db_t *db, dns_rdatacallbacks_t *callbacks);
  *     implementation used, syntax errors in the master file, etc.
  */
 
+isc_result_t
+dns_db_beginupdate(dns_db_t *db, dns_rdatacallbacks_t *callbacks);
+/*%<
+ * Begin updating 'db'.
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database.
+ *
+ * \li  'callbacks' is a pointer to an initialized dns_rdatacallbacks_t
+ *       structure.
+ *
+ * Ensures:
+ *
+ * \li On success, callbacks->add will be a valid dns_addrdatasetfunc_t
+ *      suitable for updating records in 'db' from IXFR operations.
+ *      callbacks->add_private will be a valid DB update context
+ *      which should be used as 'arg' when callbacks->add is called.
+ *
+ * Returns:
+ *
+ * \li #ISC_R_SUCCESS
+ *
+ * \li Other results are possible, depending upon the database
+ *     implementation used.
+ */
+
+isc_result_t
+dns_db_commitupdate(dns_db_t *db, dns_rdatacallbacks_t *callbacks);
+/*%<
+ * Commit the update to 'db'. Must be safe to double-call or call after 
+ * dns_db_abortupdate.
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database that is being updated.
+ *
+ * \li 'callbacks' is a valid dns_rdatacallbacks_t structure.
+ *
+ * \li callbacks->add_private is not NULL and is a valid database update context.
+ *
+ * Ensures:
+ *
+ * \li 'callbacks' is returned to its state prior to calling dns_db_beginupdate()
+ *
+ * Returns:
+ *
+ * \li #ISC_R_SUCCESS
+ *
+ * \li Other results are possible, depending upon the database
+ *     implementation used.
+ */
+
+isc_result_t
+dns_db_abortupdate(dns_db_t *db, dns_rdatacallbacks_t *callbacks);
+/*%<
+ * Abort the update to 'db'. Must be safe to double-call or call after 
+ * dns_db_commitupdate.
+ *
+ * Requires:
+ *
+ * \li 'db' is a valid database that is being updated.
+ *
+ * \li 'callbacks' is a valid dns_rdatacallbacks_t structure.
+ *
+ * \li callbacks->add_private is not NULL and is a valid database update context.
+ *
+ * Ensures:
+ *
+ * \li 'callbacks' is returned to its state prior to calling dns_db_beginupdate()
+ *
+ * Returns:
+ *
+ * \li #ISC_R_SUCCESS
+ *
+ * \li Other results are possible, depending upon the database
+ *     implementation used.
+ */
+
 isc_result_t
 dns_db_load(dns_db_t *db, const char *filename, dns_masterformat_t format,
            unsigned int options);
index 84a20b2b8daa24348d75ce7423af333d900107f4..64cfe1923e29895c4a2ce63f5ef8ee085372ef23 100644 (file)
  * timeexpire.
  */
 
-typedef enum {
-       DNS_DIFFOP_ADD = 0,       /*%< Add an RR. */
-       DNS_DIFFOP_DEL = 1,       /*%< Delete an RR. */
-       DNS_DIFFOP_EXISTS = 2,    /*%< Assert RR existence. */
-       DNS_DIFFOP_ADDRESIGN = 4, /*%< ADD + RESIGN. */
-       DNS_DIFFOP_DELRESIGN = 5  /*%< DEL + RESIGN. */
-} dns_diffop_t;
 
 typedef struct dns_difftuple dns_difftuple_t;
 typedef ISC_LIST(dns_difftuple_t) dns_difftuplelist_t;
@@ -266,6 +259,24 @@ dns_diff_applysilently(const dns_diff_t *diff, dns_db_t *db,
  *
  */
 
+typedef struct {
+       dns_db_t *db;
+       dns_dbversion_t *ver;
+       bool warn;
+} dns_updatectx_t;
+
+isc_result_t
+dns_diff_apply_with_callbacks(const dns_diff_t *diff, dns_rdatacallbacks_t *callbacks);
+/*%<
+ * Apply 'diff' to the database using the provided callbacks and context.
+ * The context contains the database, version, and warning flag.
+ * This allows for custom callback implementations.
+ *
+ * Requires:
+ *\li  'callbacks' points to a valid dns_rdatacallbacks_t structure
+ *\li  'callbacks->update' is not NULL
+ */
+
 isc_result_t
 dns_diff_load(const dns_diff_t *diff, dns_rdatacallbacks_t *callbacks);
 /*%<
index 6cd50854787687b7c7f2c15b7d404954fd25ea5c..992bfceef599baf1d5bb51a8550d8c48e6f0e279 100644 (file)
@@ -182,6 +182,14 @@ typedef struct dst_gssapi_signverifyctx dst_gssapi_signverifyctx_t;
 
 typedef enum { dns_hash_sha1 = 1 } dns_hash_t;
 
+typedef enum {
+       DNS_DIFFOP_ADD = 0,       /*%< Add an RR. */
+       DNS_DIFFOP_DEL = 1,       /*%< Delete an RR. */
+       DNS_DIFFOP_EXISTS = 2,    /*%< Assert RR existence. */
+       DNS_DIFFOP_ADDRESIGN = 4, /*%< ADD + RESIGN. */
+       DNS_DIFFOP_DELRESIGN = 5  /*%< DEL + RESIGN. */
+} dns_diffop_t;
+
 typedef enum {
        dns_fwdpolicy_none = 0,
        dns_fwdpolicy_first = 1,
@@ -433,8 +441,8 @@ typedef void (*dns_loaddonefunc_t)(void *, isc_result_t);
 typedef void (*dns_rawdatafunc_t)(dns_zone_t *, dns_masterrawheader_t *);
 
 typedef isc_result_t (*dns_addrdatasetfunc_t)(void *arg, const dns_name_t *name,
-                                             dns_rdataset_t *rdataset
-                                                     DNS__DB_FLARG);
+                                             dns_rdataset_t *rdataset,
+                                             dns_diffop_t op DNS__DB_FLARG);
 typedef void (*dns_transactionfunc_t)(void *arg);
 
 typedef isc_result_t (*dns_additionaldatafunc_t)(
index ec5e7c41e3e9f074242d9848f1105d92c465d7a5..f1a698d98612948de6c8952b05cf222ad0ff274d 100644 (file)
@@ -511,7 +511,7 @@ loadctx_create(dns_masterformat_t format, isc_mem_t *mctx, unsigned int options,
 
        REQUIRE(lctxp != NULL && *lctxp == NULL);
        REQUIRE(callbacks != NULL);
-       REQUIRE(callbacks->add != NULL);
+       REQUIRE(callbacks->update != NULL);
        REQUIRE(callbacks->error != NULL);
        REQUIRE(callbacks->warn != NULL);
        REQUIRE(mctx != NULL);
@@ -2948,8 +2948,8 @@ commit(dns_rdatacallbacks_t *callbacks, dns_loadctx_t *lctx,
                        dataset.attributes |= DNS_RDATASETATTR_RESIGN;
                        dataset.resign = resign_fromlist(this, lctx);
                }
-               result = callbacks->add(callbacks->add_private, owner,
-                                       &dataset DNS__DB_FILELINE);
+               result = callbacks->update(callbacks->add_private, owner,
+                                       &dataset, DNS_DIFFOP_ADD DNS__DB_FILELINE);
                if (result == ISC_R_NOMEMORY) {
                        (*error)(callbacks, "dns_master_load: %s",
                                 isc_result_totext(result));
index cd576bc33cf82d79479b2d0a2e199ae38190d974..d24d5815547aa0e1d72d540d8afbfd45c36334cf 100644 (file)
@@ -2046,7 +2046,7 @@ addwildcards(qpzonedb_t *qpdb, dns_qp_t *qp, const dns_name_t *name) {
 
 static isc_result_t
 loading_addrdataset(void *arg, const dns_name_t *name,
-                   dns_rdataset_t *rdataset DNS__DB_FLARG) {
+                   dns_rdataset_t *rdataset, dns_diffop_t op ISC_ATTR_UNUSED DNS__DB_FLARG) {
        qpz_load_t *loadctx = arg;
        qpzonedb_t *qpdb = (qpzonedb_t *)loadctx->db;
        qpznode_t *node = NULL;
@@ -2191,7 +2191,7 @@ beginload(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
 
        RWUNLOCK(&qpdb->lock, isc_rwlocktype_write);
 
-       callbacks->add = loading_addrdataset;
+       callbacks->update = loading_addrdataset;
        callbacks->setup = loading_setup;
        callbacks->commit = loading_commit;
        callbacks->add_private = loadctx;
@@ -2226,7 +2226,7 @@ endload(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
                RWUNLOCK(&qpdb->lock, isc_rwlocktype_write);
        }
 
-       callbacks->add = NULL;
+       callbacks->update = NULL;
        callbacks->setup = NULL;
        callbacks->commit = NULL;
        callbacks->add_private = NULL;
index dcb2afad78ef3b5c2796c0c812ee182af736de66..18dd524a3483dc2ddfbaeb3492ef35462634baa2 100644 (file)
@@ -62,6 +62,7 @@
 #include <dns/zonekey.h>
 
 #include "db_p.h"
+#include "isc/attributes.h"
 #include "rbtdb_p.h"
 
 #define EXISTS(header)                                 \
@@ -1678,7 +1679,7 @@ done:
 
 static isc_result_t
 loading_addrdataset(void *arg, const dns_name_t *name,
-                   dns_rdataset_t *rdataset DNS__DB_FLARG) {
+                   dns_rdataset_t *rdataset, dns_diffop_t op ISC_ATTR_UNUSED DNS__DB_FLARG) {
        rbtdb_load_t *loadctx = arg;
        dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)loadctx->db;
        dns_rbtnode_t *node = NULL;
@@ -1812,7 +1813,7 @@ beginload(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
 
        RWUNLOCK(&rbtdb->lock, isc_rwlocktype_write);
 
-       callbacks->add = loading_addrdataset;
+       callbacks->update = loading_addrdataset;
        callbacks->add_private = loadctx;
 
        return ISC_R_SUCCESS;
@@ -1849,7 +1850,7 @@ endload(dns_db_t *db, dns_rdatacallbacks_t *callbacks) {
                RWUNLOCK(&rbtdb->lock, isc_rwlocktype_write);
        }
 
-       callbacks->add = NULL;
+       callbacks->update = NULL;
        callbacks->add_private = NULL;
 
        isc_mem_put(rbtdb->common.mctx, loadctx, sizeof(*loadctx));
index 0443a688d1bbc57d311b27a7596ecbf75d9782e1..3a0871ca60f08dae38e75dd661514cf8c48176bc 100644 (file)
@@ -513,6 +513,11 @@ ixfr_apply_one(dns_xfrin_t *xfr, ixfr_apply_data_t *data) {
        isc_result_t result = ISC_R_SUCCESS;
        uint64_t records;
 
+       dns_rdatacallbacks_t callbacks;
+       dns_rdatacallbacks_init(&callbacks);
+       dns_db_beginupdate(xfr->db, &callbacks);
+
+
        CHECK(ixfr_begin_transaction(&xfr->ixfr));
 
        CHECK(dns_diff_apply(&data->diff, xfr->db, xfr->ver));
@@ -526,12 +531,27 @@ ixfr_apply_one(dns_xfrin_t *xfr, ixfr_apply_data_t *data) {
                CHECK(dns_journal_writediff(xfr->ixfr.journal, &data->diff));
        }
 
+       /*
+        * At the moment, rdatacallbacks doesn't offer a way to inspect the
+        * result of a transaction before committing it.
+        *
+        * So we need to commit *before* calling dns_zone_verifydb, and rely
+        * on closeversion to actually do cleanup.
+        */
+       dns_db_commitupdate(xfr->db, &callbacks);
+
        CHECK(dns_zone_verifydb(xfr->zone, xfr->db, xfr->ver));
 
        result = ixfr_end_transaction(&xfr->ixfr);
 
        return result;
 cleanup:
+       /*
+        * For the reason stated above, dns_db_abortupdate must *commit* the
+        * changes and rely on closeversion to clean them up.
+        */
+       dns_db_abortupdate(xfr->db, &callbacks);
+
        /* We need to end the transaction, but keep the previous error */
        (void)ixfr_end_transaction(&xfr->ixfr);