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 {
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
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"));
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;
+ }
}
/*
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