[SCM] Samba Shared Repository - branch master updated

Douglas Bagnall dbagnall at samba.org
Fri Sep 15 08:08:03 UTC 2017


The branch, master has been updated
       via  e115a42 getncchanges.c: Send linked attributes in each chunk
       via  c15c538 getnchanges.c: Avoid unnecessary continue
       via  3a8dfcc getncchanges.c: Split out code to get an object for a response
       via  ec8a7fa getncchanges.c: Reduce the parameters to get_nc_changes_build_object()
       via  1d70375 getncchanges.c: Remove unused ncRoot_dn parameter
       via  85898a6 getncchanges.c: Remove a really old TODO
       via  b4b843b getncchanges.c: Replace hard-coded numbers with a define
       via  63818f0 getncchanges.c: Refactor how objects get added to the response
       via  f77d55f getncchanges.c: Refactor how we add ancestor links
       via  4057438 getncchanges.c: Add ancestor links when the object normally gets sent
       via  2abdd09 getncchanges.c: Split GET_ANC block out into its own function
       via  46b3aab getncchanges.c: Split sorting linked attributes into separate function
       via  240e433 getncchanges.c: Rename anc_cache to obj_cache
      from  3ed9c90 charset: fix str[n]casecmp_m() by comparing lower case values

https://git.samba.org/?p=samba.git;a=shortlog;h=master


- Log -----------------------------------------------------------------
commit e115a4231845b8512eb70478f306f2e8bcbfd64f
Author: Tim Beale <timbeale at catalyst.net.nz>
Date:   Wed Jun 7 10:46:47 2017 +1200

    getncchanges.c: Send linked attributes in each chunk
    
    Instead of sending all the linked attributes at the end, add a
    configurable option to send the links in each replication chunk.
    
    The benefits of this approach are:
    - it can reduce memory overhead, as we don't have to keep all the links
    in memory over the entire replication cycle.
    - the client should never end up knowing about objects but not their
    links. (Although we're not sure that this has actually resulted in
    replication problems, i.e. missing links).
    
    Note that until we support GET_TGT, this approach can mean we now send
    a link where the client doesn't know about the target object, causing
    the client to siliently drop that linked attribute. Hence, this option
    is switched off by default.
    
    Implementation-wise, this code works fairly the same as before. Instead
    of sorting the entire getnc_state->la_sorted array at the end and then
    splitting it up over chunks, we now split the links up over chunks and
    then sort them when we copy them into the message. This should be OK, as
    I believe the MS-DRSR Doc says the links in the message should be sorted
    (rather than sorting *all* the links overall). Windows behaviour seems
    to chunk the links based on USN and then sort them.
    
    getnc_state->la_idx now tracks which links in getnc_state->la_list[]
    have already been sent (instead of tracking getnc_state->la_sorted).
    This means the la_sorted array no longer needs to be stored in
    getnc_state and we can free the array's memory once we've copied the
    links into the message. Unfortunately, the link_given/link_total debug
    no longer reports the correct information, so I've moved these into
    getncchanges_state struct (and now free the struct a bit later so it's
    safe to reference in the debug).
    
    The vampire_dc testenv has been updated to use this new behaviour.
    
    Signed-off-by: Tim Beale <timbeale at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    
    Autobuild-User(master): Douglas Bagnall <dbagnall at samba.org>
    Autobuild-Date(master): Fri Sep 15 10:07:33 CEST 2017 on sn-devel-144

commit c15c53817cd8ef7d7a383aebf138791b63c91f14
Author: Tim Beale <timbeale at catalyst.net.nz>
Date:   Tue Aug 22 17:32:32 2017 +1200

    getnchanges.c: Avoid unnecessary continue
    
    There's not really much after the continue that we're skipping now. We
    can just flip the logic and avoid the continue.
    
    Signed-off-by: Tim Beale <timbeale at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>

commit 3a8dfcc6a1f99a1ec50affc4add809aa870c26d5
Author: Tim Beale <timbeale at catalyst.net.nz>
Date:   Tue Aug 22 17:18:32 2017 +1200

    getncchanges.c: Split out code to get an object for a response
    
    Basically, everytime we try to add an object to the response, we want
    to:
    - Build it (i.e. pack it into an RPC message format)
    - Add it to our object-cache if we're keeping one
    - Add any ancestors needed for the client to resolve it (if GET_ANC)
    
    GET_TGT is going to use the exact same code, so split this out into a
    separate function, rather than duplicating it.
    
    The GET_ANC case also uses almost identical code, but it differs in a
    couple of minor aspects. I've left this as is for now, as I'm not sure
    if this is by accident or by design.
    
    Because all the memory was talloc'd off the 'obj' variable, we now need
    to replace it with a tmp TALLOC_CTX.
    
    Signed-off-by: Tim Beale <timbeale at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>

commit ec8a7fa22ea232b35c3198be551d13d609c55094
Author: Tim Beale <timbeale at catalyst.net.nz>
Date:   Tue Aug 22 16:50:38 2017 +1200

    getncchanges.c: Reduce the parameters to get_nc_changes_build_object()
    
    Fifteen parameters seems a bit excessive. Instead, pass it the structs
    containing the information it cares about.
    
    Signed-off-by: Tim Beale <timbeale at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>

commit 1d70375a1039297b97ae8bb7fe7327a9e516b249
Author: Tim Beale <timbeale at catalyst.net.nz>
Date:   Tue Aug 22 16:29:17 2017 +1200

    getncchanges.c: Remove unused ncRoot_dn parameter
    
    Signed-off-by: Tim Beale <timbeale at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>

commit 85898a6e2f11c440ca418040d3793f87ac80f159
Author: Tim Beale <timbeale at catalyst.net.nz>
Date:   Tue Aug 22 16:21:47 2017 +1200

    getncchanges.c: Remove a really old TODO
    
    This TODO was added in 2009 (before Samba supported linked_attributes
    in getNCChanges())
    
    Signed-off-by: Tim Beale <timbeale at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>

commit b4b843b7101ce105832840f78a581e546771576a
Author: Tim Beale <timbeale at catalyst.net.nz>
Date:   Tue Aug 22 16:19:54 2017 +1200

    getncchanges.c: Replace hard-coded numbers with a define
    
    Signed-off-by: Tim Beale <timbeale at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>

commit 63818f022fc882af926a008dbd8fac952529c0fe
Author: Tim Beale <timbeale at catalyst.net.nz>
Date:   Tue Aug 22 16:17:10 2017 +1200

    getncchanges.c: Refactor how objects get added to the response
    
    Adding GET_TGT support is going to make things more complicated, and I
    think we are going to struggle to do this without refactoring things a
    bit.
    
    This patch adds a helper struct to store state related to a single
    GetNCChanges chunk. I plan to add to this with things like max_links,
    max_objects, etc, which will cutdown on the number of variables/
    parameters we pass around.
    
    I found the double-pointer logic where we add objects to the response
    confusing - hopefully this refactor simplifies things slightly, and it
    allows us to reuse the code for the GET_TGT case.
    
    Signed-off-by: Tim Beale <timbeale at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>

commit f77d55f1be41fabc30c9d278f855d6fef4f23924
Author: Tim Beale <timbeale at catalyst.net.nz>
Date:   Tue Aug 22 16:00:57 2017 +1200

    getncchanges.c: Refactor how we add ancestor links
    
    If the current object had already been sent as an ancestor, we were
    duplicating the code that added its links and updated the HWM mark.
    We want these to occur when we reach the place where the object's USN
    naturally occurs.
    
    Instead of duplicating this code, we can just skip the call to
    get_nc_changes_build_object() if the object has already been sent.
    There is already an existing 'nothing to send'/continue case after we've
    updated the highwater mark.
    
    Signed-off-by: Tim Beale <timbeale at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>

commit 4057438e3b8db6291396d12701b52dea102d57c2
Author: Tim Beale <timbeale at catalyst.net.nz>
Date:   Tue Aug 22 15:45:39 2017 +1200

    getncchanges.c: Add ancestor links when the object normally gets sent
    
    Currently we add links each time we send an object, but we don't
    actually send these links until the end of the replication cycle.
    
    In subsequent patches we want the links to be sent in the same chunk as
    their source object, ideally in as close to USN order as possible.
    Processing ancestors complicates this a bit, as the ancestor will have a
    higher USN than what we're currently up to, and so potentially will the
    ancestor's links.
    
    This patch moves where the ancestor's links get added to the
    getnc_state->la_list. The ancestor's links now get added when the object
    would normally get sent based purely on its USN (we update the highwater
    mark at this point too).
    
    This should not affect functionality, i.e. because we send all the links
    at the end, it should make no difference at what point they get added to
    the list.
    
    This duplicates a tiny bit of code, but this will be cleaned up in the
    next patch.
    
    Signed-off-by: Tim Beale <timbeale at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>

commit 2abdd09aa2e54cccf67766dcb18b0ab0ff48633f
Author: Tim Beale <timbeale at catalyst.net.nz>
Date:   Tue Aug 22 15:34:04 2017 +1200

    getncchanges.c: Split GET_ANC block out into its own function
    
    When we add GET_TGT support, it's going to need to reuse all this code
    (i.e. to add any ancestors of the link target). This also trims down
    the rather large dcesrv_drsuapi_DsGetNCChanges() function a bit.
    
    Note also fixed a compiler warning in the WERR_DS_DRA_INCONSISTENT_DIT
    error block which may have caused issues previously (statement was
    terminated by a ',' rather than a ';').
    
    Signed-off-by: Tim Beale <timbeale at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>

commit 46b3aab51491e19da22c2bac035cf04db9be20e8
Author: Tim Beale <timbeale at catalyst.net.nz>
Date:   Tue Jun 6 15:03:33 2017 +1200

    getncchanges.c: Split sorting linked attributes into separate function
    
    Longer-term we want to split up the links so that they're sent over
    multiple GetNCChanges response messages. So it makes sense to split this
    code out into its own function. In the short-term, this removes some of
    the complexity from dcesrv_drsuapi_DsGetNCChanges() so that the function
    is not quite so big.
    
    Signed-off-by: Tim Beale <timbeale at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>

commit 240e4336117bf22c3d64371fecb608118691a315
Author: Tim Beale <timbeale at catalyst.net.nz>
Date:   Fri Jun 2 14:42:34 2017 +1200

    getncchanges.c: Rename anc_cache to obj_cache
    
    When we add GET_TGT support we will reuse the ancestor cache and it
    should work the same way - if we've already sent an object because it
    was needed for resolving a child object or a link target, then there's
    no point sending it again.
    
    This just renames anc_cache --> obj_cache.
    
    An extra is_get_anc flag has been added to getnc_state - once GET_TGT
    support is added, we can't assume GET_ANC based solely on the existence
    of the obj_cache.
    
    Signed-off-by: Tim Beale <timbeale at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>

-----------------------------------------------------------------------

Summary of changes:
 selftest/target/Samba4.pm                 |   6 +-
 source4/rpc_server/drsuapi/getncchanges.c | 770 +++++++++++++++++++-----------
 2 files changed, 490 insertions(+), 286 deletions(-)


Changeset truncated at 500 lines:

diff --git a/selftest/target/Samba4.pm b/selftest/target/Samba4.pm
index 39a64ae..f0f7042 100755
--- a/selftest/target/Samba4.pm
+++ b/selftest/target/Samba4.pm
@@ -1288,9 +1288,12 @@ sub provision_vampire_dc($$$)
 	my ($self, $prefix, $dcvars, $fl) = @_;
 	print "PROVISIONING VAMPIRE DC @ FL $fl...\n";
 	my $name = "localvampiredc";
+	my $extra_conf = "";
 
 	if ($fl == "2000") {
-	    $name = "vampire2000dc";
+		$name = "vampire2000dc";
+	} else {
+		$extra_conf = "drs: immediate link sync = yes";
 	}
 
 	# We do this so that we don't run the provision.  That's the job of 'net vampire'.
@@ -1310,6 +1313,7 @@ sub provision_vampire_dc($$$)
 	server max protocol = SMB2
 
         ntlm auth = mschapv2-and-ntlmv2-only
+	$extra_conf
 
 [sysvol]
 	path = $ctx->{statedir}/sysvol
diff --git a/source4/rpc_server/drsuapi/getncchanges.c b/source4/rpc_server/drsuapi/getncchanges.c
index cc41abb..cd3f51f 100644
--- a/source4/rpc_server/drsuapi/getncchanges.c
+++ b/source4/rpc_server/drsuapi/getncchanges.c
@@ -45,15 +45,21 @@
 #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 *anc_cache;
+	struct db_context *obj_cache;
 	struct GUID *guids;
 	uint32_t num_records;
 	uint32_t num_processed;
 	struct ldb_dn *ncRoot_dn;
 	struct GUID ncRoot_guid;
 	bool is_schema_nc;
+	bool is_get_anc;
 	uint64_t min_usn;
 	uint64_t max_usn;
 	struct drsuapi_DsReplicaHighWaterMark last_hwm;
@@ -62,15 +68,29 @@ struct drsuapi_getncchanges_state {
 	struct drsuapi_DsReplicaCursor2CtrEx *final_udv;
 	struct drsuapi_DsReplicaLinkedAttribute *la_list;
 	uint32_t la_count;
-	struct la_for_sorting *la_sorted;
 	uint32_t la_idx;
+
+	/* these are just used for debugging the replication's progress */
+	uint32_t links_given;
+	uint32_t total_links;
 };
 
 /* We must keep the GUIDs in NDR form for sorting */
 struct la_for_sorting {
-	struct drsuapi_DsReplicaLinkedAttribute *link;
-	uint8_t target_guid[16];
-        uint8_t source_guid[16];
+	const struct drsuapi_DsReplicaLinkedAttribute *link;
+	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,
@@ -440,15 +460,10 @@ static WERROR get_nc_changes_filter_attrs(struct drsuapi_DsReplicaObjectListItem
 static WERROR get_nc_changes_build_object(struct drsuapi_DsReplicaObjectListItemEx *obj,
 					  const struct ldb_message *msg,
 					  struct ldb_context *sam_ctx,
-					  struct ldb_dn *ncRoot_dn,
-					  bool   is_schema_nc,
+					  struct drsuapi_getncchanges_state *getnc_state,
 					  struct dsdb_schema *schema,
 					  DATA_BLOB *session_key,
-					  uint64_t highest_usn,
-					  uint32_t replica_flags,
-					  struct drsuapi_DsPartialAttributeSet *partial_attribute_set,
-					  struct drsuapi_DsReplicaCursorCtrEx *uptodateness_vector,
-					  enum drsuapi_DsExtendedOperation extended_op,
+					  struct drsuapi_DsGetNCChangesRequest10 *req10,
 					  bool force_object_return,
 					  uint32_t *local_pas,
 					  struct ldb_dn *machine_dn,
@@ -469,6 +484,14 @@ static WERROR get_nc_changes_build_object(struct drsuapi_DsReplicaObjectListItem
 	struct ldb_result *res = NULL;
 	WERROR werr;
 	int ret;
+	uint32_t replica_flags = req10->replica_flags;
+	struct drsuapi_DsPartialAttributeSet *partial_attribute_set =
+			req10->partial_attribute_set;
+	struct drsuapi_DsReplicaCursorCtrEx *uptodateness_vector =
+			req10->uptodateness_vector;
+	enum drsuapi_DsExtendedOperation extended_op = req10->extended_op;
+	bool is_schema_nc = getnc_state->is_schema_nc;
+	uint64_t highest_usn = getnc_state->min_usn;
 
 	/* make dsdb sytanx context for conversions */
 	dsdb_syntax_ctx_init(&syntax_ctx, sam_ctx, schema);
@@ -842,7 +865,6 @@ static WERROR get_nc_changes_add_la(TALLOC_CTX *mem_ctx,
  */
 static WERROR get_nc_changes_add_links(struct ldb_context *sam_ctx,
 				       TALLOC_CTX *mem_ctx,
-				       struct ldb_dn *ncRoot_dn,
 				       bool is_schema_nc,
 				       struct dsdb_schema *schema,
 				       uint64_t highest_usn,
@@ -1935,11 +1957,16 @@ static void dcesrv_drsuapi_update_highwatermark(const struct ldb_message *msg,
 	hwm->reserved_usn = 0;
 }
 
-static WERROR dcesrv_drsuapi_anc_cache_add(struct db_context *anc_cache,
+/**
+ * Adds an object's GUID to the cache of objects already sent.
+ * This avoids us sending the same object multiple times when
+ * the GetNCChanges request uses a flag like GET_ANC.
+ */
+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),
@@ -1960,7 +1987,7 @@ static WERROR dcesrv_drsuapi_anc_cache_add(struct db_context *anc_cache,
 		return WERR_DS_DRA_INTERNAL_ERROR;
 	}
 
-	status = dbwrap_store(anc_cache, key, val, TDB_REPLACE);
+	status = dbwrap_store(obj_cache, key, val, TDB_REPLACE);
 	if (!NT_STATUS_IS_OK(status)) {
 		return WERR_DS_DRA_INTERNAL_ERROR;
 	}
@@ -1968,11 +1995,15 @@ static WERROR dcesrv_drsuapi_anc_cache_add(struct db_context *anc_cache,
 	return WERR_OK;
 }
 
-static WERROR dcesrv_drsuapi_anc_cache_exists(struct db_context *anc_cache,
+/**
+ * Checks if the object with the GUID specified already exists in the
+ * object cache, i.e. it's already been sent in a GetNCChanges response.
+ */
+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),
@@ -1989,7 +2020,7 @@ static WERROR dcesrv_drsuapi_anc_cache_exists(struct db_context *anc_cache,
 		return WERR_DS_DRA_INTERNAL_ERROR;
 	}
 
-	exists = dbwrap_exists(anc_cache, key);
+	exists = dbwrap_exists(obj_cache, key);
 	if (!exists) {
 		return WERR_OBJECT_NOT_FOUND;
 	}
@@ -1997,7 +2028,326 @@ static WERROR dcesrv_drsuapi_anc_cache_exists(struct db_context *anc_cache,
 	return WERR_OBJECT_NAME_EXISTS;
 }
 
-/* 
+/**
+ * Copies the la_list specified into a sorted array, ready to be sent in a
+ * GetNCChanges response.
+ */
+static WERROR getncchanges_get_sorted_array(const struct drsuapi_DsReplicaLinkedAttribute *la_list,
+					    const uint32_t link_count,
+					    struct ldb_context *sam_ctx,
+					    TALLOC_CTX *mem_ctx,
+					    const struct dsdb_schema *schema,
+					    struct la_for_sorting **ret_array)
+{
+	int j;
+	struct la_for_sorting *guid_array;
+	WERROR werr = WERR_OK;
+
+	*ret_array = NULL;
+	guid_array = talloc_array(mem_ctx, struct la_for_sorting, link_count);
+	if (guid_array == NULL) {
+		DEBUG(0, ("Out of memory allocating %u linked attributes for sorting", link_count));
+		return WERR_NOT_ENOUGH_MEMORY;
+	}
+
+	for (j = 0; j < link_count; j++) {
+
+		/* we need to get the target GUIDs to compare */
+		struct dsdb_dn *dn;
+		const struct drsuapi_DsReplicaLinkedAttribute *la = &la_list[j];
+		const struct dsdb_attribute *schema_attrib;
+		const struct ldb_val *target_guid;
+		DATA_BLOB source_guid;
+		TALLOC_CTX *frame = talloc_stackframe();
+		NTSTATUS status;
+
+		schema_attrib = dsdb_attribute_by_attributeID_id(schema, la->attid);
+
+		werr = dsdb_dn_la_from_blob(sam_ctx, schema_attrib, schema, frame, la->value.blob, &dn);
+		if (!W_ERROR_IS_OK(werr)) {
+			DEBUG(0,(__location__ ": Bad la blob in sort\n"));
+			TALLOC_FREE(frame);
+			return werr;
+		}
+
+		/* Extract the target GUID in NDR form */
+		target_guid = ldb_dn_get_extended_component(dn->dn, "GUID");
+		if (target_guid == NULL
+				|| target_guid->length != sizeof(guid_array[0].target_guid)) {
+			status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+		} else {
+			/* Repack the source GUID as NDR for sorting */
+			status = GUID_to_ndr_blob(&la->identifier->guid,
+						  frame,
+						  &source_guid);
+		}
+
+		if (!NT_STATUS_IS_OK(status)
+				|| source_guid.length != sizeof(guid_array[0].source_guid)) {
+			DEBUG(0,(__location__ ": Bad la guid in sort\n"));
+			TALLOC_FREE(frame);
+			return ntstatus_to_werror(status);
+		}
+
+		guid_array[j].link = &la_list[j];
+		memcpy(guid_array[j].target_guid, target_guid->data,
+		       sizeof(guid_array[j].target_guid));
+		memcpy(guid_array[j].source_guid, source_guid.data,
+		       sizeof(guid_array[j].source_guid));
+		TALLOC_FREE(frame);
+	}
+
+	LDB_TYPESAFE_QSORT(guid_array, link_count, NULL, linked_attribute_compare);
+
+	*ret_array = guid_array;
+
+	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,
+						   schema, session_key,
+						   req10,
+						   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;
+		}
+	}
+}
+
+/**
+ * Gets the object to send, packed into an RPC struct ready to send. This also
+ * adds the object to the object cache, and adds any ancestors (if needed).
+ * @param msg - DB search result for the object to add
+ * @param guid - GUID of the object to add
+ * @param ret_obj_list - returns the object ready to be sent (in a list, along
+ * with any ancestors that might be needed). NULL if nothing to send.
+ */
+static WERROR getncchanges_get_obj_to_send(const struct ldb_message *msg,
+					   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,
+					   bool force_object_return,
+					   uint32_t *local_pas,
+					   struct ldb_dn *machine_dn,
+					   const struct GUID *guid,
+					   struct drsuapi_DsReplicaObjectListItemEx **ret_obj_list)
+{
+	struct drsuapi_DsReplicaObjectListItemEx *obj;
+	WERROR werr;
+
+	*ret_obj_list = NULL;
+
+	obj = talloc_zero(mem_ctx, struct drsuapi_DsReplicaObjectListItemEx);
+	W_ERROR_HAVE_NO_MEMORY(obj);
+
+	werr = get_nc_changes_build_object(obj, msg, sam_ctx, getnc_state,
+					   schema, session_key, req10,
+					   force_object_return,
+					   local_pas, machine_dn, guid);
+	if (!W_ERROR_IS_OK(werr)) {
+		return werr;
+	}
+
+	/*
+	 * The object may get filtered out by the UTDV's USN and not actually
+	 * sent, in which case there's nothing more to do here
+	 */
+	if (obj->meta_data_ctr == NULL) {
+		TALLOC_FREE(obj);
+		return WERR_OK;
+	}
+
+	if (getnc_state->obj_cache != NULL) {
+		werr = dcesrv_drsuapi_obj_cache_add(getnc_state->obj_cache,
+						    guid);
+		if (!W_ERROR_IS_OK(werr)) {
+			return werr;
+		}
+	}
+
+	*ret_obj_list = obj;
+
+	/*
+	 * If required, also add any ancestors that the client may need to know
+	 * about before it can resolve this object. These get prepended to the
+	 * ret_obj_list so the client adds them first.
+	 */
+	if (getnc_state->is_get_anc) {
+		werr = getncchanges_add_ancestors(obj, msg->dn, mem_ctx,
+						  sam_ctx, getnc_state,


-- 
Samba Shared Repository



More information about the samba-cvs mailing list