s4:dsdb - enhance "get_last_structural_class()" for optimisations
[obnox/samba/samba-obnox.git] / source4 / dsdb / samdb / ldb_modules / objectclass.c
index 6f239d25e1f55ae8de021711d26314833a0d7004..0d75e5ff89705b512b23db9f0a5ff86203e400c2 100644 (file)
@@ -100,7 +100,9 @@ static int objectclass_sort(struct ldb_module *module,
 {
        struct ldb_context *ldb;
        unsigned int i, lowest;
-       struct class_list *unsorted = NULL, *sorted = NULL, *current = NULL, *poss_parent = NULL, *new_parent = NULL, *current_lowest = NULL;
+       struct class_list *unsorted = NULL, *sorted = NULL, *current = NULL,
+                         *poss_parent = NULL, *new_parent = NULL,
+                         *current_lowest = NULL, *current_lowest_struct = NULL;
 
        ldb = ldb_module_get_ctx(module);
 
@@ -163,9 +165,15 @@ static int objectclass_sort(struct ldb_module *module,
        current->objectclass = dsdb_class_by_lDAPDisplayName(schema, "top");
        DLIST_ADD_END(sorted, current, struct class_list *);
 
+       /* If we don't have a schema yet, then just merge the lists again */
+       if (!schema) {
+               DLIST_CONCATENATE(sorted, unsorted, struct class_list *);
+               *sorted_out = sorted;
+               return LDB_SUCCESS;
+       }
 
        /* For each object:  find parent chain */
-       for (current = unsorted; schema && current; current = current->next) {
+       for (current = unsorted; current != NULL; current = current->next) {
                for (poss_parent = unsorted; poss_parent; poss_parent = poss_parent->next) {
                        if (ldb_attr_cmp(poss_parent->objectclass->lDAPDisplayName, current->objectclass->subClassOf) == 0) {
                                break;
@@ -181,42 +189,37 @@ static int objectclass_sort(struct ldb_module *module,
                DLIST_ADD_END(unsorted, new_parent, struct class_list *);
        }
 
-       do
-       {
+       /* For each object: order by hierarchy */
+       while (unsorted != NULL) {
                lowest = UINT_MAX;
-               current_lowest = NULL;
-               for (current = unsorted; schema && current; current = current->next) {
-                       if(current->objectclass->subClass_order < lowest) {
-                               current_lowest = current;
+               current_lowest = current_lowest_struct = NULL;
+               for (current = unsorted; current != NULL; current = current->next) {
+                       if (current->objectclass->subClass_order <= lowest) {
+                               /*
+                                * According to MS-ADTS 3.1.1.1.4 structural
+                                * and 88 object classes are always listed after
+                                * the other class types in a subclass hierarchy
+                                */
+                               if (current->objectclass->objectClassCategory > 1) {
+                                       current_lowest = current;
+                               } else {
+                                       current_lowest_struct = current;
+                               }
                                lowest = current->objectclass->subClass_order;
                        }
                }
+               if (current_lowest == NULL) {
+                       current_lowest = current_lowest_struct;
+               }
 
-               if(current_lowest != NULL) {
+               if (current_lowest != NULL) {
                        DLIST_REMOVE(unsorted,current_lowest);
                        DLIST_ADD_END(sorted,current_lowest, struct class_list *);
                }
-       } while(unsorted);
-
-
-       if (!unsorted) {
-               *sorted_out = sorted;
-               return LDB_SUCCESS;
-       }
-
-       if (!schema) {
-               /* If we don't have schema yet, then just merge the lists again */
-               DLIST_CONCATENATE(sorted, unsorted, struct class_list *);
-               *sorted_out = sorted;
-               return LDB_SUCCESS;
        }
 
-       /* This shouldn't happen, and would break MMC, perhaps there
-        * was no 'top', a conflict in the objectClasses or some other
-        * schema error?
-        */
-       ldb_asprintf_errstring(ldb, "objectclass %s is not a valid objectClass in objectClass chain", unsorted->objectclass->lDAPDisplayName);
-       return LDB_ERR_OBJECT_CLASS_VIOLATION;
+       *sorted_out = sorted;
+       return LDB_SUCCESS;
 }
 
 /*
@@ -634,7 +637,8 @@ static int objectclass_do_add(struct oc_context *ac)
 
                /* Make sure its valid to add an object of this type */
                objectclass = get_last_structural_class(ac->schema,
-                                                       objectclass_element, ac->req);
+                                                       objectclass_element,
+                                                       true);
                if(objectclass == NULL) {
                        ldb_asprintf_errstring(ldb,
                                               "Failed to find a structural class for %s",
@@ -1109,7 +1113,7 @@ static int objectclass_do_mod(struct oc_context *ac)
 
                /* Get the new top-most structural object class */
                objectclass = get_last_structural_class(ac->schema, oc_el_entry,
-                                                       ac->req);
+                                                       false);
                if (objectclass == NULL) {
                        ldb_set_errstring(ldb,
                                          "objectclass: cannot delete all structural objectclasses!");
@@ -1309,7 +1313,8 @@ static int objectclass_do_rename2(struct oc_context *ac)
                        /* existing entry without a valid object class? */
                        return ldb_operr(ldb);
                }
-               objectclass = get_last_structural_class(ac->schema, oc_el_entry, ac->req);
+               objectclass = get_last_structural_class(ac->schema, oc_el_entry,
+                                                       false);
                if (objectclass == NULL) {
                        /* existing entry without a valid object class? */
                        return ldb_operr(ldb);