From c6578c38a96ee3df5c0c222dd910c1d852ad95aa Mon Sep 17 00:00:00 2001 From: Bob Campbell Date: Mon, 5 Sep 2016 10:48:13 +1200 Subject: dsdb: refactor part of garbage_collect_tombstones into new function Pair-programmed-with: Garming Sam Signed-off-by: Bob Campbell Signed-off-by: Garming Sam BUG: https://bugzilla.samba.org/show_bug.cgi?id=12382 Reviewed-by: Andrew Bartlett (cherry picked from commit 2dfedffb740ecfe898945a9fc47b24e3c8328d7e) --- source4/dsdb/kcc/garbage_collect_tombstones.c | 385 +++++++++++++++----------- 1 file changed, 216 insertions(+), 169 deletions(-) diff --git a/source4/dsdb/kcc/garbage_collect_tombstones.c b/source4/dsdb/kcc/garbage_collect_tombstones.c index 8d2ea8b477a..62e98137adf 100644 --- a/source4/dsdb/kcc/garbage_collect_tombstones.c +++ b/source4/dsdb/kcc/garbage_collect_tombstones.c @@ -33,6 +33,209 @@ #include "lib/ldb-samba/ldb_matching_rules.h" #include "lib/util/time.h" +static NTSTATUS garbage_collect_tombstones_part(TALLOC_CTX *mem_ctx, + struct ldb_context *samdb, + struct dsdb_ldb_dn_list_node *part, + char *filter, + unsigned int *num_links_removed, + unsigned int *num_objects_removed, + struct dsdb_schema *schema, + const char **attrs, + char **error_string, + NTTIME expunge_time_nttime) +{ + int ret; + struct ldb_dn *do_dn; + struct ldb_result *res; + unsigned int i, j, k; + uint32_t flags; + TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); + if (!tmp_ctx) { + return NT_STATUS_NO_MEMORY; + } + + ret = dsdb_get_deleted_objects_dn(samdb, tmp_ctx, part->dn, &do_dn); + if (ret != LDB_SUCCESS) { + TALLOC_FREE(tmp_ctx); + /* some partitions have no Deleted Objects + container */ + return NT_STATUS_OK; + } + + DEBUG(1, ("Doing a full scan on %s and looking for deleted objects\n", + ldb_dn_get_linearized(part->dn))); + + flags = DSDB_SEARCH_SHOW_RECYCLED | + DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT | + DSDB_SEARCH_REVEAL_INTERNALS; + ret = dsdb_search(samdb, tmp_ctx, &res, part->dn, LDB_SCOPE_SUBTREE, + attrs, flags, "%s", filter); + + if (ret != LDB_SUCCESS) { + *error_string = talloc_asprintf(mem_ctx, + "Failed to search for deleted " + "objects in %s: %s", + ldb_dn_get_linearized(do_dn), + ldb_errstring(samdb)); + TALLOC_FREE(tmp_ctx); + return NT_STATUS_INTERNAL_ERROR; + } + + for (i=0; icount; i++) { + struct ldb_message *cleanup_msg = NULL; + unsigned int num_modified = 0; + + bool isDeleted = ldb_msg_find_attr_as_bool(res->msgs[i], + "isDeleted", false); + if (isDeleted) { + if (ldb_dn_compare(do_dn, res->msgs[i]->dn) == 0) { + /* Skip the Deleted Object Container */ + continue; + } + + ret = dsdb_delete(samdb, res->msgs[i]->dn, + DSDB_SEARCH_SHOW_RECYCLED + |DSDB_MODIFY_RELAX); + if (ret != LDB_SUCCESS) { + DEBUG(1,(__location__ ": Failed to remove " + "deleted object %s\n", + ldb_dn_get_linearized(res-> + msgs[i]->dn))); + } else { + DEBUG(4,("Removed deleted object %s\n", + ldb_dn_get_linearized(res-> + msgs[i]->dn))); + (*num_objects_removed)++; + } + continue; + } + + /* This must have a linked attribute */ + + /* + * From MS-ADTS 3.1.1.1.9 DCs, usn Counters, and + * the Originating Update Stamp + * + * "A link value r is deleted, but exists as a + * tombstone, if r.stamp.timeDeleted ≠ 0. When + * the current time minus r.stamp.timeDeleted + * exceeds the tombstone lifetime, the link + * value r is garbage-collected; that is, + * removed from its containing forward link + * attribute. " + */ + + for (j=0; j < res->msgs[i]->num_elements; j++) { + struct ldb_message_element *element = NULL; + /* TODO this is O(log n) per attribute with deleted values */ + const struct dsdb_attribute *attrib = NULL; + + element = &res->msgs[i]->elements[j]; + attrib = dsdb_attribute_by_lDAPDisplayName(schema, + element->name); + + /* This avoids parsing isDeleted as a link */ + if (attrib->linkID == 0 || ((attrib->linkID & 1) == 1)) { + continue; + } + + for (k = 0; k < element->num_values; k++) { + struct ldb_val *value = &element->values[k]; + uint64_t whenChanged = 0; + NTSTATUS status; + struct dsdb_dn *dn; + struct ldb_message_element *cleanup_elem = NULL; + char *guid_search_str = NULL; + char *guid_buf_str = NULL; + struct ldb_val cleanup_val; + struct GUID_txt_buf buf_guid; + struct GUID guid; + const struct ldb_val *guid_blob; + + if (dsdb_dn_is_deleted_val(value) == false) { + continue; + } + + dn = dsdb_dn_parse(tmp_ctx, samdb, + &element->values[k], + attrib->syntax->ldap_oid); + if (dn == NULL) { + DEBUG(1, ("Failed to parse linked attribute blob of " + "%s on %s while expunging expired links\n", + element->name, + ldb_dn_get_linearized(res->msgs[i]->dn))); + continue; + } + + status = dsdb_get_extended_dn_uint64(dn->dn, + &whenChanged, + "RMD_CHANGETIME"); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Error: RMD_CHANGETIME is missing on a forward link.\n")); + talloc_free(dn); + continue; + } + + if (whenChanged >= expunge_time_nttime) { + talloc_free(dn); + continue; + } + + guid_blob = ldb_dn_get_extended_component(dn->dn, "GUID"); + status = GUID_from_ndr_blob(guid_blob, &guid); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Error: Invalid GUID on link target.\n")); + talloc_free(dn); + continue; + } + + guid_buf_str = GUID_buf_string(&guid, &buf_guid); + guid_search_str = talloc_asprintf(mem_ctx, + "", + guid_buf_str); + cleanup_val = data_blob_string_const(guid_search_str); + + talloc_free(dn); + + if (cleanup_msg == NULL) { + cleanup_msg = ldb_msg_new(mem_ctx); + if (cleanup_msg == NULL) { + return NT_STATUS_NO_MEMORY; + } + cleanup_msg->dn = res->msgs[i]->dn; + } + + ret = ldb_msg_add_value(cleanup_msg, + element->name, + &cleanup_val, + &cleanup_elem); + if (ret != LDB_SUCCESS) { + return NT_STATUS_NO_MEMORY; + } + cleanup_elem->flags = LDB_FLAG_MOD_DELETE; + num_modified++; + } + } + + if (num_modified > 0) { + ret = dsdb_modify(samdb, cleanup_msg, + DSDB_REPLMD_VANISH_LINKS); + if (ret != LDB_SUCCESS) { + DEBUG(1,(__location__ ": Failed to remove deleted object %s\n", + ldb_dn_get_linearized(res->msgs[i]->dn))); + } else { + DEBUG(4,("Removed deleted object %s\n", + ldb_dn_get_linearized(res->msgs[i]->dn))); + *num_links_removed = *num_links_removed + num_modified; + } + + } + } + + TALLOC_FREE(tmp_ctx); + return NT_STATUS_OK; +} + /* * Per MS-ADTS 3.1.1.5.5 Delete Operation * @@ -58,11 +261,9 @@ NTSTATUS dsdb_garbage_collect_tombstones(TALLOC_CTX *mem_ctx, unsigned int *num_links_removed, char **error_string) { - int ret; - const char **attrs = NULL; char *filter = NULL; - + NTSTATUS status; unsigned int i; struct dsdb_attribute *next_attr; unsigned int num_link_attrs; @@ -117,178 +318,24 @@ NTSTATUS dsdb_garbage_collect_tombstones(TALLOC_CTX *mem_ctx, attrs[i] = "isDeleted"; attrs[i+1] = NULL; - filter = talloc_asprintf_append(filter, "(&(isDeleted=TRUE)(whenChanged<=%s)))", expunge_time_string); + filter = talloc_asprintf_append(filter, + "(&(isDeleted=TRUE)(whenChanged<=%s)))", + expunge_time_string); if (filter == NULL) { return NT_STATUS_NO_MEMORY; } - schema = dsdb_get_schema(samdb, mem_ctx); - for (; part != NULL; part = part->next) { - struct ldb_dn *do_dn; - struct ldb_result *res; - unsigned int j, k; - uint32_t flags; - TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); - if (!tmp_ctx) { - return NT_STATUS_NO_MEMORY; - } - - ret = dsdb_get_deleted_objects_dn(samdb, tmp_ctx, part->dn, &do_dn); - if (ret != LDB_SUCCESS) { - TALLOC_FREE(tmp_ctx); - /* some partitions have no Deleted Objects - container */ - continue; - } - - DEBUG(1, ("Doing a full scan on %s and looking for deleted objects\n", - ldb_dn_get_linearized(part->dn))); - - flags = DSDB_SEARCH_SHOW_RECYCLED | - DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT | - DSDB_SEARCH_REVEAL_INTERNALS; - ret = dsdb_search(samdb, tmp_ctx, &res, part->dn, LDB_SCOPE_SUBTREE, - attrs, flags, "%s", filter); - - if (ret != LDB_SUCCESS) { - *error_string = talloc_asprintf(mem_ctx, "Failed to search for deleted objects in %s: %s", - ldb_dn_get_linearized(do_dn), - ldb_errstring(samdb)); - TALLOC_FREE(tmp_ctx); - return NT_STATUS_INTERNAL_ERROR; + status = garbage_collect_tombstones_part(mem_ctx, samdb, part, + filter, + num_links_removed, + num_objects_removed, + schema, attrs, + error_string, + expunge_time_nttime); + if (!NT_STATUS_IS_OK(status)) { + return status; } - - for (i=0; icount; i++) { - struct ldb_message *cleanup_msg = NULL; - unsigned int num_modified = 0; - - bool isDeleted = ldb_msg_find_attr_as_bool(res->msgs[i], "isDeleted", false); - if (isDeleted) { - if (ldb_dn_compare(do_dn, res->msgs[i]->dn) == 0) { - /* Skip the Deleted Object Container */ - continue; - } - - ret = dsdb_delete(samdb, res->msgs[i]->dn, DSDB_SEARCH_SHOW_RECYCLED|DSDB_MODIFY_RELAX); - if (ret != LDB_SUCCESS) { - DEBUG(1,(__location__ ": Failed to remove deleted object %s\n", - ldb_dn_get_linearized(res->msgs[i]->dn))); - } else { - DEBUG(4,("Removed deleted object %s\n", - ldb_dn_get_linearized(res->msgs[i]->dn))); - (*num_objects_removed)++; - } - continue; - } - - /* This must have a linked attribute */ - - /* - * From MS-ADTS 3.1.1.1.9 DCs, usn Counters, and the Originating Update Stamp - * - * "A link value r is deleted, but exists as a - * tombstone, if r.stamp.timeDeleted ≠ 0. When - * the current time minus r.stamp.timeDeleted - * exceeds the tombstone lifetime, the link - * value r is garbage-collected; that is, - * removed from its containing forward link - * attribute. " - */ - - for (j=0; j < res->msgs[i]->num_elements; j++) { - struct ldb_message_element *element = &res->msgs[i]->elements[j]; - /* TODO this is O(log n) per attribute with deleted values */ - const struct dsdb_attribute *attrib - = dsdb_attribute_by_lDAPDisplayName(schema, element->name); - - /* This avoids parsing isDeleted as a link */ - if (attrib->linkID == 0 || ((attrib->linkID & 1) == 1)) { - continue; - } - - for (k = 0; k < element->num_values; k++) { - struct ldb_val *value = &element->values[k]; - uint64_t whenChanged = 0; - NTSTATUS status; - struct dsdb_dn *dn; - struct ldb_message_element *cleanup_elem = NULL; - char *guid_search_str = NULL, *guid_buf_str = NULL; - struct ldb_val cleanup_val; - struct GUID_txt_buf buf_guid; - struct GUID guid; - const struct ldb_val *guid_blob; - - if (dsdb_dn_is_deleted_val(value) == false) { - continue; - } - - dn = dsdb_dn_parse(tmp_ctx, samdb, &element->values[k], - attrib->syntax->ldap_oid); - if (dn == NULL) { - DEBUG(1, ("Failed to parse linked attribute blob of %s on %s while expunging expired links\n", element->name, - ldb_dn_get_linearized(res->msgs[i]->dn))); - continue; - } - - status = dsdb_get_extended_dn_uint64(dn->dn, &whenChanged, "RMD_CHANGETIME"); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(1, ("Error: RMD_CHANGETIME is missing on a forward link.\n")); - talloc_free(dn); - continue; - } - - if (whenChanged >= expunge_time_nttime) { - talloc_free(dn); - continue; - } - - guid_blob = ldb_dn_get_extended_component(dn->dn, "GUID"); - status = GUID_from_ndr_blob(guid_blob, &guid); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(1, ("Error: Invalid GUID on link target.\n")); - talloc_free(dn); - continue; - } - - guid_buf_str = GUID_buf_string(&guid, &buf_guid); - guid_search_str = talloc_asprintf(mem_ctx, "", guid_buf_str); - cleanup_val = data_blob_string_const(guid_search_str); - - talloc_free(dn); - - if (cleanup_msg == NULL) { - cleanup_msg = ldb_msg_new(mem_ctx); - if (cleanup_msg == NULL) { - return NT_STATUS_NO_MEMORY; - } - cleanup_msg->dn = res->msgs[i]->dn; - } - - ret = ldb_msg_add_value(cleanup_msg, element->name, &cleanup_val, &cleanup_elem); - if (ret != LDB_SUCCESS) { - return NT_STATUS_NO_MEMORY; - } - cleanup_elem->flags = LDB_FLAG_MOD_DELETE; - num_modified++; - } - } - - if (num_modified > 0) { - ret = dsdb_modify(samdb, cleanup_msg, DSDB_REPLMD_VANISH_LINKS); - if (ret != LDB_SUCCESS) { - DEBUG(1,(__location__ ": Failed to remove deleted object %s\n", - ldb_dn_get_linearized(res->msgs[i]->dn))); - } else { - DEBUG(4,("Removed deleted object %s\n", - ldb_dn_get_linearized(res->msgs[i]->dn))); - *num_links_removed = *num_links_removed + num_modified; - } - - } - } - TALLOC_FREE(tmp_ctx); - } return NT_STATUS_OK; -- cgit v1.2.1