uint64_t mod_usn_urgent;
} *ncs;
struct ldb_dn *schema_dn;
+ bool originating_updates;
};
struct la_entry {
bool active;
};
+/*
+ a ldb_modify request operating on modules below the
+ current module
+ */
+static int linked_attr_modify(struct ldb_module *module,
+ const struct ldb_message *message,
+ struct ldb_request *parent)
+{
+ struct ldb_request *mod_req;
+ int ret;
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ TALLOC_CTX *tmp_ctx = talloc_new(module);
+ struct ldb_result *res;
+
+ res = talloc_zero(tmp_ctx, struct ldb_result);
+ if (!res) {
+ talloc_free(tmp_ctx);
+ return ldb_oom(ldb_module_get_ctx(module));
+ }
+
+ ret = ldb_build_mod_req(&mod_req, ldb, tmp_ctx,
+ message,
+ NULL,
+ res,
+ ldb_modify_default_callback,
+ parent);
+ LDB_REQ_SET_LOCATION(mod_req);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ ret = ldb_request_add_control(mod_req, DSDB_CONTROL_REPLICATED_UPDATE_OID,
+ false, NULL);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ /* Run the new request */
+ ret = ldb_next_request(module, mod_req);
+
+ if (ret == LDB_SUCCESS) {
+ ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL);
+ }
+
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
/*
process a backlinks we accumulated during a transaction, adding and
deleting the backlinks from the target objects
add a backlink to the list of backlinks to add/delete in the prepare
commit
*/
-static int replmd_add_backlink(struct ldb_module *module, const struct dsdb_schema *schema,
- struct GUID *forward_guid, struct GUID *target_guid,
- bool active, const struct dsdb_attribute *schema_attr, bool immediate)
+static int replmd_add_backlink(struct ldb_module *module,
+ struct replmd_private *replmd_private,
+ const struct dsdb_schema *schema,
+ struct GUID *forward_guid,
+ struct GUID *target_guid, bool active,
+ const struct dsdb_attribute *schema_attr,
+ bool immediate)
{
const struct dsdb_attribute *target_attr;
struct la_backlink *bl;
- struct replmd_private *replmd_private =
- talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
if (!target_attr) {
modified_partition->mod_usn_urgent = ac->seq_num;
}
}
+ if (!ac->apply_mode) {
+ replmd_private->originating_updates = true;
+ }
}
if (ac->apply_mode) {
ldb_dn_get_linearized(modified_partition->dn)));
return ret;
}
+
+ if (ldb_dn_compare(modified_partition->dn,
+ replmd_private->schema_dn) == 0) {
+ struct ldb_result *ext_res;
+ ret = dsdb_module_extended(module,
+ replmd_private->schema_dn,
+ &ext_res,
+ DSDB_EXTENDED_SCHEMA_UPDATE_NOW_OID,
+ ext_res,
+ DSDB_FLAG_NEXT_MODULE,
+ parent);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ talloc_free(ext_res);
+ }
+
DLIST_REMOVE(replmd_private->ncs, modified_partition);
talloc_free(modified_partition);
}
This involves setting up the right meta-data in extended DN
components, and creating backlinks to the object
*/
-static int replmd_add_fix_la(struct ldb_module *module, struct ldb_message_element *el,
- uint64_t seq_num, const struct GUID *invocationId, time_t t,
+static int replmd_add_fix_la(struct ldb_module *module,
+ struct replmd_private *replmd_private,
+ struct ldb_message_element *el,
+ uint64_t seq_num, const struct GUID *invocationId, NTTIME now,
struct GUID *guid, const struct dsdb_attribute *sa, struct ldb_request *parent)
{
unsigned int i;
/* We will take a reference to the schema in replmd_add_backlink */
const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL);
- NTTIME now;
-
- unix_to_nt_time(&now, t);
for (i=0; i<el->num_values; i++) {
struct ldb_val *v = &el->values[i];
NTSTATUS status;
int ret;
+ if (dsdb_dn == NULL) {
+ talloc_free(tmp_ctx);
+ return LDB_ERR_INVALID_DN_SYNTAX;
+ }
+
/* note that the DN already has the extended
components from the extended_dn_store module */
status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
return ret;
}
- ret = replmd_add_backlink(module, schema, guid, &target_guid, true, sa, false);
+ ret = replmd_add_backlink(module, replmd_private,
+ schema, guid, &target_guid, true, sa,
+ false);
if (ret != LDB_SUCCESS) {
talloc_free(tmp_ctx);
return ret;
*/
static int replmd_add(struct ldb_module *module, struct ldb_request *req)
{
- struct samldb_msds_intid_persistant *msds_intid_struct;
struct ldb_context *ldb;
struct ldb_control *control;
struct replmd_replicated_request *ac;
is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
- for (i=0; i < msg->num_elements; i++) {
+ for (i=0; i < msg->num_elements;) {
struct ldb_message_element *e = &msg->elements[i];
struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
const struct dsdb_attribute *sa;
- if (e->name[0] == '@') continue;
+ if (e->name[0] == '@') {
+ i++;
+ continue;
+ }
sa = dsdb_attribute_by_lDAPDisplayName(ac->schema, e->name);
if (!sa) {
/* if the attribute is not replicated (0x00000001)
* or constructed (0x00000004) it has no metadata
*/
+ i++;
continue;
}
if (sa->linkID != 0 && functional_level > DS_DOMAIN_FUNCTION_2000) {
- ret = replmd_add_fix_la(module, e, ac->seq_num, &ac->our_invocation_id, t, &guid, sa, req);
+ ret = replmd_add_fix_la(module, replmd_private, e,
+ ac->seq_num,
+ &ac->our_invocation_id, now,
+ &guid, sa, req);
if (ret != LDB_SUCCESS) {
talloc_free(ac);
return ret;
}
/* linked attributes are not stored in
replPropertyMetaData in FL above w2k */
+ i++;
continue;
}
m->originating_usn = ac->seq_num;
m->local_usn = ac->seq_num;
ni++;
+
+ if (!(e->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
+ i++;
+ continue;
+ }
+
+ e->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
+
+ if (e->num_values != 0) {
+ i++;
+ continue;
+ }
+
+ ldb_msg_remove_element(msg, e);
}
/* fix meta data count */
if (control) {
control->critical = 0;
}
- if (ldb_dn_compare_base(replmd_private->schema_dn, req->op.add.message->dn) != 0) {
-
- /* Update the usn in the SAMLDB_MSDS_INTID_OPAQUE opaque */
- msds_intid_struct = (struct samldb_msds_intid_persistant *) ldb_get_opaque(ldb, SAMLDB_MSDS_INTID_OPAQUE);
- if (msds_intid_struct) {
- msds_intid_struct->usn = ac->seq_num;
- }
- }
/* go on with the call chain */
return ldb_next_request(module, down_req);
}
} else if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE) {
may_skip = true;
}
+ } else if (a->linkID != 0 && LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
+ ldb_request_get_control(req, DSDB_CONTROL_REPLMD_VANISH_LINKS) != NULL) {
+ /*
+ * We intentionally skip the version bump when attempting to
+ * vanish links.
+ *
+ * The control is set by dbcheck and expunge-tombstones which
+ * both attempt to be non-replicating. Otherwise, making an
+ * alteration to the replication state would trigger a
+ * broadcast of all expunged objects.
+ */
+ may_skip = true;
+ }
+
+ if (el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA) {
+ may_skip = false;
+ el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
}
if (may_skip) {
NTTIME now,
bool is_schema_nc)
{
+ const char *rdn_name = ldb_dn_get_rdn_name(msg->dn);
+ const struct dsdb_attribute *rdn_attr =
+ dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
+ const char *attr_name = rdn_attr != NULL ?
+ rdn_attr->lDAPDisplayName :
+ rdn_name;
struct ldb_message_element new_el = {
.flags = LDB_FLAG_MOD_REPLACE,
- .name = ldb_dn_get_rdn_name(msg->dn),
+ .name = attr_name,
.num_values = 1,
.values = discard_const_p(struct ldb_val, rdn_new)
};
struct ldb_message_element old_el = {
.flags = LDB_FLAG_MOD_REPLACE,
- .name = ldb_dn_get_rdn_name(msg->dn),
+ .name = attr_name,
.num_values = rdn_old ? 1 : 0,
.values = discard_const_p(struct ldb_val, rdn_old)
};
bool rmd_is_provided;
bool rmd_is_just_resorted = false;
const char *not_rename_attrs[4 + msg->num_elements];
-
+
if (rename_attrs) {
attrs = rename_attrs;
} else {
return LDB_ERR_OPERATIONS_ERROR;
}
- for (i=0; i<msg->num_elements; i++) {
+ for (i=0; i<msg->num_elements;) {
+ struct ldb_message_element *el = &msg->elements[i];
struct ldb_message_element *old_el;
- old_el = ldb_msg_find_element(res->msgs[0], msg->elements[i].name);
- ret = replmd_update_rpmd_element(ldb, msg, &msg->elements[i], old_el, &omd, schema, seq_num,
+
+ old_el = ldb_msg_find_element(res->msgs[0], el->name);
+ ret = replmd_update_rpmd_element(ldb, msg, el, old_el,
+ &omd, schema, seq_num,
our_invocation_id,
now, is_schema_nc,
req);
}
if (!*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
- *is_urgent = replmd_check_urgent_attribute(&msg->elements[i]);
+ *is_urgent = replmd_check_urgent_attribute(el);
+ }
+
+ if (!(el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
+ i++;
+ continue;
}
+ el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
+
+ if (el->num_values != 0) {
+ i++;
+ continue;
+ }
+
+ ldb_msg_remove_element(msg, el);
}
}
struct parsed_dn {
struct dsdb_dn *dsdb_dn;
- struct GUID *guid;
+ struct GUID guid;
struct ldb_val *v;
};
static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
{
- return GUID_compare(pdn1->guid, pdn2->guid);
+ return GUID_compare(&pdn1->guid, &pdn2->guid);
+}
+
+static int GUID_compare_struct(struct GUID *g1, struct GUID g2)
+{
+ return GUID_compare(g1, &g2);
}
static struct parsed_dn *parsed_dn_find(struct parsed_dn *pdn,
}
return NULL;
}
- BINARY_ARRAY_SEARCH(pdn, count, guid, guid, GUID_compare, ret);
+ BINARY_ARRAY_SEARCH(pdn, count, guid, guid, GUID_compare_struct, ret);
return ret;
}
const char *ldap_oid, struct ldb_request *parent)
{
unsigned int i;
+ bool values_are_sorted = true;
struct ldb_context *ldb = ldb_module_get_ctx(module);
if (el == NULL) {
dn = p->dsdb_dn->dn;
- p->guid = talloc(*pdn, struct GUID);
- if (p->guid == NULL) {
- ldb_module_oom(module);
- return LDB_ERR_OPERATIONS_ERROR;
- }
-
- status = dsdb_get_extended_dn_guid(dn, p->guid, "GUID");
+ status = dsdb_get_extended_dn_guid(dn, &p->guid, "GUID");
if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
/* we got a DN without a GUID - go find the GUID */
- int ret = dsdb_module_guid_by_dn(module, dn, p->guid, parent);
+ int ret = dsdb_module_guid_by_dn(module, dn, &p->guid, parent);
if (ret != LDB_SUCCESS) {
ldb_asprintf_errstring(ldb, "Unable to find GUID for DN %s\n",
ldb_dn_get_linearized(dn));
}
return ret;
}
- ret = dsdb_set_extended_dn_guid(dn, p->guid, "GUID");
+ ret = dsdb_set_extended_dn_guid(dn, &p->guid, "GUID");
if (ret != LDB_SUCCESS) {
return ret;
}
} else if (!NT_STATUS_IS_OK(status)) {
return LDB_ERR_OPERATIONS_ERROR;
}
-
+ if (i > 0 && values_are_sorted) {
+ int cmp = parsed_dn_compare(p, &(*pdn)[i - 1]);
+ if (cmp < 0) {
+ values_are_sorted = false;
+ }
+ }
/* keep a pointer to the original ldb_val */
p->v = v;
}
-
- TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
-
+ if (! values_are_sorted) {
+ TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
+ }
return LDB_SUCCESS;
}
/*
check if any links need upgrading from w2k format
-
- The parent_ctx is the ldb_message_element which contains the values array that dns[i].v points at, and which should be used for allocating any new value.
*/
-static int replmd_check_upgrade_links(struct parsed_dn *dns, uint32_t count, struct ldb_message_element *parent_ctx, const struct GUID *invocation_id)
+static int replmd_check_upgrade_links(struct parsed_dn *dns, uint32_t count,
+ struct ldb_message_element *el,
+ const struct GUID *invocation_id)
{
uint32_t i;
for (i=0; i<count; i++) {
uint32_t version;
int ret;
- status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn, &version, "RMD_VERSION");
+ status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn,
+ &version, "RMD_VERSION");
if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ /*
+ * We optimistically assume they are all the same; if
+ * the first one is fixed, they are all fixed.
+ *
+ * If the first one was *not* fixed and we find a
+ * later one that is, that is an occasion to shout
+ * with DEBUG(0).
+ */
+ if (i == 0) {
+ return LDB_SUCCESS;
+ }
+ DEBUG(0, ("Mixed w2k and fixed format "
+ "linked attributes\n"));
continue;
}
/* it's an old one that needs upgrading */
- ret = replmd_update_la_val(parent_ctx->values, dns[i].v, dns[i].dsdb_dn, dns[i].dsdb_dn, invocation_id,
- 1, 1, 0, 0, false);
+ ret = replmd_update_la_val(el->values, dns[i].v,
+ dns[i].dsdb_dn, dns[i].dsdb_dn,
+ invocation_id, 1, 1, 0, 0, false);
if (ret != LDB_SUCCESS) {
return ret;
}
handle adding a linked attribute
*/
static int replmd_modify_la_add(struct ldb_module *module,
+ struct replmd_private *replmd_private,
const struct dsdb_schema *schema,
struct ldb_message *msg,
struct ldb_message_element *el,
/* for each new value, see if it exists already with the same GUID */
for (i=0; i<el->num_values; i++) {
- struct parsed_dn *p = parsed_dn_find(old_dns, old_num_values, dns[i].guid, NULL);
+ struct parsed_dn *p = parsed_dn_find(old_dns, old_num_values, &dns[i].guid, NULL);
if (p == NULL) {
/* this is a new linked attribute value */
new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val, num_new_values+1);
uint32_t rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
+ struct GUID_txt_buf guid_str;
ldb_asprintf_errstring(ldb, "Attribute %s already exists for target GUID %s",
- el->name, GUID_string(tmp_ctx, p->guid));
+ el->name, GUID_buf_string(&p->guid, &guid_str));
talloc_free(tmp_ctx);
/* error codes for 'member' need to be
special cased */
}
}
- ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, true);
+ ret = replmd_add_backlink(module, replmd_private,
+ schema, msg_guid, &dns[i].guid,
+ true, schema_attr, true);
if (ret != LDB_SUCCESS) {
talloc_free(tmp_ctx);
return ret;
handle deleting all active linked attributes
*/
static int replmd_modify_la_delete(struct ldb_module *module,
+ struct replmd_private *replmd_private,
const struct dsdb_schema *schema,
struct ldb_message *msg,
struct ldb_message_element *el,
{
unsigned int i;
struct parsed_dn *dns, *old_dns;
- TALLOC_CTX *tmp_ctx = talloc_new(msg);
+ TALLOC_CTX *tmp_ctx = NULL;
int ret;
const struct GUID *invocation_id;
struct ldb_context *ldb = ldb_module_get_ctx(module);
+ struct ldb_control *vanish_links_ctrl = NULL;
+ bool vanish_links = false;
+ unsigned int num_to_delete = el->num_values;
NTTIME now;
unix_to_nt_time(&now, t);
return LDB_ERR_NO_SUCH_ATTRIBUTE;
}
+ tmp_ctx = talloc_new(msg);
+ if (tmp_ctx == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
if (ret != LDB_SUCCESS) {
talloc_free(tmp_ctx);
invocation_id = samdb_ntds_invocation_id(ldb);
if (!invocation_id) {
+ talloc_free(tmp_ctx);
return LDB_ERR_OPERATIONS_ERROR;
}
return ret;
}
+ if (parent) {
+ vanish_links_ctrl = ldb_request_get_control(parent, DSDB_CONTROL_REPLMD_VANISH_LINKS);
+ if (vanish_links_ctrl) {
+ vanish_links = true;
+ vanish_links_ctrl->critical = false;
+ }
+ }
+
+ el->num_values = 0;
el->values = NULL;
/* see if we are being asked to delete any links that
don't exist or are already deleted */
- for (i=0; i<el->num_values; i++) {
+ for (i=0; i < num_to_delete; i++) {
struct parsed_dn *p = &dns[i];
struct parsed_dn *p2;
uint32_t rmd_flags;
- p2 = parsed_dn_find(old_dns, old_el->num_values, p->guid, NULL);
+ p2 = parsed_dn_find(old_dns, old_el->num_values, &p->guid, NULL);
if (!p2) {
+ struct GUID_txt_buf buf;
ldb_asprintf_errstring(ldb, "Attribute %s doesn't exist for target GUID %s",
- el->name, GUID_string(tmp_ctx, p->guid));
+ el->name, GUID_buf_string(&p->guid, &buf));
if (ldb_attr_cmp(el->name, "member") == 0) {
+ talloc_free(tmp_ctx);
return LDB_ERR_UNWILLING_TO_PERFORM;
} else {
+ talloc_free(tmp_ctx);
return LDB_ERR_NO_SUCH_ATTRIBUTE;
}
}
rmd_flags = dsdb_dn_rmd_flags(p2->dsdb_dn->dn);
if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
+ struct GUID_txt_buf buf;
+ const char *guid_str = GUID_buf_string(&p->guid, &buf);
+ if (vanish_links) {
+ DEBUG(0, ("Deleting deleted linked attribute %s to %s, "
+ "because vanish_links control is set\n",
+ el->name, guid_str));
+ continue;
+ }
ldb_asprintf_errstring(ldb, "Attribute %s already deleted for target GUID %s",
- el->name, GUID_string(tmp_ctx, p->guid));
+ el->name, guid_str);
if (ldb_attr_cmp(el->name, "member") == 0) {
+ talloc_free(tmp_ctx);
return LDB_ERR_UNWILLING_TO_PERFORM;
} else {
+ talloc_free(tmp_ctx);
return LDB_ERR_NO_SUCH_ATTRIBUTE;
}
}
}
- /* for each new value, see if it exists already with the same GUID
- if it is not already deleted and matches the delete list then delete it
- */
- for (i=0; i<old_el->num_values; i++) {
- struct parsed_dn *p = &old_dns[i];
- uint32_t rmd_flags;
+ if (vanish_links) {
+ if (num_to_delete == old_el->num_values || num_to_delete == 0) {
+ el->flags = LDB_FLAG_MOD_REPLACE;
- if (el->num_values && parsed_dn_find(dns, el->num_values, p->guid, NULL) == NULL) {
- continue;
+ for (i = 0; i < old_el->num_values; i++) {
+ ret = replmd_add_backlink(module,
+ replmd_private,
+ schema, msg_guid,
+ &old_dns[i].guid,
+ false, schema_attr,
+ true);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+ }
+ talloc_free(tmp_ctx);
+ return LDB_SUCCESS;
+ } else {
+ unsigned int num_values = 0;
+ unsigned int j = 0;
+ for (i = 0; i < old_el->num_values; i++) {
+ if (parsed_dn_find(dns, num_to_delete, &old_dns[i].guid, NULL) != NULL) {
+ /* The element is in the delete list.
+ mark it dead. */
+ ret = replmd_add_backlink(module,
+ replmd_private,
+ schema,
+ msg_guid,
+ &old_dns[i].guid,
+ false,
+ schema_attr,
+ true);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+ old_dns[i].v->length = 0;
+ } else {
+ num_values++;
+ }
+ }
+ for (i = 0; i < old_el->num_values; i++) {
+ if (old_el->values[i].length != 0) {
+ old_el->values[j] = old_el->values[i];
+ j++;
+ if (j == num_values) {
+ break;
+ }
+ }
+ }
+ old_el->num_values = num_values;
}
+ } else {
- rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
- if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
+ /* for each new value, see if it exists already with the same GUID
+ if it is not already deleted and matches the delete list then delete it
+ */
+ for (i=0; i<old_el->num_values; i++) {
+ struct parsed_dn *p = &old_dns[i];
+ uint32_t rmd_flags;
- ret = replmd_update_la_val(old_el->values, p->v, p->dsdb_dn, p->dsdb_dn,
- invocation_id, seq_num, seq_num, now, 0, true);
- if (ret != LDB_SUCCESS) {
- talloc_free(tmp_ctx);
- return ret;
- }
+ if (num_to_delete && parsed_dn_find(dns, num_to_delete, &p->guid, NULL) == NULL) {
+ continue;
+ }
- ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, true);
- if (ret != LDB_SUCCESS) {
- talloc_free(tmp_ctx);
- return ret;
+ rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
+ if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
+
+ ret = replmd_update_la_val(old_el->values, p->v, p->dsdb_dn, p->dsdb_dn,
+ invocation_id, seq_num, seq_num, now, 0, true);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+ ret = replmd_add_backlink(module, replmd_private,
+ schema, msg_guid, &p->guid,
+ false, schema_attr, true);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
}
}
-
el->values = talloc_steal(msg->elements, old_el->values);
el->num_values = old_el->num_values;
handle replacing a linked attribute
*/
static int replmd_modify_la_replace(struct ldb_module *module,
+ struct replmd_private *replmd_private,
const struct dsdb_schema *schema,
struct ldb_message *msg,
struct ldb_message_element *el,
if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
- ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, false);
+ ret = replmd_add_backlink(module, replmd_private,
+ schema, msg_guid, &old_dns[i].guid,
+ false, schema_attr, false);
if (ret != LDB_SUCCESS) {
talloc_free(tmp_ctx);
return ret;
}
- p = parsed_dn_find(dns, el->num_values, old_p->guid, NULL);
+ p = parsed_dn_find(dns, el->num_values, &old_p->guid, NULL);
if (p) {
/* we don't delete it if we are re-adding it */
continue;
if (old_dns &&
(old_p = parsed_dn_find(old_dns,
- old_num_values, p->guid, NULL)) != NULL) {
+ old_num_values, &p->guid, NULL)) != NULL) {
/* update in place */
ret = replmd_update_la_val(old_el->values, old_p->v, p->dsdb_dn,
old_p->dsdb_dn, invocation_id,
num_new_values++;
}
- ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, false);
+ ret = replmd_add_backlink(module, replmd_private,
+ schema, msg_guid, &dns[i].guid,
+ true, schema_attr, false);
if (ret != LDB_SUCCESS) {
talloc_free(tmp_ctx);
return ret;
handle linked attributes in modify requests
*/
static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
+ struct replmd_private *replmd_private,
struct ldb_message *msg,
uint64_t seq_num, time_t t,
struct ldb_request *parent)
const struct dsdb_schema *schema;
struct GUID old_guid;
- if (seq_num == 0) {
- /* there the replmd_update_rpmd code has already
- * checked and saw that there are no linked
- * attributes */
- return LDB_SUCCESS;
- }
-
if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
- /* don't do anything special for linked attributes */
+ /*
+ * Nothing special is required for modifying or vanishing links
+ * in fl2000 since they are just strings in a multi-valued
+ * attribute.
+ */
+ struct ldb_control *ctrl = ldb_request_get_control(parent,
+ DSDB_CONTROL_REPLMD_VANISH_LINKS);
+ if (ctrl) {
+ ctrl->critical = false;
+ }
return LDB_SUCCESS;
}
+ /*
+ * TODO:
+ *
+ * We should restrict this to the intersection of the list of
+ * linked attributes in the schema and the list of attributes
+ * being modified.
+ *
+ * This will help performance a little, as otherwise we have
+ * to allocate the entire object value-by-value.
+ */
ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
DSDB_FLAG_NEXT_MODULE |
DSDB_SEARCH_SHOW_RECYCLED |
old_el = ldb_msg_find_element(old_msg, el->name);
switch (el->flags & LDB_FLAG_MOD_MASK) {
case LDB_FLAG_MOD_REPLACE:
- ret = replmd_modify_la_replace(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
+ ret = replmd_modify_la_replace(module, replmd_private,
+ schema, msg, el, old_el,
+ schema_attr, seq_num, t,
+ &old_guid, parent);
break;
case LDB_FLAG_MOD_DELETE:
- ret = replmd_modify_la_delete(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
+ ret = replmd_modify_la_delete(module, replmd_private,
+ schema, msg, el, old_el,
+ schema_attr, seq_num, t,
+ &old_guid, parent);
break;
case LDB_FLAG_MOD_ADD:
- ret = replmd_modify_la_add(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
+ ret = replmd_modify_la_add(module, replmd_private,
+ schema, msg, el, old_el,
+ schema_attr, seq_num, t,
+ &old_guid, parent);
break;
default:
ldb_asprintf_errstring(ldb,
static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
{
- struct samldb_msds_intid_persistant *msds_intid_struct;
struct ldb_context *ldb;
struct replmd_replicated_request *ac;
struct ldb_request *down_req;
return ret;
}
- ret = replmd_modify_handle_linked_attribs(module, msg, ac->seq_num, t, req);
+ ret = replmd_modify_handle_linked_attribs(module, replmd_private,
+ msg, ac->seq_num, t, req);
if (ret != LDB_SUCCESS) {
talloc_free(ac);
return ret;
}
}
- if (!ldb_dn_compare_base(replmd_private->schema_dn, msg->dn)) {
- /* Update the usn in the SAMLDB_MSDS_INTID_OPAQUE opaque */
- msds_intid_struct = (struct samldb_msds_intid_persistant *) ldb_get_opaque(ldb, SAMLDB_MSDS_INTID_OPAQUE);
- if (msds_intid_struct) {
- msds_intid_struct->usn = ac->seq_num;
- }
- }
-
/* go on with the call chain */
return ldb_next_request(module, down_req);
}
const struct dsdb_attribute *target_attr;
struct ldb_message_element *el2;
struct ldb_val dn_val;
+ uint32_t dsdb_flags = 0;
if (dsdb_dn_is_deleted_val(&el->values[i])) {
continue;
el2->values = &dn_val;
el2->num_values = 1;
- ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE, parent);
+ /*
+ * Ensure that we tell the modification to vanish any linked
+ * attributes (not simply mark them as isDeleted = TRUE)
+ */
+ dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
+
+ ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, parent);
if (ret != LDB_SUCCESS) {
talloc_free(tmp_ctx);
return ret;
struct ldb_message_element *el;
TALLOC_CTX *tmp_ctx;
struct ldb_result *res, *parent_res;
- const char *preserved_attrs[] = {
+ static const char * const preserved_attrs[] = {
/* yes, this really is a hard coded list. See MS-ADTS
section 3.1.1.5.5.1.1 */
- "nTSecurityDescriptor", "attributeID", "attributeSyntax", "dNReferenceUpdate", "dNSHostName",
- "flatName", "governsID", "groupType", "instanceType", "lDAPDisplayName", "legacyExchangeDN",
- "isDeleted", "isRecycled", "lastKnownParent", "msDS-LastKnownRDN", "mS-DS-CreatorSID",
- "mSMQOwnerID", "nCName", "objectClass", "distinguishedName", "objectGUID", "objectSid",
- "oMSyntax", "proxiedObjectName", "name", "replPropertyMetaData", "sAMAccountName",
- "securityIdentifier", "sIDHistory", "subClassOf", "systemFlags", "trustPartner", "trustDirection",
- "trustType", "trustAttributes", "userAccountControl", "uSNChanged", "uSNCreated", "whenCreated",
- "whenChanged", NULL};
+ "attributeID",
+ "attributeSyntax",
+ "dNReferenceUpdate",
+ "dNSHostName",
+ "flatName",
+ "governsID",
+ "groupType",
+ "instanceType",
+ "lDAPDisplayName",
+ "legacyExchangeDN",
+ "isDeleted",
+ "isRecycled",
+ "lastKnownParent",
+ "msDS-LastKnownRDN",
+ "msDS-PortLDAP",
+ "mS-DS-CreatorSID",
+ "mSMQOwnerID",
+ "nCName",
+ "objectClass",
+ "distinguishedName",
+ "objectGUID",
+ "objectSid",
+ "oMSyntax",
+ "proxiedObjectName",
+ "name",
+ "nTSecurityDescriptor",
+ "replPropertyMetaData",
+ "sAMAccountName",
+ "securityIdentifier",
+ "sIDHistory",
+ "subClassOf",
+ "systemFlags",
+ "trustPartner",
+ "trustDirection",
+ "trustType",
+ "trustAttributes",
+ "userAccountControl",
+ "uSNChanged",
+ "uSNCreated",
+ "whenCreated",
+ "whenChanged",
+ NULL
+ };
+ static const char * const all_attrs[] = {
+ DSDB_SECRET_ATTRIBUTES,
+ "*",
+ NULL
+ };
unsigned int i, el_count = 0;
+ uint32_t dsdb_flags = 0;
enum deletion_state deletion_state, next_deletion_state;
if (ldb_dn_is_special(req->op.del.dn)) {
/* we need the complete msg off disk, so we can work out which
attributes need to be removed */
- ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, NULL,
+ ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, all_attrs,
DSDB_FLAG_NEXT_MODULE |
DSDB_SEARCH_SHOW_RECYCLED |
DSDB_SEARCH_REVEAL_INTERNALS |
ldb_asprintf_errstring(ldb,
__location__
": Failed to remove backlink of "
- "%s when deleting %s",
+ "%s when deleting %s: %s",
el->name,
- old_dn_str);
+ old_dn_str,
+ ldb_errstring(ldb));
talloc_free(tmp_ctx);
return LDB_ERR_OPERATIONS_ERROR;
}
if (sa->searchFlags & SEARCH_FLAG_PRESERVEONDELETE) {
continue;
}
+ } else {
+ /*
+ * Ensure that we tell the modification to vanish any linked
+ * attributes (not simply mark them as isDeleted = TRUE)
+ */
+ dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
}
ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
if (ret != LDB_SUCCESS) {
msg->dn = new_dn;
}
- ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE, req);
+ ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, req);
if (ret != LDB_SUCCESS) {
ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
* 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
+ * out
*/
- ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
+ 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));
ldb_dn_get_linearized(parent_msg->dn));
return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
}
-
+
ret = dsdb_wellknown_dn(ldb_module_get_ctx(ar->module), msg,
nc_root,
DS_GUID_LOSTANDFOUND_CONTAINER,
&guid_str_buf);
filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
- if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
+ if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
ret = ldb_build_search_req(&search_req,
ldb,
ar->req);
LDB_REQ_SET_LOCATION(search_req);
- ret = dsdb_request_add_controls(search_req,
+ ret = dsdb_request_add_controls(search_req,
DSDB_SEARCH_SHOW_RECYCLED|
DSDB_SEARCH_SHOW_DELETED|
DSDB_SEARCH_SHOW_EXTENDED_DN);
"Failed to form conflict DN for %s\n",
ldb_dn_get_linearized(msg->dn));
- return replmd_replicated_request_werror(ar, WERR_NOMEM);
+ return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
}
ret = dsdb_module_rename(ar->module, ar->search_msg->dn, new_dn,
nmd.ctr.ctr1.array = talloc_array(ar,
struct replPropertyMetaData1,
nmd.ctr.ctr1.count);
- if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOMEM);
+ if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
/* first copy the old meta data */
for (i=0; i < omd.ctr.ctr1.count; i++) {
char *tmp_str;
char *filter;
struct ldb_request *search_req;
- static const char *attrs[] = { "*", "parentGUID", "instanceType",
+ static const char *attrs[] = { "repsFrom", "replUpToDateVector",
+ "parentGUID", "instanceType",
"replPropertyMetaData", "nTSecurityDescriptor",
- NULL };
+ "isDeleted", NULL };
struct GUID_txt_buf guid_str_buf;
if (ar->index_current >= ar->objs->num_objects) {
&guid_str_buf);
filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
- if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
+ if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
ret = ldb_build_search_req(&search_req,
ldb,
nuv.ctr.ctr2.cursors = talloc_array(ar,
struct drsuapi_DsReplicaCursor2,
nuv.ctr.ctr2.count);
- if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOMEM);
+ if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
/* first copy the old vector */
for (i=0; i < ouv.ctr.ctr2.count; i++) {
* create the change ldb_message
*/
msg = ldb_msg_new(ar);
- if (!msg) return replmd_replicated_request_werror(ar, WERR_NOMEM);
+ if (!msg) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
msg->dn = ar->search_msg->dn;
ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
struct repsFromToBlob *trf;
trf = talloc(ar, struct repsFromToBlob);
- if (!trf) return replmd_replicated_request_werror(ar, WERR_NOMEM);
+ if (!trf) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
(ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
{
- struct ldb_context *ldb;
+ struct ldb_context *ldb = ldb_module_get_ctx(ar->module);
+ struct replmd_private *replmd_private =
+ talloc_get_type_abort(ldb_module_get_private(ar->module),
+ struct replmd_private);
int ret;
static const char *attrs[] = {
"replUpToDateVector",
};
struct ldb_request *search_req;
- ldb = ldb_module_get_ctx(ar->module);
ar->search_msg = NULL;
+ /*
+ * Let the caller know that we did an originating updates
+ */
+ ar->objs->originating_updates = replmd_private->originating_updates;
+
ret = ldb_build_search_req(&search_req,
ldb,
ar,
uint32_t i;
struct replmd_private *replmd_private =
talloc_get_type(ldb_module_get_private(module), struct replmd_private);
- struct dsdb_control_replicated_update *rep_update;
ldb = ldb_module_get_ctx(module);
if (req->controls) {
req->controls = talloc_memdup(ar, req->controls,
talloc_get_size(req->controls));
- if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOMEM);
+ if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
}
- /* This allows layers further down to know if a change came in
- over replication and what the replication flags were */
- rep_update = talloc_zero(ar, struct dsdb_control_replicated_update);
- if (rep_update == NULL) {
- return ldb_module_oom(module);
- }
- rep_update->dsdb_repl_flags = objs->dsdb_repl_flags;
-
- ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, rep_update);
+ ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL);
if (ret != LDB_SUCCESS) {
return ret;
}
process one linked attribute structure
*/
static int replmd_process_linked_attribute(struct ldb_module *module,
+ struct replmd_private *replmd_private,
struct la_entry *la_entry,
struct ldb_request *parent)
{
/* find the attribute being modified */
attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
if (attr == NULL) {
- DEBUG(0, (__location__ ": Unable to find attributeID 0x%x\n", la->attid));
+ struct GUID_txt_buf guid_str;
+ ldb_asprintf_errstring(ldb, "Unable to find attributeID 0x%x for link on <GUID=%s>",
+ la->attid,
+ GUID_buf_string(&la->identifier->guid,
+ &guid_str));
talloc_free(tmp_ctx);
return LDB_ERR_OPERATIONS_ERROR;
}
if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
/* remove the existing backlink */
- ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, false, attr, false);
+ ret = replmd_add_backlink(module, replmd_private,
+ schema, &la->identifier->guid,
+ &guid, false, attr, true);
if (ret != LDB_SUCCESS) {
talloc_free(tmp_ctx);
return ret;
if (active) {
/* add the new backlink */
- ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, true, attr, false);
+ ret = replmd_add_backlink(module, replmd_private,
+ schema, &la->identifier->guid,
+ &guid, true, attr, true);
if (ret != LDB_SUCCESS) {
talloc_free(tmp_ctx);
return ret;
la->meta_data.originating_usn, seq_num,
la->meta_data.originating_change_time,
la->meta_data.version,
- (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?false:true);
+ !active);
if (ret != LDB_SUCCESS) {
talloc_free(tmp_ctx);
return ret;
}
if (active) {
- ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid,
- true, attr, false);
+ ret = replmd_add_backlink(module, replmd_private,
+ schema, &la->identifier->guid,
+ &guid, true, attr, true);
if (ret != LDB_SUCCESS) {
talloc_free(tmp_ctx);
return ret;
old_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
- ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
+ ret = linked_attr_modify(module, msg, parent);
if (ret != LDB_SUCCESS) {
ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
ldb_errstring(ldb),
talloc_free(e);
}
+ replmd_private->originating_updates = false;
+
return ldb_next_start_trans(module);
}
for (la = DLIST_TAIL(replmd_private->la_list); la; la=prev) {
prev = DLIST_PREV(la);
DLIST_REMOVE(replmd_private->la_list, la);
- ret = replmd_process_linked_attribute(module, la, NULL);
+ ret = replmd_process_linked_attribute(module, replmd_private,
+ la, NULL);
if (ret != LDB_SUCCESS) {
replmd_txn_cleanup(replmd_private);
return ret;