s4-drs: need to set the getncchanges extended_ret on success too
[ira/wip.git] / source4 / rpc_server / drsuapi / getncchanges.c
index 64588f3a5d04b6f1b59347f8f5047535f528970f..ad35de883305a260906f8ad0c62ae79949386577 100644 (file)
@@ -30,6 +30,7 @@
 #include "rpc_server/dcerpc_server_proto.h"
 #include "../libcli/drsuapi/drsuapi.h"
 #include "libcli/security/security.h"
+#include "lib/util/binsearch.h"
 
 /*
   build a DsReplicaObjectIdentifier from a ldb msg
@@ -57,6 +58,29 @@ static struct drsuapi_DsReplicaObjectIdentifier *get_object_identifier(TALLOC_CT
        return identifier;
 }
 
+static int udv_compare(const struct GUID *guid1, struct GUID guid2)
+{
+       return GUID_compare(guid1, &guid2);
+}
+
+/*
+  see if we can filter an attribute using the uptodateness_vector
+ */
+static bool udv_filter(const struct drsuapi_DsReplicaCursorCtrEx *udv,
+                      const struct GUID *originating_invocation_id,
+                      uint64_t originating_usn)
+{
+       const struct drsuapi_DsReplicaCursor *c;
+       if (udv == NULL) return false;
+       BINARY_ARRAY_SEARCH(udv->cursors, udv->count, source_dsa_invocation_id, 
+                           originating_invocation_id, udv_compare, c);
+       if (c && originating_usn <= c->highest_usn) {
+               return true;
+       }
+       return false;
+       
+}
+
 /* 
   drsuapi_DsGetNCChanges for one object
 */
@@ -67,7 +91,8 @@ static WERROR get_nc_changes_build_object(struct drsuapi_DsReplicaObjectListItem
                                          struct dsdb_schema *schema,
                                          DATA_BLOB *session_key,
                                          uint64_t highest_usn,
-                                         uint32_t replica_flags)
+                                         uint32_t replica_flags,
+                                         struct drsuapi_DsReplicaCursorCtrEx *uptodateness_vector)
 {
        const struct ldb_val *md_value;
        int i, n;
@@ -156,6 +181,14 @@ static WERROR get_nc_changes_build_object(struct drsuapi_DsReplicaObjectListItem
                        }
                }
 
+               /* filter by uptodateness_vector */
+               if (md.ctr.ctr1.array[i].attid != DRSUAPI_ATTRIBUTE_instanceType &&
+                   udv_filter(uptodateness_vector,
+                              &md.ctr.ctr1.array[i].originating_invocation_id, 
+                              md.ctr.ctr1.array[i].originating_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;
@@ -164,11 +197,15 @@ static WERROR get_nc_changes_build_object(struct drsuapi_DsReplicaObjectListItem
                n++;
        }
 
-       /*
-         note that if n==0 we still need to send the change, as it
-         could be a rename, which changes the uSNChanged, but not any
-         of the replicated attributes
-        */
+       /* ignore it if its an empty change. Note that renames always
+        * change the 'name' attribute, so they won't be ignored by
+        * this */
+       if (n == 0 ||
+           (n == 1 && attids[0] == DRSUAPI_ATTRIBUTE_instanceType)) {
+               talloc_free(obj->meta_data_ctr);
+               obj->meta_data_ctr = NULL;
+               return WERR_OK;
+       }
 
        obj->meta_data_ctr->count = n;
 
@@ -302,7 +339,8 @@ static WERROR get_nc_changes_add_links(struct ldb_context *sam_ctx,
                                       uint32_t replica_flags,
                                       struct ldb_message *msg,
                                       struct drsuapi_DsReplicaLinkedAttribute **la_list,
-                                      uint32_t *la_count)
+                                      uint32_t *la_count,
+                                      struct drsuapi_DsReplicaCursorCtrEx *uptodateness_vector)
 {
        int i;
        TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
@@ -430,6 +468,10 @@ static WERROR get_nc_changes_udv(struct ldb_context *sam_ctx,
        struct replUpToDateVectorBlob ouv;
        int i;
 
+       udv->version = 2;
+       udv->reserved1 = 0;
+       udv->reserved2 = 0;
+
        werr = load_udv(sam_ctx, udv, ncRoot_dn, &ouv);
        if (!W_ERROR_IS_OK(werr)) {
                return werr;
@@ -588,16 +630,16 @@ static WERROR getncchanges_rid_alloc(struct drsuapi_bind_state *b_state,
        /* work out who is the RID Manager */
        ret = samdb_rid_manager_dn(ldb, mem_ctx, &rid_manager_dn);
        if (ret != LDB_SUCCESS) {
-               DEBUG(0, (__location__ ": Failed to find RID Manager object - %s", ldb_errstring(ldb)));
+               DEBUG(0, (__location__ ": Failed to find RID Manager object - %s\n", ldb_errstring(ldb)));
                return WERR_DS_DRA_INTERNAL_ERROR;
        }
 
-       req_dn = ldb_dn_new(ldb, mem_ctx, req8->naming_context->dn);
+       req_dn = ldb_dn_new(mem_ctx, ldb, req8->naming_context->dn);
        if (!req_dn ||
            !ldb_dn_validate(req_dn) ||
-           ldb_dn_compare(samdb_ntds_settings_dn(ldb), rid_manager_dn) != 0) {
+           ldb_dn_compare(req_dn, rid_manager_dn) != 0) {
                /* that isn't the RID Manager DN */
-               DEBUG(0,(__location__ ": RID Alloc request for wrong DN %s",
+               DEBUG(0,(__location__ ": RID Alloc request for wrong DN %s\n",
                         req8->naming_context->dn));
                ctr6->extended_ret = DRSUAPI_EXOP_ERR_MISMATCH;
                return WERR_OK;
@@ -606,14 +648,15 @@ static WERROR getncchanges_rid_alloc(struct drsuapi_bind_state *b_state,
        /* find the DN of the RID Manager */
        ret = samdb_reference_dn(ldb, mem_ctx, rid_manager_dn, "fSMORoleOwner", &fsmo_role_dn);
        if (ret != LDB_SUCCESS) {
-               DEBUG(0,(__location__ ": Failed to find fSMORoleOwner in RID Manager object - %s",
+               DEBUG(0,(__location__ ": Failed to find fSMORoleOwner in RID Manager object - %s\n",
                         ldb_errstring(ldb)));
+               ctr6->extended_ret = DRSUAPI_EXOP_ERR_FSMO_NOT_OWNER;
                return WERR_DS_DRA_INTERNAL_ERROR;
        }
 
        if (ldb_dn_compare(samdb_ntds_settings_dn(ldb), fsmo_role_dn) != 0) {
                /* we're not the RID Manager - go away */
-               DEBUG(0,(__location__ ": RID Alloc request when not RID Manager"));
+               DEBUG(0,(__location__ ": RID Alloc request when not RID Manager\n"));
                ctr6->extended_ret = DRSUAPI_EXOP_ERR_FSMO_NOT_OWNER;
                return WERR_OK;
        }
@@ -624,12 +667,28 @@ static WERROR getncchanges_rid_alloc(struct drsuapi_bind_state *b_state,
        exop->fsmo_info = req8->fsmo_info;
        exop->destination_dsa_guid = req8->destination_dsa_guid;
 
+       ret = ldb_transaction_start(ldb);
+       if (ret != LDB_SUCCESS) {
+               DEBUG(0,(__location__ ": Failed transaction start - %s\n",
+                        ldb_errstring(ldb)));
+               return WERR_DS_DRA_INTERNAL_ERROR;
+       }
+
        ret = ldb_extended(ldb, DSDB_EXTENDED_ALLOCATE_RID_POOL, exop, &ext_res);
        if (ret != LDB_SUCCESS) {
                DEBUG(0,(__location__ ": Failed extended allocation RID pool operation - %s\n",
                         ldb_errstring(ldb)));
+               ldb_transaction_cancel(ldb);
+               return WERR_DS_DRA_INTERNAL_ERROR;
+       }
+
+       ret = ldb_transaction_commit(ldb);
+       if (ret != LDB_SUCCESS) {
+               DEBUG(0,(__location__ ": Failed transaction commit - %s\n",
+                        ldb_errstring(ldb)));
                return WERR_DS_DRA_INTERNAL_ERROR;
        }
+
        talloc_free(ext_res);
 
        base_dn = samdb_base_dn(ldb);
@@ -637,15 +696,7 @@ static WERROR getncchanges_rid_alloc(struct drsuapi_bind_state *b_state,
        DEBUG(2,("Allocated RID pool for server %s\n",
                 GUID_string(mem_ctx, &req8->destination_dsa_guid)));
 
-       /* to complete the rest of the operation we need to point
-          getncchanges at the base DN for the domain */
-       req8->naming_context->dn = ldb_dn_get_linearized(base_dn);
-       ret = dsdb_find_guid_by_dn(ldb, base_dn, &req8->naming_context->guid);
-       if (ret != LDB_SUCCESS) {
-               DEBUG(0,(__location__ ": Failed to find base DN GUID - %s\n",
-                        ldb_errstring(ldb)));
-               return WERR_DS_DRA_INTERNAL_ERROR;
-       }
+       ctr6->extended_ret = DRSUAPI_EXOP_ERR_SUCCESS;
 
        return WERR_OK;
 }
@@ -662,6 +713,7 @@ struct drsuapi_getncchanges_state {
        struct ldb_dn *last_dn;
        struct drsuapi_DsReplicaLinkedAttribute *la_list;
        uint32_t la_count;
+       struct drsuapi_DsReplicaCursorCtrEx *uptodateness_vector;
 };
 
 /* 
@@ -697,6 +749,7 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
        struct drsuapi_DsGetNCChangesRequest8 *req8;
        uint32_t options;
        uint32_t max_objects;
+       struct ldb_dn *search_dn = NULL;
 
        DCESRV_PULL_HANDLE_WERR(h, r->in.bind_handle, DRSUAPI_BIND_HANDLE);
        b_state = h->data;
@@ -765,6 +818,7 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
        case DRSUAPI_EXOP_FSMO_RID_ALLOC:
                werr = getncchanges_rid_alloc(b_state, mem_ctx, req8, &r->out.ctr->ctr6);
                W_ERROR_NOT_OK_RETURN(werr);
+               search_dn = samdb_base_dn(b_state->sam_ctx);
                break;
 
        case DRSUAPI_EXOP_FSMO_REQ_ROLE:
@@ -847,10 +901,14 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
                        scope = LDB_SCOPE_BASE;
                }
                
+               if (!search_dn) {
+                       search_dn = getnc_state->ncRoot_dn;
+               }
+
                DEBUG(1,(__location__ ": getncchanges on %s using filter %s\n",
                         ldb_dn_get_linearized(getnc_state->ncRoot_dn), search_filter));
                ret = drsuapi_search_with_extended_dn(b_state->sam_ctx, getnc_state, &getnc_state->site_res,
-                                                     getnc_state->ncRoot_dn, scope, attrs,
+                                                     search_dn, scope, attrs,
                                                      search_filter);
                if (ret != LDB_SUCCESS) {
                        return WERR_DS_DRA_INTERNAL_ERROR;
@@ -868,6 +926,14 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
                              (comparison_fn_t)site_res_cmp_usn_order);
                }
 
+               getnc_state->uptodateness_vector = talloc_steal(getnc_state, req8->uptodateness_vector);
+               if (getnc_state->uptodateness_vector) {
+                       /* make sure its sorted */
+                       qsort(getnc_state->uptodateness_vector->cursors, 
+                             getnc_state->uptodateness_vector->count,
+                             sizeof(getnc_state->uptodateness_vector->cursors[0]),
+                             (comparison_fn_t)drsuapi_DsReplicaCursor_compare);
+               }
        }
 
        /* Prefix mapping */
@@ -923,7 +989,7 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
                werr = get_nc_changes_build_object(obj, msg,
                                                   b_state->sam_ctx, getnc_state->ncRoot_dn, 
                                                   schema, &session_key, getnc_state->min_usn,
-                                                  req8->replica_flags);
+                                                  req8->replica_flags, getnc_state->uptodateness_vector);
                if (!W_ERROR_IS_OK(werr)) {
                        return werr;
                }
@@ -934,7 +1000,8 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
                                                req8->replica_flags,
                                                msg,
                                                &getnc_state->la_list,
-                                               &getnc_state->la_count);
+                                               &getnc_state->la_count,
+                                               getnc_state->uptodateness_vector);
                if (!W_ERROR_IS_OK(werr)) {
                        return werr;
                }
@@ -948,7 +1015,7 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
                }
 
                if (obj->meta_data_ctr == NULL) {
-                       DEBUG(0,(__location__ ": getncchanges skipping send of object %s\n",
+                       DEBUG(8,(__location__ ": getncchanges skipping send of object %s\n",
                                 ldb_dn_get_linearized(msg->dn)));
                        /* no attributes to send */
                        talloc_free(obj);
@@ -1004,10 +1071,6 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
                          b_state->sam_ctx, (ldb_qsort_cmp_fn_t)linked_attribute_compare);
 
                r->out.ctr->ctr6.uptodateness_vector = talloc(mem_ctx, struct drsuapi_DsReplicaCursor2CtrEx);
-               r->out.ctr->ctr6.uptodateness_vector->version = 2;
-               r->out.ctr->ctr6.uptodateness_vector->reserved1 = 0;
-               r->out.ctr->ctr6.uptodateness_vector->reserved2 = 0;
-
                r->out.ctr->ctr6.new_highwatermark.highest_usn = r->out.ctr->ctr6.new_highwatermark.tmp_highest_usn;
 
                werr = get_nc_changes_udv(b_state->sam_ctx, getnc_state->ncRoot_dn, 
@@ -1021,6 +1084,12 @@ WERROR dcesrv_drsuapi_DsGetNCChanges(struct dcesrv_call_state *dce_call, TALLOC_
                b_state->getncchanges_state = NULL;
        }
 
+       if (req8->extended_op != DRSUAPI_EXOP_NONE) {
+               r->out.ctr->ctr6.uptodateness_vector = NULL;
+               r->out.ctr->ctr6.nc_object_count = 0;
+               ZERO_STRUCT(r->out.ctr->ctr6.new_highwatermark);
+       }
+
        DEBUG(r->out.ctr->ctr6.more_data?2:1,
              ("DsGetNCChanges with uSNChanged >= %llu flags 0x%08x on %s gave %u objects (done %d/%d la=%d)\n",
               (unsigned long long)(req8->highwatermark.highest_usn+1),