#include "lib/util/binsearch.h"
#include "lib/util/tsort.h"
+static const uint64_t DELETED_OBJECT_CONTAINER_CHANGE_TIME = 253402127999L;
struct replmd_private {
TALLOC_CTX *la_ctx;
struct la_entry *la_list;
uint64_t seq_num;
bool is_urgent;
+ const struct dsdb_attribute *isRecycled;
+ const struct dsdb_attribute *lastKnownRDN;
+ const struct dsdb_attribute *isDeleted;
+ bool is_recycled_tested;
};
enum urgent_situation {
m->attid = sa->attributeID_id;
m->version = 1;
- m->originating_change_time = now;
+ if (m->attid == 0x20030) {
+ const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
+ const char* rdn;
+
+ if (rdn_val == NULL) {
+ ldb_oom(ldb);
+ talloc_free(ac);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ rdn = (const char*)rdn_val->data;
+ if (strcmp(rdn, "Deleted Objects") == 0) {
+ /*
+ * Set the originating_change_time to 29/12/9999 at 23:59:59
+ * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
+ */
+ NTTIME deleted_obj_ts;
+
+ unix_to_nt_time(&deleted_obj_ts, DELETED_OBJECT_CONTAINER_CHANGE_TIME);
+ m->originating_change_time = deleted_obj_ts;
+ } else {
+ m->originating_change_time = now;
+ }
+ } else {
+ m->originating_change_time = now;
+ }
m->originating_invocation_id = *our_invocation_id;
m->originating_usn = ac->seq_num;
m->local_usn = ac->seq_num;
return LDB_SUCCESS;
}
- /* if the attribute's value haven't changed then return LDB_SUCCESS */
+ /* if the attribute's value haven't changed then return LDB_SUCCESS
+ * Unless we have the provision control or if the attribute is
+ * interSiteTopologyGenerator as this page explain: http://support.microsoft.com/kb/224815
+ * this attribute is periodicaly written by the DC responsible for the intersite generation
+ * in a given site
+ */
if (old_el != NULL && ldb_msg_element_compare(el, old_el) == 0) {
- if (!ldb_request_get_control(req, LDB_CONTROL_PROVISION_OID)) {
+ if (strcmp(el->name, "interSiteTopologyGenerator") != 0 &&
+ !ldb_request_get_control(req, LDB_CONTROL_PROVISION_OID)) {
/*
* allow this to make it possible for dbcheck
* to rebuild broken metadata
md1 = &omd->ctr.ctr1.array[i];
md1->version++;
md1->attid = a->attributeID_id;
- md1->originating_change_time = now;
+ if (md1->attid == 0x20030) {
+ const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
+ const char* rdn;
+
+ if (rdn_val == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ rdn = (const char*)rdn_val->data;
+ if (strcmp(rdn, "Deleted Objects") == 0) {
+ /*
+ * Set the originating_change_time to 29/12/9999 at 23:59:59
+ * as specified in MS-ADTS 7.1.1.4.2 Deleted Objects Container
+ */
+ NTTIME deleted_obj_ts;
+
+ unix_to_nt_time(&deleted_obj_ts, DELETED_OBJECT_CONTAINER_CHANGE_TIME);
+ md1->originating_change_time = deleted_obj_ts;
+ } else {
+ md1->originating_change_time = now;
+ }
+ } else {
+ md1->originating_change_time = now;
+ }
md1->originating_invocation_id = *our_invocation_id;
md1->originating_usn = *seq_num;
md1->local_usn = *seq_num;
const char * const *rename_attrs,
struct ldb_message *msg, uint64_t *seq_num,
time_t t,
- bool *is_urgent)
+ bool *is_urgent, bool *rodc)
{
const struct ldb_val *omd_value;
enum ndr_err_code ndr_err;
struct ldb_context *ldb;
struct ldb_message_element *objectclass_el;
enum urgent_situation situation;
- bool rodc, rmd_is_provided;
+ bool rmd_is_provided;
if (rename_attrs) {
attrs = rename_attrs;
DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
DSDB_SEARCH_REVEAL_INTERNALS, req);
- if (ret != LDB_SUCCESS || res->count != 1) {
- DEBUG(0,(__location__ ": Object %s failed to find uSNChanged\n",
- ldb_dn_get_linearized(msg->dn)));
- return LDB_ERR_OPERATIONS_ERROR;
+ if (ret != LDB_SUCCESS) {
+ return ret;
}
objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
DSDB_SEARCH_SHOW_EXTENDED_DN |
DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
DSDB_SEARCH_REVEAL_INTERNALS, req);
- if (ret != LDB_SUCCESS || res->count != 1) {
- DEBUG(0,(__location__ ": Object %s failed to find replPropertyMetaData\n",
- ldb_dn_get_linearized(msg->dn)));
- return LDB_ERR_OPERATIONS_ERROR;
+ if (ret != LDB_SUCCESS) {
+ return ret;
}
objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
if (!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)) {
unsigned instanceType;
- ret = samdb_rodc(ldb, &rodc);
+ ret = samdb_rodc(ldb, rodc);
if (ret != LDB_SUCCESS) {
DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
- } else if (rodc) {
- ldb_asprintf_errstring(ldb, "RODC modify is forbidden\n");
+ } else if (*rodc) {
+ ldb_set_errstring(ldb, "RODC modify is forbidden!");
return LDB_ERR_REFERRAL;
}
continue;
}
if ((schema_attr->linkID & 1) == 1) {
+ if (parent && ldb_request_get_control(parent, DSDB_CONTROL_DBCHECK)) {
+ continue;
+ }
/* Odd is for the target. Illegal to modify */
ldb_asprintf_errstring(ldb,
"attribute %s must not be modified directly, it is a linked attribute", el->name);
struct ldb_message *msg;
time_t t = time(NULL);
int ret;
- bool is_urgent = false;
- struct loadparm_context *lp_ctx;
- char *referral;
+ bool is_urgent = false, rodc = false;
unsigned int functional_level;
const DATA_BLOB *guid_blob;
functional_level = dsdb_functional_level(ldb);
- lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
- struct loadparm_context);
-
/* we have to copy the message as the caller might have it as a const */
msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
if (msg == NULL) {
ldb_msg_remove_attr(msg, "uSNChanged");
ret = replmd_update_rpmd(module, ac->schema, req, NULL,
- msg, &ac->seq_num, t, &is_urgent);
- if (ret == LDB_ERR_REFERRAL) {
+ msg, &ac->seq_num, t, &is_urgent, &rodc);
+ if (rodc && (ret == LDB_ERR_REFERRAL)) {
+ struct loadparm_context *lp_ctx;
+ char *referral;
+
+ lp_ctx = talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
+ struct loadparm_context);
+
referral = talloc_asprintf(req,
"ldap://%s/%s",
lpcfg_dnsdomain(lp_ctx),
ldb_dn_get_linearized(msg->dn));
ret = ldb_module_send_referral(req, referral);
talloc_free(ac);
- return ldb_module_done(req, NULL, NULL, ret);
+ return ret;
}
if (ret != LDB_SUCCESS) {
ret = add_time_element(msg, "whenChanged", t);
if (ret != LDB_SUCCESS) {
talloc_free(ac);
+ ldb_operr(ldb);
return ret;
}
ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
if (ret != LDB_SUCCESS) {
talloc_free(ac);
+ ldb_operr(ldb);
return ret;
}
}
const char *attrs[5] = { NULL, };
time_t t = time(NULL);
int ret;
- bool is_urgent = false;
+ bool is_urgent = false, rodc = false;
ac = talloc_get_type(req->context, struct replmd_replicated_request);
ldb = ldb_module_get_ctx(ac->module);
attrs[4] = NULL;
ret = replmd_update_rpmd(ac->module, ac->schema, req, attrs,
- msg, &ac->seq_num, t, &is_urgent);
- if (ret == LDB_ERR_REFERRAL) {
+ msg, &ac->seq_num, t, &is_urgent, &rodc);
+ if (rodc && (ret == LDB_ERR_REFERRAL)) {
struct ldb_dn *olddn = ac->req->op.rename.olddn;
struct loadparm_context *lp_ctx;
char *referral;
lpcfg_dnsdomain(lp_ctx),
ldb_dn_get_linearized(olddn));
ret = ldb_module_send_referral(req, referral);
- talloc_free(ac);
+ talloc_free(ares);
return ldb_module_done(req, NULL, NULL, ret);
}
if (ret != LDB_SUCCESS) {
talloc_free(ares);
- return ldb_module_done(ac->req, NULL, NULL,
- ldb_error(ldb, ret,
- "failed to call replmd_update_rpmd()"));
+ return ldb_module_done(ac->req, NULL, NULL, ret);
}
if (ac->seq_num == 0) {
ret = add_time_element(msg, "whenChanged", t);
if (ret != LDB_SUCCESS) {
talloc_free(ac);
+ ldb_operr(ldb);
return ret;
}
ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
if (ret != LDB_SUCCESS) {
talloc_free(ac);
+ ldb_operr(ldb);
return ret;
}
}
msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
- ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_DELETE, NULL);
+ ret = ldb_msg_add_empty(msg, "objectCategory", LDB_FLAG_MOD_REPLACE, NULL);
if (ret != LDB_SUCCESS) {
talloc_free(tmp_ctx);
ldb_module_oom(module);
return ret;
}
- ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_DELETE, NULL);
+ ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_REPLACE, NULL);
if (ret != LDB_SUCCESS) {
talloc_free(tmp_ctx);
ldb_module_oom(module);
case OBJECT_RECYCLED:
case OBJECT_TOMBSTONE:
- /* we also mark it as recycled, meaning this object can't be
- recovered (we are stripping its attributes) */
- if (functional_level >= DS_DOMAIN_FUNCTION_2008_R2) {
+ /*
+ * we also mark it as recycled, meaning this object can't be
+ * recovered (we are stripping its attributes).
+ * This is done only if we have this schema object of course ...
+ * This behavior is identical to the one of Windows 2008R2 which
+ * always set the isRecycled attribute, even if the recycle-bin is
+ * not activated and what ever the forest level is.
+ */
+ if (dsdb_attribute_by_lDAPDisplayName(schema, "isRecycled") != NULL) {
ret = ldb_msg_add_string(msg, "isRecycled", "TRUE");
if (ret != LDB_SUCCESS) {
DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
const struct ldb_val *rmd_value, *omd_value;
struct replPropertyMetaDataBlob omd, rmd;
enum ndr_err_code ndr_err;
- bool rename_incoming_record;
+ bool rename_incoming_record, rodc;
struct replPropertyMetaData1 *rmd_name, *omd_name;
if (ares->error != LDB_ERR_ENTRY_ALREADY_EXISTS) {
return replmd_op_callback(req, ares);
}
- /*
+ ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ /*
+ * we have a conflict, and need to decide if we will keep the
+ * new record or the old record
+ */
+ conflict_dn = req->op.add.message->dn;
+
+ if (rodc) {
+ /*
+ * We are on an RODC, or were a GC for this
+ * partition, so we have to fail this until
+ * someone who owns the partition sorts it
+ * out
+ */
+ ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
+ "Conflict adding object '%s' from incoming replication as we are read only for the partition. \n"
+ " - We must fail the operation until a master for this partition resolves the conflict",
+ ldb_dn_get_linearized(conflict_dn));
+ goto failed;
+ }
+
+ /*
* we have a conflict, and need to decide if we will keep the
* new record or the old record
*/
return replmd_op_callback(req, ares);
}
+static int replmd_add_isrecycled(struct replmd_replicated_request *ar,
+ struct ldb_context *ldb,
+ struct ldb_message *msg,
+ struct replPropertyMetaDataBlob *md) {
+ time_t t = time(NULL);
+ NTTIME now;
+ struct replPropertyMetaData1 *m;
+ const struct GUID *our_invocation_id;
+ const struct ldb_val* v;
+ int ret;
+
+ v = ldb_dn_get_rdn_val(msg->dn);
+ if (!v || strcmp((char*)v->data, "Deleted Objects") == 0) {
+ return LDB_SUCCESS;
+ }
+
+ our_invocation_id = samdb_ntds_invocation_id(ldb);
+ if (!our_invocation_id) {
+ ldb_debug_set(ldb, LDB_DEBUG_ERROR,
+ "replmd_add: unable to find invocationId\n");
+ return replmd_replicated_request_error(ar, LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ unix_to_nt_time(&now, t);
+
+ ret = ldb_msg_add_string(msg, "isRecycled", "TRUE");
+ if (ret != LDB_SUCCESS) {
+ return replmd_replicated_request_error(ar, ret);
+ }
+
+ md->ctr.ctr1.count++;
+ md->ctr.ctr1.array = talloc_realloc(ar, md->ctr.ctr1.array,
+ struct replPropertyMetaData1,
+ md->ctr.ctr1.count);
+ if (md->ctr.ctr1.array == NULL) {
+ return replmd_replicated_request_werror(ar, WERR_NOMEM);
+ }
+
+ /* rdn is at the end so shift it to end first */
+ m = &md->ctr.ctr1.array[md->ctr.ctr1.count - 2];
+ md->ctr.ctr1.array[md->ctr.ctr1.count - 1] = *m;
+
+ /* Allocate a new entry in the replPropertyMetadata */
+
+ m->attid = ar->isRecycled->attributeID_id;
+ m->version = 1;
+ m->originating_change_time = now;
+ m->originating_invocation_id = *our_invocation_id;
+ m->originating_usn = ar->seq_num;
+ m->local_usn = ar->seq_num;
+
+ return LDB_SUCCESS;
+}
/*
this is called when a new object comes in over DRS
*/
struct replPropertyMetaDataBlob *md;
struct ldb_val md_value;
unsigned int i;
+ bool is_deleted = false;
+ bool is_recycled = false;
+ bool has_lastknownrdn = false;
int ret;
/*
return replmd_replicated_request_error(ar, ret);
}
+ if (!ar->is_recycled_tested) {
+ ar->isRecycled = dsdb_attribute_by_lDAPDisplayName(ar->schema,
+ "isRecycled");
+ ar->is_recycled_tested = true;
+ }
+
/* remove any message elements that have zero values */
for (i=0; i<msg->num_elements; i++) {
struct ldb_message_element *el = &msg->elements[i];
i--;
continue;
}
+
+ if (ldb_attr_cmp(el->name, "isDeleted") == 0) {
+ struct ldb_val *v = &el->values[0];
+
+ if (strncmp((const char*)v->data, "TRUE", 4) == 0 ) {
+ DEBUG(11, ("Found isDeleted on %s while doing replmd_replicated_apply_add\n",
+ ldb_dn_get_linearized(msg->dn)));
+ is_deleted = true;
+ }
+ continue;
+ }
+
+ if (ldb_attr_cmp(el->name, "isRecycled") == 0) {
+ struct ldb_val *v = &el->values[0];
+
+ /*
+ * Normaly we do not store boolean equals to false, but
+ * nothing forbids to do so, especially if you undelete
+ * an object.
+ */
+ if (strncmp((const char*)v->data, "TRUE", 4) == 0 ) {
+ is_recycled = true;
+ }
+ continue;
+ }
+
+ if (ldb_attr_cmp(el->name, "msDS-LastKnownRDN") == 0) {
+ has_lastknownrdn = true;
+ continue;
+ }
+ }
+
+ if (is_deleted && ar->isRecycled && !is_recycled && !has_lastknownrdn) {
+ /*
+ * The object is deleted and we have the isRecycled attribute in
+ * the schema, but it is missing on the object the recycle-bin
+ * is not activated and it hasn't the msDS-LastKnownRDN so we
+ * mark the object as deleted because it means that it comes
+ * from a pre windows 2008R2 server or from a Samba DC before
+ * changes related to isRecycled.
+ */
+ ret = replmd_add_isrecycled(ar, ldb, msg, md);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
}
/*
false, NULL);
if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
+ if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
+ /* this tells the partition module to make it a
+ partial replica if creating an NC */
+ ret = ldb_request_add_control(change_req,
+ DSDB_CONTROL_PARTIAL_REPLICA,
+ false, NULL);
+ if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
+ }
+
return ldb_next_request(ar->module, change_req);
}
uint32_t j,ni=0;
unsigned int removed_attrs = 0;
int ret;
+ bool found_old_is_deleted = false;
+ bool found_old_is_recycled = false;
+ bool found_old_has_lastknownrdn = false;
+ bool old_is_deleted = false;
+ bool old_is_recycled = false;
+ bool old_has_lastknownrdn = false;
+ bool found_new_is_deleted = false;
+ bool found_new_is_recycled = false;
+ bool found_new_has_lastknownrdn = false;
+ bool new_is_deleted = false;
+ bool new_is_recycled = false;
+ bool new_has_lastknownrdn = false;
ldb = ldb_module_get_ctx(ar->module);
msg = ar->objs->objects[ar->index_current].msg;
ZERO_STRUCT(omd);
omd.version = 1;
+ if (!ar->is_recycled_tested) {
+ ar->isDeleted = dsdb_attribute_by_lDAPDisplayName(ar->schema,
+ "isDeleted");
+ ar->isRecycled = dsdb_attribute_by_lDAPDisplayName(ar->schema,
+ "isRecycled");
+ ar->lastKnownRDN = dsdb_attribute_by_lDAPDisplayName(ar->schema,
+ "msDS-lastKnownRDN");
+ ar->is_recycled_tested = true;
+ }
+
/* find existing meta data */
omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
if (omd_value) {
return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
}
+ for (i=0; i < ar->search_msg->num_elements; i++) {
+ struct ldb_message_element *el = &ar->search_msg->elements[i];
+
+ if (ar->isDeleted && strcmp(el->name, ar->isDeleted->lDAPDisplayName) == 0) {
+ struct ldb_val *v = NULL;
+
+ if (el->num_values == 0) {
+ continue;
+ }
+
+ found_old_is_deleted = true;
+
+ v = &el->values[0];
+
+ if (strncmp((const char*)v->data, "TRUE", 4) == 0) {
+ old_is_deleted = true;
+ }
+ }
+
+ if (ar->isRecycled && strcmp(el->name, ar->isRecycled->lDAPDisplayName) == 0) {
+ struct ldb_val *v = NULL;
+
+ if (el->num_values == 0) {
+ continue;
+ }
+
+ found_old_is_recycled = true;
+
+ v = &el->values[0];
+
+ if (strncmp((const char*)v->data, "TRUE", 4) == 0) {
+ old_is_recycled = true;
+ }
+ }
+
+ if (ar->lastKnownRDN && strcmp(el->name, ar->lastKnownRDN->lDAPDisplayName) == 0) {
+ struct ldb_val *v = NULL;
+
+ if (el->num_values == 0) {
+ continue;
+ }
+
+ found_old_has_lastknownrdn = true;
+
+ v = &el->values[0];
+
+ if (strncmp((const char*)v->data, "TRUE", 4) == 0) {
+ old_has_lastknownrdn = true;
+ }
+ }
+ }
+
ZERO_STRUCT(nmd);
nmd.version = 1;
nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
*/
nmd.ctr.ctr1.count = ni;
+ for (i=0; i < msg->num_elements; i++) {
+ struct ldb_message_element *el = &msg->elements[i];
+
+ if (ar->isDeleted && strcmp(el->name, ar->isDeleted->lDAPDisplayName) == 0) {
+ struct ldb_val *v = NULL;
+
+ if (el->num_values == 0) {
+ continue;
+ }
+
+ found_new_is_deleted = true;
+
+ v = &el->values[0];
+
+ if (strncmp((const char*)v->data, "TRUE", 4) == 0) {
+ new_is_deleted = true;
+ }
+ }
+
+ if (ar->isRecycled && strcmp(el->name, ar->isRecycled->lDAPDisplayName) == 0) {
+ struct ldb_val *v = NULL;
+
+ if (el->num_values == 0) {
+ continue;
+ }
+
+ found_new_is_recycled = true;
+
+ v = &el->values[0];
+
+ if (strncmp((const char*)v->data, "TRUE", 4) == 0) {
+ new_is_recycled = true;
+ }
+ }
+
+ if (ar->lastKnownRDN && strcmp(el->name, ar->lastKnownRDN->lDAPDisplayName) == 0) {
+ struct ldb_val *v = NULL;
+
+ if (el->num_values == 0) {
+ continue;
+ }
+
+ found_new_has_lastknownrdn = true;
+
+ v = &el->values[0];
+
+ if (strncmp((const char*)v->data, "TRUE", 4) == 0) {
+ new_has_lastknownrdn = true;
+ }
+ }
+ }
+
+ is_deleted = false;
+ if (found_old_is_deleted) {
+ is_deleted = old_is_deleted;
+ }
+ if (found_new_is_deleted) {
+ is_deleted = new_is_deleted;
+ }
+
+ is_recycled = false;
+ if (found_old_is_recycled) {
+ is_recycled = old_is_recycled;
+ }
+ if (found_new_is_recycled) {
+ is_recycled = new_is_recycled;
+ }
+
+ has_lastknownrdn = false;
+ if (found_old_has_lastknownrdn) {
+ has_lastknownrdn = old_has_lastknownrdn;
+ }
+ if (found_new_has_lastknownrdn) {
+ has_lastknownrdn = new_has_lastknownrdn;
+ }
+
+ if (is_deleted && ar->isRecycled && !is_recycled && !has_lastknownrdn) {
+ /*
+ * The replicated attributes for the current object has the
+ * isDeleted attribute but not the isRecycled and no the
+ * lastKnownRDN. It means that we knew the object before and
+ * now we are notified that is has been deleted but it's not a
+ * recycled one. If we support the isRecycled attribute we had
+ * this attribute.
+ */
+ ret = replmd_add_isrecycled(ar, ldb, msg, &nmd);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
/*
* the rdn attribute (the alias for the name attribute),
* 'cn' for most objects is the last entry in the meta data array
}
if (ares->type != LDB_REPLY_DONE) {
- ldb_set_errstring(ldb, "Invalid reply type\n!");
+ ldb_asprintf_errstring(ldb, "Invalid LDB reply type %d", ares->type);
return ldb_module_done(ar->req, NULL, NULL,
LDB_ERR_OPERATIONS_ERROR);
}
/* we only change whenChanged and uSNChanged if the seq_num
has changed */
- if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
+ ret = add_time_element(msg, "whenChanged", t);
+ if (ret != LDB_SUCCESS) {
talloc_free(tmp_ctx);
- return ldb_operr(ldb);
+ ldb_operr(ldb);
+ return ret;
}
- if (add_uint64_element(ldb, msg, "uSNChanged",
- seq_num) != LDB_SUCCESS) {
+ ret = add_uint64_element(ldb, msg, "uSNChanged", seq_num);
+ if (ret != LDB_SUCCESS) {
talloc_free(tmp_ctx);
- return ldb_operr(ldb);
+ ldb_operr(ldb);
+ return ret;
}
old_el = ldb_msg_find_element(msg, attr->lDAPDisplayName);