s4-dsdb: added support for backlinks in repl_meta_data
authorAndrew Tridgell <tridge@samba.org>
Wed, 16 Dec 2009 00:34:33 +0000 (11:34 +1100)
committerAndrew Tridgell <tridge@samba.org>
Fri, 1 Jan 2010 21:16:48 +0000 (08:16 +1100)
backlinks need more careful handling now that we store the additional
meta data for deleted links. It is easier to handle this in
repl_meta_data than in linked_attributes.

Eventually linked_attributes will disappear, with the functionality
moved into repl_meta_data.

Pair-Programmed-With: Andrew Bartlett <abartlet@samba.org>

source4/dsdb/samdb/ldb_modules/repl_meta_data.c

index 249dd61a4a3e6c550ecf3d2e6592731ed9db4faa..7488a1bee94b4f5e1f72b018c846ca6f8ac69a6e 100644 (file)
@@ -53,6 +53,8 @@
 struct replmd_private {
        TALLOC_CTX *la_ctx;
        struct la_entry *la_list;
+       TALLOC_CTX *bl_ctx;
+       struct la_backlink *la_backlinks;
        struct nc_entry {
                struct nc_entry *prev, *next;
                struct ldb_dn *dn;
@@ -87,6 +89,7 @@ struct replmd_replicated_request {
 
 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar);
 
+
 /*
   initialise the module
   allocate the private structure and build the list
@@ -107,6 +110,169 @@ static int replmd_init(struct ldb_module *module)
        return ldb_next_init(module);
 }
 
+/*
+  cleanup our per-transaction contexts
+ */
+static void replmd_txn_cleanup(struct replmd_private *replmd_private)
+{
+       talloc_free(replmd_private->la_ctx);
+       replmd_private->la_list = NULL;
+       replmd_private->la_ctx = NULL;
+
+       talloc_free(replmd_private->bl_ctx);
+       replmd_private->la_backlinks = NULL;
+       replmd_private->bl_ctx = NULL;
+}
+
+
+struct la_backlink {
+       struct la_backlink *next, *prev;
+       const char *attr_name;
+       struct GUID forward_guid, target_guid;
+       bool active;
+};
+
+/*
+  add a backlink to the list of backlinks to add/delete in the prepare
+  commit
+ */
+static int replmd_add_backlink(struct ldb_module *module, const struct dsdb_schema *schema,
+                              struct GUID *forward_guid, struct GUID *target_guid,
+                              bool active, const struct dsdb_attribute *schema_attr)
+{
+       const struct dsdb_attribute *target_attr;
+       struct la_backlink *bl;
+       struct replmd_private *replmd_private =
+               talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
+
+       target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID + 1);
+       if (!target_attr) {
+               /*
+                * windows 2003 has a broken schema where the
+                * definition of msDS-IsDomainFor is missing (which is
+                * supposed to be the backlink of the
+                * msDS-HasDomainNCs attribute
+                */
+               return LDB_SUCCESS;
+       }
+
+       /* see if its already in the list */
+       for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
+               if (GUID_equal(forward_guid, &bl->forward_guid) &&
+                   GUID_equal(target_guid, &bl->target_guid) &&
+                   (target_attr->lDAPDisplayName == bl->attr_name ||
+                    strcmp(target_attr->lDAPDisplayName, bl->attr_name) == 0)) {
+                       break;
+               }
+       }
+
+       if (bl) {
+               /* we found an existing one */
+               if (bl->active == active) {
+                       return LDB_SUCCESS;
+               }
+               DLIST_REMOVE(replmd_private->la_backlinks, bl);
+               talloc_free(bl);
+               return LDB_SUCCESS;
+       }
+
+       if (replmd_private->bl_ctx == NULL) {
+               replmd_private->bl_ctx = talloc_new(replmd_private);
+               if (replmd_private->bl_ctx == NULL) {
+                       ldb_module_oom(module);
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+       }
+
+       /* its a new one */
+       bl = talloc(replmd_private->bl_ctx, struct la_backlink);
+       if (bl == NULL) {
+               ldb_module_oom(module);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       bl->attr_name = target_attr->lDAPDisplayName;
+       bl->forward_guid = *forward_guid;
+       bl->target_guid = *target_guid;
+       bl->active = active;
+
+       DLIST_ADD(replmd_private->la_backlinks, bl);
+
+       return LDB_SUCCESS;
+}
+
+/*
+  process the list of backlinks we accumulated during
+  a transaction, adding and deleting the backlinks
+  from the target objects
+ */
+static int replmd_process_backlink(struct ldb_module *module, struct la_backlink *bl)
+{
+       struct ldb_dn *target_dn, *source_dn;
+       int ret;
+       struct ldb_context *ldb = ldb_module_get_ctx(module);
+       struct ldb_message *msg;
+       TALLOC_CTX *tmp_ctx = talloc_new(bl);
+       char *dn_string;
+
+       /*
+         - find DN of target
+         - find DN of source
+         - construct ldb_message
+              - either an add or a delete
+        */
+       ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->target_guid, &target_dn);
+       if (ret != LDB_SUCCESS) {
+               ldb_asprintf_errstring(ldb, "Failed to find target DN for linked attribute with GUID %s\n",
+                                      GUID_string(bl, &bl->target_guid));
+               talloc_free(tmp_ctx);
+               return ret;
+       }
+
+       ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->forward_guid, &source_dn);
+       if (ret != LDB_SUCCESS) {
+               ldb_asprintf_errstring(ldb, "Failed to find source DN for linked attribute with GUID %s\n",
+                                      GUID_string(bl, &bl->forward_guid));
+               talloc_free(tmp_ctx);
+               return ret;
+       }
+
+       msg = ldb_msg_new(tmp_ctx);
+       if (msg == NULL) {
+               ldb_module_oom(module);
+               talloc_free(tmp_ctx);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       /* construct a ldb_message for adding/deleting the backlink */
+       msg->dn = target_dn;
+       dn_string = ldb_dn_get_extended_linearized(tmp_ctx, source_dn, 1);
+       if (!dn_string) {
+               ldb_module_oom(module);
+               talloc_free(tmp_ctx);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       ret = ldb_msg_add_steal_string(msg, bl->attr_name, dn_string);
+       if (ret != LDB_SUCCESS) {
+               talloc_free(tmp_ctx);
+               return ret;
+       }
+       msg->elements[0].flags = bl->active?LDB_FLAG_MOD_ADD:LDB_FLAG_MOD_DELETE;
+
+       ret = dsdb_module_modify(module, msg, 0);
+       if (ret != LDB_SUCCESS) {
+               ldb_asprintf_errstring(ldb, "Failed to %s backlink from %s to %s - %s",
+                                      bl->active?"add":"remove",
+                                      ldb_dn_get_linearized(source_dn),
+                                      ldb_dn_get_linearized(target_dn),
+                                      ldb_errstring(ldb));
+               talloc_free(tmp_ctx);
+               return ret;
+       }
+       talloc_free(tmp_ctx);
+       return ret;
+}
+
 
 /*
  * Callback for most write operations in this module:
@@ -1097,12 +1263,14 @@ static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct p
   handle adding a linked attribute
  */
 static int replmd_modify_la_add(struct ldb_module *module,
+                               struct dsdb_schema *schema,
                                struct ldb_message *msg,
                                struct ldb_message_element *el,
                                struct ldb_message_element *old_el,
                                const struct dsdb_attribute *schema_attr,
                                uint64_t seq_num,
-                               time_t t)
+                               time_t t,
+                               struct GUID *msg_guid)
 {
        int i;
        struct parsed_dn *dns, *old_dns;
@@ -1166,6 +1334,12 @@ static int replmd_modify_la_add(struct ldb_module *module,
                                return ret;
                        }
                }
+
+               ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr);
+               if (ret != LDB_SUCCESS) {
+                       talloc_free(tmp_ctx);
+                       return ret;
+               }
        }
 
        /* add the new ones on to the end of the old values, constructing a new el->values */
@@ -1197,12 +1371,14 @@ static int replmd_modify_la_add(struct ldb_module *module,
   handle deleting all active linked attributes
  */
 static int replmd_modify_la_delete(struct ldb_module *module,
+                                  struct dsdb_schema *schema,
                                   struct ldb_message *msg,
                                   struct ldb_message_element *el,
                                   struct ldb_message_element *old_el,
                                   const struct dsdb_attribute *schema_attr,
                                   uint64_t seq_num,
-                                  time_t t)
+                                  time_t t,
+                                  struct GUID *msg_guid)
 {
        int i;
        struct parsed_dn *dns, *old_dns;
@@ -1280,6 +1456,12 @@ static int replmd_modify_la_delete(struct ldb_module *module,
                        talloc_free(tmp_ctx);
                        return ret;
                }
+
+               ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr);
+               if (ret != LDB_SUCCESS) {
+                       talloc_free(tmp_ctx);
+                       return ret;
+               }
        }
 
        el->values = talloc_steal(msg->elements, old_el->values);
@@ -1298,12 +1480,14 @@ static int replmd_modify_la_delete(struct ldb_module *module,
   handle replacing a linked attribute
  */
 static int replmd_modify_la_replace(struct ldb_module *module,
+                                   struct dsdb_schema *schema,
                                    struct ldb_message *msg,
                                    struct ldb_message_element *el,
                                    struct ldb_message_element *old_el,
                                    const struct dsdb_attribute *schema_attr,
                                    uint64_t seq_num,
-                                   time_t t)
+                                   time_t t,
+                                   struct GUID *msg_guid)
 {
        int i;
        struct parsed_dn *dns, *old_dns;
@@ -1344,13 +1528,20 @@ static int replmd_modify_la_replace(struct ldb_module *module,
                struct parsed_dn *p;
                const struct ldb_val *v;
 
+               v = ldb_dn_get_extended_component(old_p->dsdb_dn->dn, "DELETED");
+               if (v) continue;
+
+               ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr);
+               if (ret != LDB_SUCCESS) {
+                       talloc_free(tmp_ctx);
+                       return ret;
+               }
+
                p = parsed_dn_find(dns, el->num_values, old_p->guid);
                if (p) {
                        /* we don't delete it if we are re-adding it */
                        continue;
                }
-               v = ldb_dn_get_extended_component(old_p->dsdb_dn->dn, "DELETED");
-               if (v) continue;
 
                ret = replmd_update_la_val(old_el->values, old_p->v, old_p, old_p,
                                           invocation_id, seq_num, t, true);
@@ -1394,6 +1585,12 @@ static int replmd_modify_la_replace(struct ldb_module *module,
                        }
                        num_new_values++;
                }
+
+               ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr);
+               if (ret != LDB_SUCCESS) {
+                       talloc_free(tmp_ctx);
+                       return ret;
+               }
        }
 
        /* add the new values to the end of old_el */
@@ -1436,6 +1633,7 @@ static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
        struct ldb_context *ldb = ldb_module_get_ctx(module);
        struct ldb_message *old_msg;
        struct dsdb_schema *schema = dsdb_get_schema(ldb);
+       struct GUID old_guid;
 
        if (seq_num == 0) {
                /* there the replmd_update_rpmd code has already
@@ -1462,6 +1660,8 @@ static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
        }
        old_msg = res->msgs[0];
 
+       old_guid = samdb_result_guid(old_msg, "objectGUID");
+
        for (i=0; i<msg->num_elements; i++) {
                struct ldb_message_element *el = &msg->elements[i];
                struct ldb_message_element *old_el, *new_el;
@@ -1484,13 +1684,13 @@ static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
                old_el = ldb_msg_find_element(old_msg, el->name);
                switch (el->flags & LDB_FLAG_MOD_MASK) {
                case LDB_FLAG_MOD_REPLACE:
-                       ret = replmd_modify_la_replace(module, msg, el, old_el, schema_attr, seq_num, t);
+                       ret = replmd_modify_la_replace(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid);
                        break;
                case LDB_FLAG_MOD_DELETE:
-                       ret = replmd_modify_la_delete(module, msg, el, old_el, schema_attr, seq_num, t);
+                       ret = replmd_modify_la_delete(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid);
                        break;
                case LDB_FLAG_MOD_ADD:
-                       ret = replmd_modify_la_add(module, msg, el, old_el, schema_attr, seq_num, t);
+                       ret = replmd_modify_la_add(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid);
                        break;
                default:
                        ldb_asprintf_errstring(ldb,
@@ -2877,9 +3077,7 @@ static int replmd_start_transaction(struct ldb_module *module)
        /* create our private structure for this transaction */
        struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
                                                                struct replmd_private);
-       talloc_free(replmd_private->la_ctx);
-       replmd_private->la_list = NULL;
-       replmd_private->la_ctx = NULL;
+       replmd_txn_cleanup(replmd_private);
 
        /* free any leftover mod_usn records from cancelled
           transactions */
@@ -2901,6 +3099,7 @@ static int replmd_prepare_commit(struct ldb_module *module)
        struct replmd_private *replmd_private = 
                talloc_get_type(ldb_module_get_private(module), struct replmd_private);
        struct la_entry *la, *prev;
+       struct la_backlink *bl;
        int ret;
 
        /* walk the list backwards, to do the first entry first, as we
@@ -2913,16 +3112,22 @@ static int replmd_prepare_commit(struct ldb_module *module)
                DLIST_REMOVE(replmd_private->la_list, la);
                ret = replmd_process_linked_attribute(module, la);
                if (ret != LDB_SUCCESS) {
-                       talloc_free(replmd_private->la_ctx);
-                       replmd_private->la_list = NULL;
-                       replmd_private->la_ctx = NULL;
+                       replmd_txn_cleanup(replmd_private);
                        return ret;
                }
        }
 
-       talloc_free(replmd_private->la_ctx);
-       replmd_private->la_list = NULL;
-       replmd_private->la_ctx = NULL;
+       /* process our backlink list, creating and deleting backlinks
+          as necessary */
+       for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
+               ret = replmd_process_backlink(module, bl);
+               if (ret != LDB_SUCCESS) {
+                       replmd_txn_cleanup(replmd_private);
+                       return ret;
+               }
+       }
+
+       replmd_txn_cleanup(replmd_private);
 
        /* possibly change @REPLCHANGED */
        ret = replmd_notify_store(module);
@@ -2937,9 +3142,8 @@ static int replmd_del_transaction(struct ldb_module *module)
 {
        struct replmd_private *replmd_private = 
                talloc_get_type(ldb_module_get_private(module), struct replmd_private);
-       talloc_free(replmd_private->la_ctx);
-       replmd_private->la_list = NULL;
-       replmd_private->la_ctx = NULL;
+       replmd_txn_cleanup(replmd_private);
+
        return ldb_next_del_trans(module);
 }