libnet-vampire: add attributes and classes from the replicated schema to the bootstra...
authorMatthieu Patou <mat@matws.net>
Mon, 8 Oct 2012 04:46:38 +0000 (21:46 -0700)
committerAndrew Bartlett <abartlet@samba.org>
Thu, 23 May 2013 10:26:17 +0000 (20:26 +1000)
Replicated schema might have attributes and auxilary classes  on some
critical classes (ie.  top, user, computer ) that are not in the bootstrap
schema. Without those new attributes and classes, bootstrap schema is
unable to translate those critical classes in the schema constructed
from the replicated data. Without thoses classes new schema is useless
and can't be indexed properly.

In order to overcome this problem, we put all new attributes and classes
definitions into the bootstrap schema so that foundations classes can be
translated.

Pair-Programmed-With: Stefan Metzmacher <metze@samba.org>

Signed-off-by: Matthieu Patou <mat@matws.net>
Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
source4/libnet/libnet_vampire.c

index 599119f81411d76d067b082bb0c120884e2a74be..0f20b151abe2cc4f4c3a3cb7fc41585d0aa11231 100644 (file)
@@ -216,6 +216,77 @@ NTSTATUS libnet_vampire_cb_check_options(void *private_data,
        return NT_STATUS_OK;
 }
 
+static NTSTATUS libnet_vampire_merge_schema(struct ldb_context *ldb,
+                                           struct dsdb_schema *dest_schema,
+                                           const struct dsdb_schema *ref_schema)
+{
+       const struct dsdb_class *cur_class = NULL;
+       const struct dsdb_attribute *cur_attr = NULL;
+       int ret;
+
+       for (cur_class = ref_schema->classes;
+            cur_class;
+            cur_class = cur_class->next)
+       {
+               const struct dsdb_class *tmp1;
+               struct dsdb_class *tmp2;
+
+               tmp1 = dsdb_class_by_governsID_id(dest_schema,
+                                                 cur_class->governsID_id);
+               if (tmp1 != NULL) {
+                       continue;
+               }
+
+               /*
+                * Do a shallow copy so that original next and prev are
+                * not modified, we don't need to do a deep copy
+                * as the rest won't be modified and this is for
+                * a short lived object.
+                */
+               tmp2 = talloc(dest_schema->classes, struct dsdb_class);
+               if (tmp2 == NULL) {
+                       return NT_STATUS_NO_MEMORY;
+               }
+               *tmp2 = *cur_class;
+               DLIST_ADD(dest_schema->classes, tmp2);
+       }
+
+       for (cur_attr = ref_schema->attributes;
+            cur_attr;
+            cur_attr = cur_attr->next)
+       {
+               const struct dsdb_attribute *tmp1;
+               struct dsdb_attribute *tmp2;
+
+               tmp1 = dsdb_attribute_by_attributeID_id(dest_schema,
+                                               cur_attr->attributeID_id);
+               if (tmp1 != NULL) {
+                       continue;
+               }
+
+               /*
+                * Do a shallow copy so that original next and prev are
+                * not modified, we don't need to do a deep copy
+                * as the rest won't be modified and this is for
+                * a short lived object.
+                */
+               tmp2 = talloc(dest_schema->attributes, struct dsdb_attribute);
+               if (tmp2 == NULL) {
+                       return NT_STATUS_NO_MEMORY;
+               }
+               *tmp2 = *cur_attr;
+               DLIST_ADD(dest_schema->attributes, tmp2);
+       }
+
+       ret = dsdb_setup_sorted_accessors(ldb, dest_schema);
+       if (LDB_SUCCESS != ret) {
+               DEBUG(0,("Failed to add new attribute to reference schema!\n"));
+               return NT_STATUS_INTERNAL_ERROR;
+       }
+
+       return NT_STATUS_OK;
+}
+
 static NTSTATUS libnet_vampire_cb_apply_schema(struct libnet_vampire_cb_state *s,
                                               const struct libnet_BecomeDC_StoreChunk *c)
 {
@@ -321,6 +392,11 @@ static NTSTATUS libnet_vampire_cb_apply_schema(struct libnet_vampire_cb_state *s
        NT_STATUS_HAVE_NO_MEMORY(tmp_dns_name);
        s_dsa->other_info->dns_name = tmp_dns_name;
 
+       if (s->self_made_schema == NULL) {
+               DEBUG(0,("libnet_vampire_cb_apply_schema: called with out self_made_schema\n"));
+               return NT_STATUS_INTERNAL_ERROR;
+       }
+
        schema_ldb = provision_get_schema(s, s->lp_ctx,
                                          c->forest->schema_dn_str,
                                          &s->prefixmap_blob);
@@ -352,9 +428,31 @@ static NTSTATUS libnet_vampire_cb_apply_schema(struct libnet_vampire_cb_state *s
        while (schema_list) {
                uint32_t converted_obj_count = 0;
                uint32_t failed_obj_count = 0;
+               int cycle_before_switching = 0;
                TALLOC_CTX *tmp_ctx = talloc_new(s);
                NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
 
+               if (s->self_made_schema != working_schema) {
+                       /*
+                        * If the selfmade schema is not the schema used to
+                        * translate and validate replicated object,
+                        * Which means that we are using the bootstrap schema
+                        * Then we add attributes and classes that were already
+                        * translated to the working schema, the idea is that
+                        * we might need to add new attributes and classes
+                        * to be able to translate critical replicated objects
+                        * and without that we wouldn't be able to translate them
+                        */
+                       NTSTATUS nt_status;
+
+                       nt_status = libnet_vampire_merge_schema(s->ldb,
+                                                       working_schema,
+                                                       s->self_made_schema);
+                       if (!NT_STATUS_IS_OK(nt_status)) {
+                               return nt_status;
+                       }
+               }
+
                for (schema_list_item = schema_list; schema_list_item; schema_list_item=schema_list_next_item) {
                        struct dsdb_extended_replicated_object object;
 
@@ -385,16 +483,22 @@ static NTSTATUS libnet_vampire_cb_apply_schema(struct libnet_vampire_cb_state *s
                                 * Convert the schema from ldb_message format
                                 * (OIDs as OID strings) into schema, using
                                 * the remote prefixMap
+                                *
+                                * It's not likely, but possible to get the
+                                * same object twice and we should keep
+                                * the last instance.
                                 */
-                               status = dsdb_schema_set_el_from_ldb_msg(s->ldb,
-                                                                        s->self_made_schema,
-                                                                        object.msg);
+                               status = dsdb_schema_set_el_from_ldb_msg_dups(s->ldb,
+                                                               s->self_made_schema,
+                                                               object.msg, true);
                                if (!W_ERROR_IS_OK(status)) {
                                        DEBUG(1,("Warning: failed to convert object %s into a schema element: %s\n",
                                                 ldb_dn_get_linearized(object.msg->dn),
                                                 win_errstr(status)));
                                        failed_obj_count++;
                                } else {
+                                       DEBUG(8,("Converted object %s into a schema element\n",
+                                                ldb_dn_get_linearized(object.msg->dn)));
                                        DLIST_REMOVE(schema_list, schema_list_item);
                                        converted_obj_count++;
                                }
@@ -402,9 +506,8 @@ static NTSTATUS libnet_vampire_cb_apply_schema(struct libnet_vampire_cb_state *s
                }
                talloc_free(tmp_ctx);
 
-               DEBUG(4,("Schema load pass %d: %d/%d of %d objects left to be converted.\n",
-                        pass_no, failed_obj_count, converted_obj_count, object_count));
-               pass_no++;
+               DEBUG(4,("Schema load pass %d: converted %d, %d of %d objects left to be converted.\n",
+                        pass_no, converted_obj_count, failed_obj_count, object_count));
 
                /* check if we converted any objects in this pass */
                if (converted_obj_count == 0) {
@@ -412,7 +515,15 @@ static NTSTATUS libnet_vampire_cb_apply_schema(struct libnet_vampire_cb_state *s
                        return NT_STATUS_INTERNAL_ERROR;
                }
 
-               if (schema_list) {
+               /*
+                * Don't try to load the schema if there is missing object
+                * _and_ we are on the first pass as some critical objects
+                * might be missing.
+                */
+               cycle_before_switching = lpcfg_parm_int(s->lp_ctx, NULL,
+                                                       "become dc",
+                                                       "schema convert retrial", 1);
+               if (failed_obj_count == 0 || pass_no > cycle_before_switching) {
                        /* prepare for another cycle */
                        working_schema = s->self_made_schema;
 
@@ -422,6 +533,7 @@ static NTSTATUS libnet_vampire_cb_apply_schema(struct libnet_vampire_cb_state *s
                                return NT_STATUS_INTERNAL_ERROR;
                        }
                }
+               pass_no++;
        };
 
        /* free temp objects for 1st conversion phase */