TODO ... s4-dsdb: While replicating add isRecycled
[metze/samba/wip.git] / source4 / dsdb / samdb / ldb_modules / repl_meta_data.c
index b7a6a70b364fb7754d5c3e427e82be9af557e17a..1e039d9be539dbbbb16e52557c14df2a2aee752b 100644 (file)
@@ -87,6 +87,10 @@ struct replmd_replicated_request {
 
        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 {
@@ -1092,9 +1096,15 @@ static int replmd_update_rpmd_element(struct ldb_context *ldb,
                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
@@ -3002,9 +3012,15 @@ static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
        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"));
@@ -3526,6 +3542,59 @@ failed:
        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
  */
@@ -3538,6 +3607,9 @@ static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
        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;
 
        /*
@@ -3578,6 +3650,12 @@ static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
                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];
@@ -3590,6 +3668,51 @@ static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
                        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;
+               }
        }
 
        /*
@@ -3707,6 +3830,18 @@ static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
        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;
@@ -3714,6 +3849,16 @@ static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
        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) {
@@ -3740,6 +3885,58 @@ static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
                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;
@@ -3825,6 +4022,97 @@ static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
         */
        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