getncchanges.c: Remove a really old TODO
[bbaumbach/samba-autobuild/.git] / source4 / rpc_server / drsuapi / getncchanges.c
index 9905ad9b48927f5609580c16de7ba84bbff176af..ccc766d5c9612d31b3479975ff41d5aa7aa9a9c5 100644 (file)
 #undef DBGC_CLASS
 #define DBGC_CLASS            DBGC_DRS_REPL
 
-/* state of a partially completed getncchanges call */
+#define DRS_GUID_SIZE       16
+
+/*
+ * state of a partially-completed replication cycle. This state persists
+ * over multiple calls to dcesrv_drsuapi_DsGetNCChanges()
+ */
 struct drsuapi_getncchanges_state {
        struct db_context *obj_cache;
        struct GUID *guids;
@@ -70,8 +75,19 @@ struct drsuapi_getncchanges_state {
 /* We must keep the GUIDs in NDR form for sorting */
 struct la_for_sorting {
        const struct drsuapi_DsReplicaLinkedAttribute *link;
-       uint8_t target_guid[16];
-        uint8_t source_guid[16];
+       uint8_t target_guid[DRS_GUID_SIZE];
+       uint8_t source_guid[DRS_GUID_SIZE];
+};
+
+/*
+ * stores the state for a chunk of replication data. This state information
+ * only exists for a single call to dcesrv_drsuapi_DsGetNCChanges()
+ */
+struct getncchanges_repl_chunk {
+       struct drsuapi_DsGetNCChangesCtr6 *ctr6;
+
+       /* the last object written to the response */
+       struct drsuapi_DsReplicaObjectListItemEx *last_object;
 };
 
 static int drsuapi_DsReplicaHighWaterMark_cmp(const struct drsuapi_DsReplicaHighWaterMark *h1,
@@ -1945,7 +1961,7 @@ static WERROR dcesrv_drsuapi_obj_cache_add(struct db_context *obj_cache,
                                           const struct GUID *guid)
 {
        enum ndr_err_code ndr_err;
-       uint8_t guid_buf[16] = { 0, };
+       uint8_t guid_buf[DRS_GUID_SIZE] = { 0, };
        DATA_BLOB b = {
                .data = guid_buf,
                .length = sizeof(guid_buf),
@@ -1982,7 +1998,7 @@ static WERROR dcesrv_drsuapi_obj_cache_exists(struct db_context *obj_cache,
                                              const struct GUID *guid)
 {
        enum ndr_err_code ndr_err;
-       uint8_t guid_buf[16] = { 0, };
+       uint8_t guid_buf[DRS_GUID_SIZE] = { 0, };
        DATA_BLOB b = {
                .data = guid_buf,
                .length = sizeof(guid_buf),
@@ -2083,7 +2099,183 @@ static WERROR getncchanges_get_sorted_array(const struct drsuapi_DsReplicaLinked
        return werr;
 }
 
-/* 
+
+/**
+ * Adds any ancestor/parent objects of the child_obj specified.
+ * This is needed when the GET_ANC flag is specified in the request.
+ * @param new_objs if parents are added, this gets updated to point to a chain
+ * of parent objects (with the parents first and the child last)
+ */
+static WERROR getncchanges_add_ancestors(struct drsuapi_DsReplicaObjectListItemEx *child_obj,
+                                        struct ldb_dn *child_dn,
+                                        TALLOC_CTX *mem_ctx,
+                                        struct ldb_context *sam_ctx,
+                                        struct drsuapi_getncchanges_state *getnc_state,
+                                        struct dsdb_schema *schema,
+                                        DATA_BLOB *session_key,
+                                        struct drsuapi_DsGetNCChangesRequest10 *req10,
+                                        uint32_t *local_pas,
+                                        struct ldb_dn *machine_dn,
+                                        struct drsuapi_DsReplicaObjectListItemEx **new_objs)
+{
+       int ret;
+       const struct GUID *next_anc_guid = NULL;
+       WERROR werr = WERR_OK;
+       static const char * const msg_attrs[] = {
+                                           "*",
+                                           "nTSecurityDescriptor",
+                                           "parentGUID",
+                                           "replPropertyMetaData",
+                                           DSDB_SECRET_ATTRIBUTES,
+                                           NULL };
+
+       next_anc_guid = child_obj->parent_object_guid;
+
+       while (next_anc_guid != NULL) {
+               struct drsuapi_DsReplicaObjectListItemEx *anc_obj = NULL;
+               struct ldb_message *anc_msg = NULL;
+               struct ldb_result *anc_res = NULL;
+               struct ldb_dn *anc_dn = NULL;
+
+               /*
+                * Don't send an object twice. (If we've sent the object, then
+                * we've also sent all its parents as well)
+                */
+               werr = dcesrv_drsuapi_obj_cache_exists(getnc_state->obj_cache,
+                                                      next_anc_guid);
+               if (W_ERROR_EQUAL(werr, WERR_OBJECT_NAME_EXISTS)) {
+                       return WERR_OK;
+               }
+               if (W_ERROR_IS_OK(werr)) {
+                       return WERR_INTERNAL_ERROR;
+               }
+               if (!W_ERROR_EQUAL(werr, WERR_OBJECT_NOT_FOUND)) {
+                       return werr;
+               }
+
+               anc_obj = talloc_zero(mem_ctx,
+                                     struct drsuapi_DsReplicaObjectListItemEx);
+               if (anc_obj == NULL) {
+                       return WERR_NOT_ENOUGH_MEMORY;
+               }
+
+               anc_dn = ldb_dn_new_fmt(anc_obj, sam_ctx, "<GUID=%s>",
+                                       GUID_string(anc_obj, next_anc_guid));
+               if (anc_dn == NULL) {
+                       return WERR_NOT_ENOUGH_MEMORY;
+               }
+
+               ret = drsuapi_search_with_extended_dn(sam_ctx, anc_obj,
+                                                     &anc_res, anc_dn,
+                                                     LDB_SCOPE_BASE,
+                                                     msg_attrs, NULL);
+               if (ret != LDB_SUCCESS) {
+                       const char *anc_str = NULL;
+                       const char *obj_str = NULL;
+
+                       anc_str = ldb_dn_get_extended_linearized(anc_obj,
+                                                                anc_dn,
+                                                                1);
+                       obj_str = ldb_dn_get_extended_linearized(anc_obj,
+                                                                child_dn,
+                                                                1);
+
+                       DBG_ERR("getncchanges: failed to fetch ANC "
+                               "DN %s for DN %s - %s\n",
+                               anc_str, obj_str, ldb_errstring(sam_ctx));
+                       return WERR_DS_DRA_INCONSISTENT_DIT;
+               }
+
+               anc_msg = anc_res->msgs[0];
+
+               werr = get_nc_changes_build_object(anc_obj, anc_msg,
+                                                  sam_ctx,
+                                                  getnc_state->ncRoot_dn,
+                                                  getnc_state->is_schema_nc,
+                                                  schema, session_key,
+                                                  getnc_state->min_usn,
+                                                  req10->replica_flags,
+                                                  req10->partial_attribute_set,
+                                                  req10->uptodateness_vector,
+                                                  req10->extended_op,
+                                                  false, /* force_object_return */
+                                                  local_pas,
+                                                  machine_dn,
+                                                  next_anc_guid);
+               if (!W_ERROR_IS_OK(werr)) {
+                       return werr;
+               }
+
+               /*
+                * Regardless of whether we actually use it or not,
+                * we add it to the cache so we don't look at it again
+                */
+               werr = dcesrv_drsuapi_obj_cache_add(getnc_state->obj_cache,
+                                                   next_anc_guid);
+               if (!W_ERROR_IS_OK(werr)) {
+                       return werr;
+               }
+
+               /*
+                * Any ancestors which are below the highwatermark
+                * or uptodateness_vector shouldn't be added,
+                * but we still look further up the
+                * tree for ones which have been changed recently.
+                */
+               if (anc_obj->meta_data_ctr != NULL) {
+
+                       /*
+                        * prepend the parent to the list so that the client-side
+                        * adds the parent object before it adds the children
+                        */
+                       anc_obj->next_object = *new_objs;
+                       *new_objs = anc_obj;
+               }
+
+               anc_msg = NULL;
+               TALLOC_FREE(anc_res);
+               TALLOC_FREE(anc_dn);
+
+               /*
+                * We may need to resolve more parents...
+                */
+               next_anc_guid = anc_obj->parent_object_guid;
+       }
+       return werr;
+}
+
+/**
+ * Adds a list of new objects into the getNCChanges response message
+ */
+static void getncchanges_add_objs_to_resp(struct getncchanges_repl_chunk *repl_chunk,
+                                         struct drsuapi_DsReplicaObjectListItemEx *obj_list)
+{
+       struct drsuapi_DsReplicaObjectListItemEx *obj;
+
+       /*
+        * We track the last object added to the response message, so just add
+        * the new object-list onto the end
+        */
+       if (repl_chunk->last_object == NULL) {
+               repl_chunk->ctr6->first_object = obj_list;
+       } else {
+               repl_chunk->last_object->next_object = obj_list;
+       }
+
+       for (obj = obj_list; obj != NULL; obj = obj->next_object) {
+               repl_chunk->ctr6->object_count += 1;
+
+               /*
+                * Remember the last object in the response - we'll use this to
+                * link the next object(s) processed onto the existing list
+                */
+               if (obj->next_object == NULL) {
+                       repl_chunk->last_object = obj;
+               }
+       }
+}
+
+/*
   drsuapi_DsGetNCChanges
 
   see MS-DRSR 4.1.10.5.2 for basic logic of this function
@@ -2096,7 +2288,7 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
        uint32_t i, k;
        struct dsdb_schema *schema;
        struct drsuapi_DsReplicaOIDMapping_Ctr *ctr;
-       struct drsuapi_DsReplicaObjectListItemEx **currentObject;
+       struct getncchanges_repl_chunk repl_chunk = { 0 };
        NTSTATUS status;
        DATA_BLOB session_key;
        WERROR werr;
@@ -2138,7 +2330,7 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
        invocation_id = *(samdb_ntds_invocation_id(sam_ctx));
 
        *r->out.level_out = 6;
-       /* TODO: linked attributes*/
+
        r->out.ctr->ctr6.linked_attributes_count = 0;
        r->out.ctr->ctr6.linked_attributes = discard_const_p(struct drsuapi_DsReplicaLinkedAttribute, &no_linked_attr);
 
@@ -2623,7 +2815,8 @@ allowed:
        r->out.ctr->ctr6.old_highwatermark = req10->highwatermark;
        r->out.ctr->ctr6.new_highwatermark = req10->highwatermark;
 
-       currentObject = &r->out.ctr->ctr6.first_object;
+       repl_chunk.ctr6 = &r->out.ctr->ctr6;
+       repl_chunk.last_object = NULL;
 
        max_objects = lpcfg_parm_int(dce_call->conn->dce_ctx->lp_ctx, NULL, "drs", "max object sync", 1000);
        /*
@@ -2691,7 +2884,7 @@ allowed:
                                            NULL };
                struct ldb_result *msg_res;
                struct ldb_dn *msg_dn;
-               const struct GUID *next_anc_guid = NULL;
+               bool obj_already_sent = false;
 
                obj = talloc_zero(mem_ctx, struct drsuapi_DsReplicaObjectListItemEx);
                W_ERROR_HAVE_NO_MEMORY(obj);
@@ -2733,40 +2926,41 @@ allowed:
                msg = msg_res->msgs[0];
 
                /*
-                * If it has already been added as an ancestor of
-                * an object, we don't need to do anything more,
-                * as we've already added the links.
+                * Check if we've already sent the object as an ancestor of
+                * another object. If so, we don't need to send it again
                 */
                if (getnc_state->obj_cache != NULL) {
                        werr = dcesrv_drsuapi_obj_cache_exists(getnc_state->obj_cache,
                                                               &getnc_state->guids[i]);
                        if (W_ERROR_EQUAL(werr, WERR_OBJECT_NAME_EXISTS)) {
-                               dcesrv_drsuapi_update_highwatermark(msg,
-                                               getnc_state->max_usn,
-                                               &r->out.ctr->ctr6.new_highwatermark);
-                               /* no attributes to send */
-                               talloc_free(obj);
-                               continue;
+                               obj_already_sent = true;
                        }
                }
 
-               max_wait_reached = (time(NULL) - start > max_wait);
+               if (!obj_already_sent) {
+                       max_wait_reached = (time(NULL) - start > max_wait);
 
-               werr = get_nc_changes_build_object(obj, msg,
-                                                  sam_ctx, getnc_state->ncRoot_dn,
-                                                  getnc_state->is_schema_nc,
-                                                  schema, &session_key, getnc_state->min_usn,
-                                                  req10->replica_flags,
-                                                  req10->partial_attribute_set,
-                                                  req10->uptodateness_vector,
-                                                  req10->extended_op,
-                                                  max_wait_reached,
-                                                  local_pas, machine_dn,
-                                                  &getnc_state->guids[i]);
-               if (!W_ERROR_IS_OK(werr)) {
-                       return werr;
+                       werr = get_nc_changes_build_object(obj, msg,
+                                                          sam_ctx, getnc_state->ncRoot_dn,
+                                                          getnc_state->is_schema_nc,
+                                                          schema, &session_key, getnc_state->min_usn,
+                                                          req10->replica_flags,
+                                                          req10->partial_attribute_set,
+                                                          req10->uptodateness_vector,
+                                                          req10->extended_op,
+                                                          max_wait_reached,
+                                                          local_pas, machine_dn,
+                                                          &getnc_state->guids[i]);
+                       if (!W_ERROR_IS_OK(werr)) {
+                               return werr;
+                       }
                }
 
+               /*
+                * We've reached the USN where this object naturally occurs.
+                * Regardless of whether we've already sent the object (as an
+                * ancestor), we add its links and update the HWM at this point
+                */
                werr = get_nc_changes_add_links(sam_ctx, getnc_state,
                                                getnc_state->ncRoot_dn,
                                                getnc_state->is_schema_nc,
@@ -2784,11 +2978,11 @@ allowed:
                                        getnc_state->max_usn,
                                        &r->out.ctr->ctr6.new_highwatermark);
 
-               if (obj->meta_data_ctr == NULL) {
+               if (obj_already_sent || obj->meta_data_ctr == NULL) {
                        DEBUG(8,(__location__ ": getncchanges skipping send of object %s\n",
                                 ldb_dn_get_linearized(msg->dn)));
-                       /* no attributes to send */
-                       talloc_free(obj);
+                       /* nothing to send */
+                       TALLOC_FREE(obj);
                        continue;
                }
 
@@ -2800,143 +2994,29 @@ allowed:
                        if (!W_ERROR_IS_OK(werr)) {
                                return werr;
                        }
-
-                       if (getnc_state->is_get_anc) {
-                               next_anc_guid = obj->parent_object_guid;
-                       }
                }
 
-               while (next_anc_guid != NULL) {
-                       struct drsuapi_DsReplicaObjectListItemEx *anc_obj = NULL;
-                       struct ldb_message *anc_msg = NULL;
-                       struct ldb_result *anc_res = NULL;
-                       struct ldb_dn *anc_dn = NULL;
-
-                       werr = dcesrv_drsuapi_obj_cache_exists(getnc_state->obj_cache,
-                                                              next_anc_guid);
-                       if (W_ERROR_EQUAL(werr, WERR_OBJECT_NAME_EXISTS)) {
-                               /*
-                                * We don't need to send it twice.
-                                */
-                               break;
-                       }
-                       if (W_ERROR_IS_OK(werr)) {
-                               return WERR_INTERNAL_ERROR;
-                       }
-                       if (!W_ERROR_EQUAL(werr, WERR_OBJECT_NOT_FOUND)) {
-                               return werr;
-                       }
-                       werr = WERR_OK;
-
-                       anc_obj = talloc_zero(mem_ctx,
-                                       struct drsuapi_DsReplicaObjectListItemEx);
-                       if (anc_obj == NULL) {
-                               return WERR_NOT_ENOUGH_MEMORY;
-                       }
-
-                       anc_dn = ldb_dn_new_fmt(anc_obj, sam_ctx, "<GUID=%s>",
-                                       GUID_string(anc_obj, next_anc_guid));
-                       if (anc_dn == NULL) {
-                               return WERR_NOT_ENOUGH_MEMORY;
-                       }
-
-                       ret = drsuapi_search_with_extended_dn(sam_ctx, anc_obj,
-                                                             &anc_res, anc_dn,
-                                                             LDB_SCOPE_BASE,
-                                                             msg_attrs, NULL);
-                       if (ret != LDB_SUCCESS) {
-                               const char *anc_str = NULL;
-                               const char *obj_str = NULL;
-
-                               anc_str = ldb_dn_get_extended_linearized(anc_obj,
-                                                                        anc_dn,
-                                                                        1);
-                               obj_str = ldb_dn_get_extended_linearized(anc_obj,
-                                                                        msg->dn,
-                                                                        1),
-
-                               DBG_ERR("getncchanges: failed to fetch ANC "
-                                       "DN %s for DN %s - %s\n",
-                                       anc_str, obj_str,
-                                       ldb_errstring(sam_ctx));
-                               return WERR_DS_DRA_INCONSISTENT_DIT;
-                       }
-
-                       anc_msg = anc_res->msgs[0];
-
-                       werr = get_nc_changes_build_object(anc_obj, anc_msg,
-                                                          sam_ctx,
-                                                          getnc_state->ncRoot_dn,
-                                                          getnc_state->is_schema_nc,
-                                                          schema, &session_key,
-                                                          getnc_state->min_usn,
-                                                          req10->replica_flags,
-                                                          req10->partial_attribute_set,
-                                                          req10->uptodateness_vector,
-                                                          req10->extended_op,
-                                                          false, /* force_object_return */
-                                                          local_pas,
-                                                          machine_dn,
-                                                          next_anc_guid);
-                       if (!W_ERROR_IS_OK(werr)) {
-                               return werr;
-                       }
-
-                       werr = get_nc_changes_add_links(sam_ctx, getnc_state,
-                                                       getnc_state->ncRoot_dn,
-                                                       getnc_state->is_schema_nc,
-                                                       schema, getnc_state->min_usn,
-                                                       req10->replica_flags,
-                                                       anc_msg,
-                                                       &getnc_state->la_list,
-                                                       &getnc_state->la_count,
-                                                       req10->uptodateness_vector);
-                       if (!W_ERROR_IS_OK(werr)) {
-                               return werr;
-                       }
-
-                       /*
-                        * Regardless of if we actually use it or not,
-                        * we add it to the cache so we don't look at it again
-                        */
-                       werr = dcesrv_drsuapi_obj_cache_add(getnc_state->obj_cache,
-                                                           next_anc_guid);
+               /*
+                * For GET_ANC, prepend any parents that the client needs
+                * to know about before it can add this object
+                */
+               if (getnc_state->is_get_anc) {
+                       werr = getncchanges_add_ancestors(obj, msg->dn, mem_ctx,
+                                                         sam_ctx, getnc_state,
+                                                         schema, &session_key,
+                                                         req10, local_pas,
+                                                         machine_dn,
+                                                         &new_objs);
                        if (!W_ERROR_IS_OK(werr)) {
                                return werr;
                        }
-
-                       /*
-                        * Any ancestors which are below the highwatermark
-                        * or uptodateness_vector shouldn't be added,
-                        * but we still look further up the
-                        * tree for ones which have been changed recently.
-                        */
-                       if (anc_obj->meta_data_ctr != NULL) {
-                               /*
-                                * prepend it to the list
-                                */
-                               anc_obj->next_object = new_objs;
-                               new_objs = anc_obj;
-                       }
-
-                       anc_msg = NULL;
-                       TALLOC_FREE(anc_res);
-                       TALLOC_FREE(anc_dn);
-
-                       /*
-                        * We may need to resolve more...
-                        */
-                       next_anc_guid = anc_obj->parent_object_guid;
                }
 
-               *currentObject = new_objs;
-               while (new_objs != NULL) {
-                       r->out.ctr->ctr6.object_count += 1;
-                       if (new_objs->next_object == NULL) {
-                               currentObject = &new_objs->next_object;
-                       }
-                       new_objs = new_objs->next_object;
-               }
+               /*
+                * Add the object (and any parents it might have) into the
+                * response message
+                */
+               getncchanges_add_objs_to_resp(&repl_chunk, new_objs);
 
                DEBUG(8,(__location__ ": replicating object %s\n", ldb_dn_get_linearized(msg->dn)));