s4-drs: fixed the cursor generation to always be filled in
authorAndrew Tridgell <tridge@samba.org>
Sat, 12 Sep 2009 01:14:29 +0000 (11:14 +1000)
committerAndrew Tridgell <tridge@samba.org>
Sat, 12 Sep 2009 01:15:28 +0000 (11:15 +1000)
We were relying on the uSNChanged>=n search always finding the DN of
the root of the partition, but this now doesn't happen very often as
we are now restricting when we change uSNChanged. This means we need
to always load the replUpToDateVector attribute from the NC root and
use it to populate the cursors in the return.

source4/rpc_server/drsuapi/getncchanges.c

index 338888610527c9fa895c95323bc98582d23be055..725a380f89b1f682b71102ed82f04037894b9533 100644 (file)
@@ -42,14 +42,17 @@ static WERROR get_nc_changes_build_object(struct drsuapi_DsReplicaObjectListItem
                                          struct ldb_context *sam_ctx,
                                          struct ldb_dn *ncRoot_dn,
                                          struct dsdb_schema *schema,
-                                         DATA_BLOB *session_key)
+                                         DATA_BLOB *session_key,
+                                         uint64_t highest_usn)
 {
        const struct ldb_val *md_value;
-       int i;
+       int i, n;
        struct ldb_dn *obj_dn;
        struct replPropertyMetaDataBlob md;
        struct dom_sid *sid;
        uint32_t rid = 0;
+       enum ndr_err_code ndr_err;
+       uint32_t *attids;
 
        if (ldb_dn_compare(ncRoot_dn, msg->dn) == 0) {
                obj->is_nc_prefix = true;
@@ -61,34 +64,44 @@ static WERROR get_nc_changes_build_object(struct drsuapi_DsReplicaObjectListItem
        }
        obj->next_object = NULL;
        
-       obj->meta_data_ctr = talloc(obj, struct drsuapi_DsReplicaMetaDataCtr);
        md_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
-       if (md_value) {
-               enum ndr_err_code ndr_err;
-               ndr_err = ndr_pull_struct_blob(md_value, obj,
-                                              lp_iconv_convenience(ldb_get_opaque(sam_ctx, "loadparm")), &md,
-                                              (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
-               if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
-                       return WERR_DS_DRA_INTERNAL_ERROR;
-               }
-               
-               if (md.version != 1) {
-                       return WERR_DS_DRA_INTERNAL_ERROR;
-               }
-               
-               obj->meta_data_ctr->count = md.ctr.ctr1.count;
-               obj->meta_data_ctr->meta_data = talloc_array(obj, struct drsuapi_DsReplicaMetaData, md.ctr.ctr1.count);
-               for (i=0; i<md.ctr.ctr1.count; i++) {
-                       obj->meta_data_ctr->meta_data[i].originating_change_time = md.ctr.ctr1.array[i].originating_change_time;
-                       obj->meta_data_ctr->meta_data[i].version = md.ctr.ctr1.array[i].version;
-                       obj->meta_data_ctr->meta_data[i].originating_invocation_id = md.ctr.ctr1.array[i].originating_invocation_id;
-                       obj->meta_data_ctr->meta_data[i].originating_usn = md.ctr.ctr1.array[i].originating_usn;
-               }
-       } else {
-               obj->meta_data_ctr->meta_data = talloc(obj, struct drsuapi_DsReplicaMetaData);
-               obj->meta_data_ctr->count = 0;
-               ZERO_STRUCT(md);
+       if (!md_value) {
+               /* nothing to send */
+               return WERR_OK;
+       }
+
+       ndr_err = ndr_pull_struct_blob(md_value, obj,
+                                      lp_iconv_convenience(ldb_get_opaque(sam_ctx, "loadparm")), &md,
+                                      (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+               return WERR_DS_DRA_INTERNAL_ERROR;
+       }
+       
+       if (md.version != 1) {
+               return WERR_DS_DRA_INTERNAL_ERROR;
        }
+
+       obj->meta_data_ctr = talloc(obj, struct drsuapi_DsReplicaMetaDataCtr);
+       attids = talloc_array(obj, uint32_t, md.ctr.ctr1.count);
+       
+       obj->meta_data_ctr->meta_data = talloc_array(obj, struct drsuapi_DsReplicaMetaData, md.ctr.ctr1.count);
+       for (n=i=0; i<md.ctr.ctr1.count; i++) {
+               if (md.ctr.ctr1.array[i].originating_usn < highest_usn) continue;
+               obj->meta_data_ctr->meta_data[n].originating_change_time = md.ctr.ctr1.array[i].originating_change_time;
+               obj->meta_data_ctr->meta_data[n].version = md.ctr.ctr1.array[i].version;
+               obj->meta_data_ctr->meta_data[n].originating_invocation_id = md.ctr.ctr1.array[i].originating_invocation_id;
+               obj->meta_data_ctr->meta_data[n].originating_usn = md.ctr.ctr1.array[i].originating_usn;
+               attids[n] = md.ctr.ctr1.array[i].attid;
+               n++;
+       }
+       if (n == 0) {
+               /* nothing to send */
+               talloc_free(obj->meta_data_ctr);
+               obj->meta_data_ctr = NULL;
+               return WERR_OK;
+       }
+       obj->meta_data_ctr->count = n;
+
        obj->object.identifier = talloc(obj, struct drsuapi_DsReplicaObjectIdentifier);
        obj_dn = ldb_msg_find_attr_as_dn(sam_ctx, obj, msg, "distinguishedName");
        obj->object.identifier->dn = ldb_dn_get_linearized(obj_dn);
@@ -114,18 +127,18 @@ static WERROR get_nc_changes_build_object(struct drsuapi_DsReplicaObjectListItem
                struct ldb_message_element *el;
                WERROR werr;
 
-               sa = dsdb_attribute_by_attributeID_id(schema, md.ctr.ctr1.array[i].attid);
+               sa = dsdb_attribute_by_attributeID_id(schema, attids[i]);
                if (!sa) {
-                       DEBUG(0,("Unable to find attributeID %u in schema\n", md.ctr.ctr1.array[i].attid));
+                       DEBUG(0,("Unable to find attributeID %u in schema\n", attids[i]));
                        return WERR_DS_DRA_INTERNAL_ERROR;
                }
 
                el = ldb_msg_find_element(msg, sa->lDAPDisplayName);
                if (el == NULL) {
                        DEBUG(0,("No element '%s' for attributeID %u in message\n", 
-                                sa->lDAPDisplayName, md.ctr.ctr1.array[i].attid));
+                                sa->lDAPDisplayName, attids[i]));
                        ZERO_STRUCT(obj->object.attribute_ctr.attributes[i]);
-                       obj->object.attribute_ctr.attributes[i].attid = md.ctr.ctr1.array[i].attid;
+                       obj->object.attribute_ctr.attributes[i].attid = attids[i];
                } else {
                        werr = dsdb_attribute_ldb_to_drsuapi(sam_ctx, schema, el, obj,
                                                             &obj->object.attribute_ctr.attributes[i]);
@@ -156,62 +169,92 @@ static int replmd_drsuapi_DsReplicaCursor2_compare(const struct drsuapi_DsReplic
        return GUID_compare(&c1->source_dsa_invocation_id, &c2->source_dsa_invocation_id);
 }
 
-static WERROR get_nc_changes_udv(struct drsuapi_DsReplicaCursor2CtrEx *udv,
-                                struct ldb_message *msg,
-                                struct ldb_context *sam_ctx)
+/*
+  load replUpToDateVector from a DN
+ */
+static WERROR load_udv(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
+                      struct ldb_dn *dn, struct replUpToDateVectorBlob *ouv)
 {
-       uint32_t it_value;
-
-       it_value = ldb_msg_find_attr_as_uint(msg, "instanceType", 0);
-       if ((it_value & INSTANCE_TYPE_IS_NC_HEAD) == INSTANCE_TYPE_IS_NC_HEAD) {
-               const struct ldb_val *ouv_value;
-               struct drsuapi_DsReplicaCursor2 *tmp_cursor;
-               uint64_t highest_commited_usn;
-               NTTIME now;
-               time_t t = time(NULL);
-
-               int ret = ldb_sequence_number(sam_ctx, LDB_SEQ_HIGHEST_SEQ, &highest_commited_usn);
-               if (ret != LDB_SUCCESS) {
-                       return WERR_DS_DRA_INTERNAL_ERROR;
-               }
-               tmp_cursor = talloc(udv, struct drsuapi_DsReplicaCursor2);
-               tmp_cursor->source_dsa_invocation_id = *(samdb_ntds_invocation_id(sam_ctx));
-               tmp_cursor->highest_usn = highest_commited_usn;
-               unix_to_nt_time(&now, t);
-               tmp_cursor->last_sync_success = now;
-
-               ouv_value = ldb_msg_find_ldb_val(msg, "replUpToDateVector");
-               if (ouv_value) {
-                       struct replUpToDateVectorBlob ouv;
-                       enum ndr_err_code ndr_err;
-
-                       ndr_err = ndr_pull_struct_blob(ouv_value, udv,
-                                                      lp_iconv_convenience(ldb_get_opaque(sam_ctx, "loadparm")), &ouv,
-                                                      (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
-                       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
-                               return WERR_DS_DRA_INTERNAL_ERROR;
-                       }
-                       if (ouv.version != 2) {
-                               return WERR_DS_DRA_INTERNAL_ERROR;
-                       }
+       const char *attrs[] = { "replUpToDateVector", NULL };
+       struct ldb_result *res = NULL;
+       TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+       struct ldb_message_element *el;
+       enum ndr_err_code ndr_err;
+
+       ZERO_STRUCTP(ouv);
+
+       if (ldb_search(sam_ctx, tmp_ctx, &res, dn, LDB_SCOPE_BASE, attrs, NULL) != LDB_SUCCESS ||
+           res->count < 1) {
+               DEBUG(0,("load_udv: failed to read partition object\n"));
+               talloc_free(tmp_ctx);
+               return WERR_DS_DRA_INTERNAL_ERROR;
+       }
 
-                       udv->count = ouv.ctr.ctr2.count + 1;
-                       udv->cursors = talloc_steal(udv, ouv.ctr.ctr2.cursors);
-                       udv->cursors = talloc_realloc(udv, udv->cursors, struct drsuapi_DsReplicaCursor2, udv->count);
-                       if (!udv->cursors) {
-                               return WERR_DS_DRA_INTERNAL_ERROR;
-                       }
-                       udv->cursors[udv->count - 1] = *tmp_cursor;
+       el = ldb_msg_find_element(res->msgs[0], "replUpToDateVector");
+       if (el == NULL || el->num_values < 1) {
+               talloc_free(tmp_ctx);
+               ouv->version = 2;
+               return WERR_OK;
+       }
 
-                       qsort(udv->cursors, udv->count,
-                             sizeof(struct drsuapi_DsReplicaCursor2),
-                             (comparison_fn_t)replmd_drsuapi_DsReplicaCursor2_compare);
-               } else {
-                       udv->count = 1;
-                       udv->cursors = talloc_steal(udv, tmp_cursor);
-               }
+       ndr_err = ndr_pull_struct_blob(&el->values[0], 
+                                      mem_ctx, lp_iconv_convenience(ldb_get_opaque(sam_ctx, "loadparm")),
+                                      ouv, 
+                                      (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
+       talloc_free(tmp_ctx);
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+               DEBUG(0,(__location__ ": Failed to parse replUpToDateVector for %s\n",
+                        ldb_dn_get_linearized(dn)));
+               return WERR_DS_DRA_INTERNAL_ERROR;
+       }
+       
+       return WERR_OK;
+       
+}
+
+/*
+  fill in the cursors return based on the replUpToDateVector for the ncRoot_dn
+ */
+static WERROR get_nc_changes_udv(struct ldb_context *sam_ctx,
+                                struct ldb_dn *ncRoot_dn,
+                                struct drsuapi_DsReplicaCursor2CtrEx *udv)
+{
+       WERROR werr;
+       struct drsuapi_DsReplicaCursor2 *tmp_cursor;
+       uint64_t highest_commited_usn;
+       NTTIME now;
+       time_t t = time(NULL);
+       int ret;
+       struct replUpToDateVectorBlob ouv;
+
+       werr = load_udv(sam_ctx, udv, ncRoot_dn, &ouv);
+       if (!W_ERROR_IS_OK(werr)) {
+               return werr;
+       }
+       
+       ret = ldb_sequence_number(sam_ctx, LDB_SEQ_HIGHEST_SEQ, &highest_commited_usn);
+       if (ret != LDB_SUCCESS) {
+               return WERR_DS_DRA_INTERNAL_ERROR;
        }
 
+       tmp_cursor = talloc(udv, struct drsuapi_DsReplicaCursor2);
+       tmp_cursor->source_dsa_invocation_id = *(samdb_ntds_invocation_id(sam_ctx));
+       tmp_cursor->highest_usn = highest_commited_usn;
+       unix_to_nt_time(&now, t);
+       tmp_cursor->last_sync_success = now;
+
+       udv->count = ouv.ctr.ctr2.count + 1;
+       udv->cursors = talloc_steal(udv, ouv.ctr.ctr2.cursors);
+       udv->cursors = talloc_realloc(udv, udv->cursors, struct drsuapi_DsReplicaCursor2, udv->count);
+       if (!udv->cursors) {
+               return WERR_DS_DRA_INTERNAL_ERROR;
+       }
+       udv->cursors[udv->count - 1] = *tmp_cursor;
+       
+       qsort(udv->cursors, udv->count,
+             sizeof(struct drsuapi_DsReplicaCursor2),
+             (comparison_fn_t)replmd_drsuapi_DsReplicaCursor2_compare);
+
        return WERR_OK;
 }
 
@@ -229,10 +272,11 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
        int i;
        struct dsdb_schema *schema;
        struct drsuapi_DsReplicaOIDMapping_Ctr *ctr;
-       struct drsuapi_DsReplicaObjectListItemEx *currentObject;
+       struct drsuapi_DsReplicaObjectListItemEx **currentObject;
        NTSTATUS status;
        DATA_BLOB session_key;
        const char *attrs[] = { "*", "parentGUID", NULL };
+       WERROR werr;
 
        /*
         * connect to the samdb. TODO: We need to check that the caller
@@ -260,9 +304,6 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
                return WERR_DS_DRA_BAD_NC;
        }
 
-       DEBUG(4,("DsGetNSChanges with uSNChanged >= %llu\n", 
-                (unsigned long long)r->in.req->req8.highwatermark.highest_usn));
-
        /* we need the session key for encrypting password attributes */
        status = dcesrv_inherited_session_key(dce_call->conn, &session_key);
        if (!NT_STATUS_IS_OK(status)) {
@@ -312,37 +353,46 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
        r->out.ctr->ctr6.uptodateness_vector->reserved1 = 0;
        r->out.ctr->ctr6.uptodateness_vector->reserved2 = 0;
 
-       r->out.ctr->ctr6.first_object = talloc(mem_ctx, struct drsuapi_DsReplicaObjectListItemEx);
-       currentObject = r->out.ctr->ctr6.first_object;
+       r->out.ctr->ctr6.first_object = NULL;
+       currentObject = &r->out.ctr->ctr6.first_object;
 
        for(i=0; i<site_res->count; i++) {
                int uSN;
-               WERROR werr;
+               struct drsuapi_DsReplicaObjectListItemEx *obj;
+               obj = talloc_zero(mem_ctx, struct drsuapi_DsReplicaObjectListItemEx);
 
                uSN = ldb_msg_find_attr_as_int(site_res->msgs[i], "uSNChanged", -1);
-               r->out.ctr->ctr6.object_count++;
                if (uSN > r->out.ctr->ctr6.new_highwatermark.highest_usn) {
                        r->out.ctr->ctr6.new_highwatermark.highest_usn = uSN;
                }
 
-               werr = get_nc_changes_build_object(currentObject, site_res->msgs[i], sam_ctx, ncRoot_dn, 
-                                                  schema, &session_key);
+               werr = get_nc_changes_build_object(obj, site_res->msgs[i], sam_ctx, ncRoot_dn, 
+                                                  schema, &session_key, r->in.req->req8.highwatermark.highest_usn);
                if (!W_ERROR_IS_OK(werr)) {
-                       r->out.ctr->ctr6.first_object = NULL;
                        return werr;
                }
 
-               werr = get_nc_changes_udv(r->out.ctr->ctr6.uptodateness_vector, site_res->msgs[i], sam_ctx);
-               if (!W_ERROR_IS_OK(werr)) {
-                        return werr;
+               if (obj->meta_data_ctr == NULL) {
+                       /* no attributes to send */
+                       talloc_free(obj);
+                       continue;
                }
 
-               if (i == (site_res->count-1)) {
-                       break;
-               }
-               currentObject->next_object = talloc_zero(mem_ctx, struct drsuapi_DsReplicaObjectListItemEx);
-               currentObject = currentObject->next_object;
+               r->out.ctr->ctr6.object_count++;
+               
+               *currentObject = obj;
+               currentObject = &obj->next_object;
        }
 
+       werr = get_nc_changes_udv(sam_ctx, ncRoot_dn, r->out.ctr->ctr6.uptodateness_vector);
+       if (!W_ERROR_IS_OK(werr)) {
+               return werr;
+       }
+
+
+       DEBUG(4,("DsGetNSChanges with uSNChanged >= %llu on %s gave %u objects\n", 
+                (unsigned long long)r->in.req->req8.highwatermark.highest_usn,
+                ncRoot->dn, r->out.ctr->ctr6.object_count));
+
        return WERR_OK;
 }