ldb_tdb: Do not fail in GUID index mode if there is a duplicate attribute
authorGary Lockyer <gary@catalyst.net.nz>
Tue, 27 Feb 2018 22:47:22 +0000 (11:47 +1300)
committerStefan Metzmacher <metze@samba.org>
Wed, 2 May 2018 12:18:10 +0000 (14:18 +0200)
It is not the job of the index code to enforce this, but do give a
a warning given it has been detected.

However, now that we do allow it, we must never return the same
object twice to the caller, so filter for it in ltdb_index_filter().

The GUID list is sorted, which makes this cheap to handle, thankfully.

Signed-off-by: Gary Lockyer <gary@catalyst.net.nz>
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
BUG: https://bugzilla.samba.org/show_bug.cgi?id=13335

(cherry picked from commit 5c1504b94d1417894176811f18c5d450de22cfd2)

lib/ldb/ldb_tdb/ldb_index.c

index 99fef23662fe828a5534e94e58f61fbb70df4e10..ee2027319e33249ef0ceb5643302b665657824e7 100644 (file)
@@ -1526,6 +1526,7 @@ static int ltdb_index_filter(struct ltdb_private *ltdb,
        struct ldb_message *msg;
        struct ldb_message *filtered_msg;
        unsigned int i;
+       uint8_t previous_guid_key[LTDB_GUID_KEY_SIZE] = {};
 
        ldb = ldb_module_get_ctx(ac->module);
 
@@ -1538,11 +1539,6 @@ static int ltdb_index_filter(struct ltdb_private *ltdb,
                int ret;
                bool matched;
 
-               msg = ldb_msg_new(ac);
-               if (!msg) {
-                       return LDB_ERR_OPERATIONS_ERROR;
-               }
-
                ret = ltdb_idx_to_key(ac->module, ltdb,
                                      ac, &dn_list->dn[i],
                                      &tdb_key);
@@ -1550,6 +1546,33 @@ static int ltdb_index_filter(struct ltdb_private *ltdb,
                        return ret;
                }
 
+               if (ltdb->cache->GUID_index_attribute != NULL) {
+                       /*
+                        * If we are in GUID index mode, then the dn_list is
+                        * sorted.  If we got a duplicate, forget about it, as
+                        * otherwise we would send the same entry back more
+                        * than once.
+                        *
+                        * This is needed in the truncated DN case, or if a
+                        * duplicate was forced in via
+                        * LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK
+                        */
+
+                       if (memcmp(previous_guid_key, tdb_key.dptr,
+                                  sizeof(previous_guid_key)) == 0) {
+                               continue;
+                       }
+
+                       memcpy(previous_guid_key, tdb_key.dptr,
+                              sizeof(previous_guid_key));
+               }
+
+               msg = ldb_msg_new(ac);
+               if (!msg) {
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+
+
                ret = ltdb_search_key(ac->module, ltdb,
                                      tdb_key, msg,
                                      LDB_UNPACK_DATA_FLAG_NO_DATA_ALLOC|
@@ -1923,9 +1946,36 @@ static int ltdb_index_add1(struct ldb_module *module,
                BINARY_ARRAY_SEARCH_GTE(list->dn, list->count,
                                        *key_val, ldb_val_equal_exact_ordered,
                                        exact, next);
+
+               /*
+                * Give a warning rather than fail, this could be a
+                * duplicate value in the record allowed by a caller
+                * forcing in the value with
+                * LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK
+                */
                if (exact != NULL) {
-                       talloc_free(list);
-                       return LDB_ERR_OPERATIONS_ERROR;
+                       /* This can't fail, gives a default at worst */
+                       const struct ldb_schema_attribute *attr
+                               = ldb_schema_attribute_by_name(
+                                       ldb,
+                                       ltdb->cache->GUID_index_attribute);
+                       struct ldb_val v;
+                       ret = attr->syntax->ldif_write_fn(ldb, list,
+                                                         exact, &v);
+                       if (ret == LDB_SUCCESS) {
+                               ldb_debug(ldb, LDB_DEBUG_WARNING,
+                                         __location__
+                                         ": duplicate attribute value in %s "
+                                         "for index on %s, "
+                                         "duplicate of %s %*.*s in %s",
+                                         ldb_dn_get_linearized(msg->dn),
+                                         el->name,
+                                         ltdb->cache->GUID_index_attribute,
+                                         (int)v.length,
+                                         (int)v.length,
+                                         v.data,
+                                         ldb_dn_get_linearized(dn_key));
+                       }
                }
 
                if (next == NULL) {