Date: Monday, May 28, 2018 @ 07:43:55 Author: tpowa Revision: 325068 upgpkg: samba 4.8.2-1
bump to latest version Modified: samba/trunk/PKGBUILD Deleted: samba/trunk/bug13335.patch ----------------+ PKGBUILD | 15 bug13335.patch | 2039 ------------------------------------------------------- 2 files changed, 4 insertions(+), 2050 deletions(-) Modified: PKGBUILD =================================================================== --- PKGBUILD 2018-05-28 07:28:16 UTC (rev 325067) +++ PKGBUILD 2018-05-28 07:43:55 UTC (rev 325068) @@ -10,7 +10,7 @@ pkgbase=samba pkgname=('libwbclient' 'smbclient' 'samba') -pkgver=4.8.1 +pkgver=4.8.2 pkgrel=1 arch=(x86_64) url="http://www.samba.org" @@ -23,16 +23,10 @@ http://us1.samba.org/samba/ftp/stable/${pkgbase}-${pkgver}.tar.asc samba.logrotate samba.pam - samba.conf - bug13335.patch) + samba.conf) validpgpkeys=('52FBC0B86D954B0843324CDC6F33915B6568B7EA') #Samba Distribution Verification Key <samba-b...@samba.org> ### UNINSTALL dmapi package before building!!! -prepare() { - cd samba-${pkgver} - patch -p1 -i ../bug13335.patch -} - build() { # Use samba-pkg as a staging directory for the split packages # (This is so RPATHS and symlinks are generated correctly via @@ -234,9 +228,8 @@ # copy ldap example install -D -m644 ${srcdir}/samba-${pkgver}/examples/LDAP/samba.schema ${pkgdir}/usr/share/doc/samba/examples/LDAP/samba.schema } -md5sums=('3cdb976a892bc036bfb61eeb97f68450' +md5sums=('417c065455f8948d1de2be4edd074390' 'SKIP' '995621522c6ec9b68c1b858ceed627ed' '96f82c38f3f540b53f3e5144900acf17' - '49abd7b719e3713a3f75a8a50958e381' - '86db2a3247a79d195341759da4c27454') + '49abd7b719e3713a3f75a8a50958e381') Deleted: bug13335.patch =================================================================== --- bug13335.patch 2018-05-28 07:28:16 UTC (rev 325067) +++ bug13335.patch 2018-05-28 07:43:55 UTC (rev 325068) @@ -1,2039 +0,0 @@ -From b26d21cebda58547818e24927131a3c62955bd9c Mon Sep 17 00:00:00 2001 -From: Gary Lockyer <g...@catalyst.net.nz> -Date: Wed, 21 Feb 2018 15:12:40 +1300 -Subject: [PATCH 1/5] ldb_tdb: Add tests for truncated index keys - -Tests for the index truncation code as well as the GUID index -format in general. - -Covers truncation of both the DN and equality search keys. - -Signed-off-by: Gary Lockyer <g...@catalyst.net.nz> -Reviewed-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> -Reviewed-by: Andrew Bartlett <abart...@samba.org> - -Autobuild-User(master): Andrew Bartlett <abart...@samba.org> -Autobuild-Date(master): Sat Mar 3 09:58:40 CET 2018 on sn-devel-144 - -BUG: https://bugzilla.samba.org/show_bug.cgi?id=13335 - -(cherry picked into 4.8 and cut down to operate without truncated -index values from master commit 4c0c888b571d4c21ab267024178353925a8c087c -by Andrew Bartlett) ---- - lib/ldb/tests/python/index.py | 1007 +++++++++++++++++++++++++++++++++++++++++ - lib/ldb/wscript | 2 +- - 2 files changed, 1008 insertions(+), 1 deletion(-) - create mode 100755 lib/ldb/tests/python/index.py - -diff --git a/lib/ldb/tests/python/index.py b/lib/ldb/tests/python/index.py -new file mode 100755 -index 00000000000..cd3735b5625 ---- /dev/null -+++ b/lib/ldb/tests/python/index.py -@@ -0,0 +1,1007 @@ -+#!/usr/bin/env python -+# -+# Tests for truncated index keys -+# -+# Copyright (C) Andrew Bartlett <abart...@samba.org> 2018 -+# -+# This program is free software; you can redistribute it and/or modify -+# it under the terms of the GNU General Public License as published by -+# the Free Software Foundation; either version 3 of the License, or -+# (at your option) any later version. -+# -+# This program is distributed in the hope that it will be useful, -+# but WITHOUT ANY WARRANTY; without even the implied warranty of -+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+# GNU General Public License for more details. -+# -+# You should have received a copy of the GNU General Public License -+# along with this program. If not, see <http://www.gnu.org/licenses/>. -+# -+"""Tests for index keys -+ -+This is a modified version of the test from master for databases such -+as lmdb have a maximum key length, instead just checking that the -+GUID index code still operates correctly. -+ -+Many of the test names are therefore incorrect, but are retained -+to keep the code easy to backport into if more tested are added in -+master. -+ -+""" -+ -+import os -+from unittest import TestCase -+import sys -+import ldb -+import shutil -+ -+PY3 = sys.version_info > (3, 0) -+ -+ -+def tempdir(): -+ import tempfile -+ try: -+ dir_prefix = os.path.join(os.environ["SELFTEST_PREFIX"], "tmp") -+ except KeyError: -+ dir_prefix = None -+ return tempfile.mkdtemp(dir=dir_prefix) -+ -+ -+def contains(result, dn): -+ if result is None: -+ return False -+ -+ for r in result: -+ if str(r["dn"]) == dn: -+ return True -+ return False -+ -+ -+class MaxIndexKeyLengthTests(TestCase): -+ def checkGuids(self, key, guids): -+ # -+ # This check relies on the current implementation where the indexes -+ # are in the same database as the data. -+ # -+ # It checks that the index record exists, unless guids is None then -+ # the record must not exist. And the it contains the expected guid -+ # entries. -+ # -+ # The caller needs to provide the GUID's in the expected order -+ # -+ res = self.l.search( -+ base=key, -+ scope=ldb.SCOPE_BASE) -+ if guids is None: -+ self.assertEqual(len(res), 0) -+ return -+ self.assertEqual(len(res), 1) -+ -+ # The GUID index format has only one value -+ index = res[0]["@IDX"][0] -+ self.assertEqual(len(guids), len(index)) -+ self.assertEqual(guids, index) -+ -+ def tearDown(self): -+ shutil.rmtree(self.testdir) -+ super(MaxIndexKeyLengthTests, self).tearDown() -+ -+ # Ensure the LDB is closed now, so we close the FD -+ del(self.l) -+ -+ def setUp(self): -+ super(MaxIndexKeyLengthTests, self).setUp() -+ self.testdir = tempdir() -+ self.filename = os.path.join(self.testdir, "key_len_test.ldb") -+ # Note that the maximum key length is set to 50 -+ self.l = ldb.Ldb(self.filename, -+ options=[ -+ "modules:rdn_name", -+ "max_key_len_for_self_test:50"]) -+ self.l.add({"dn": "@ATTRIBUTES", -+ "uniqueThing": "UNIQUE_INDEX"}) -+ self.l.add({"dn": "@INDEXLIST", -+ "@IDXATTR": [b"uniqueThing", b"notUnique"], -+ "@IDXONE": [b"1"], -+ "@IDXGUID": [b"objectUUID"], -+ "@IDX_DN_GUID": [b"GUID"]}) -+ -+ # Test that DN's longer the maximum key length can be added -+ # and that duplicate DN's are rejected correctly -+ def test_add_long_dn_add(self): -+ # -+ # For all entries the DN index key gets truncated to -+ # @INDEX:@IDXDN:OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA -+ # -+ self.l.add({"dn": "OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA,DC=ORG", -+ "objectUUID": b"0123456789abcdef"}) -+ -+ self.l.add({"dn": "OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA,DC=COM", -+ "objectUUID": b"0123456789abcde0"}) -+ -+ self.l.add({"dn": "OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA,DC=GOV", -+ "objectUUID": b"0123456789abcde1"}) -+ -+ # Key is equal to max length does not get inserted into the truncated -+ # key namespace -+ self.l.add({"dn": "OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA", -+ "objectUUID": b"0123456789abcde5"}) -+ self.checkGuids( -+ "@INDEX:@IDXDN:OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA", -+ b"0123456789abcde5") -+ -+ # This key should not get truncated, as it's one character less than -+ # max, and will not be in the truncate name space -+ self.l.add({"dn": "OU=A_LONG_DNXXXXXXXXXXXXXX,DC=SAMBA", -+ "objectUUID": b"0123456789abcde7"}) -+ self.checkGuids( -+ "@INDEX:@IDXDN:OU=A_LONG_DNXXXXXXXXXXXXXX,DC=SAMBA", -+ b"0123456789abcde7") -+ -+ try: -+ self.l.add({"dn": "OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA,DC=ORG", -+ "objectUUID": b"0123456789abcde2"}) -+ except ldb.LdbError as err: -+ enum = err.args[0] -+ self.assertEqual(enum, ldb.ERR_ENTRY_ALREADY_EXISTS) -+ -+ try: -+ self.l.add({"dn": "OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA,DC=COM", -+ "objectUUID": b"0123456789abcde3"}) -+ except ldb.LdbError as err: -+ enum = err.args[0] -+ self.assertEqual(enum, ldb.ERR_ENTRY_ALREADY_EXISTS) -+ -+ try: -+ self.l.add({"dn": "OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA,DC=GOV", -+ "objectUUID": b"0123456789abcde4"}) -+ except ldb.LdbError as err: -+ enum = err.args[0] -+ self.assertEqual(enum, ldb.ERR_ENTRY_ALREADY_EXISTS) -+ -+ try: -+ self.l.add({"dn": "OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA", -+ "objectUUID": b"0123456789abcde6"}) -+ except ldb.LdbError as err: -+ enum = err.args[0] -+ self.assertEqual(enum, ldb.ERR_ENTRY_ALREADY_EXISTS) -+ -+ try: -+ self.l.add({"dn": "OU=A_LONG_DNXXXXXXXXXX,DC=SAMBA", -+ "objectUUID": b"0123456789abcde8"}) -+ except ldb.LdbError as err: -+ enum = err.args[0] -+ self.assertEqual(enum, ldb.ERR_ENTRY_ALREADY_EXISTS) -+ -+ def test_rename_truncated_dn_keys(self): -+ # For all entries the DN index key gets truncated to -+ # 0 1 2 3 4 5 -+ # 12345678901234567890123456789012345678901234567890 -+ # @INDEX:@IDXDN:OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA -+ self.l.add({"dn": "OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA,DC=ORG", -+ "objectUUID": b"0123456789abcdef"}) -+ -+ self.l.add({"dn": "OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA,DC=COM", -+ "objectUUID": b"0123456789abcde0"}) -+ -+ # Non conflicting rename, should succeed -+ self.l.rename("OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA,DC=ORG", -+ "OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA,DC=GOV") -+ -+ # Conflicting rename should fail -+ try: -+ self.l.rename("OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA,DC=COM", -+ "OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA,DC=GOV") -+ except ldb.LdbError as err: -+ enum = err.args[0] -+ self.assertEqual(enum, ldb.ERR_ENTRY_ALREADY_EXISTS) -+ -+ def test_delete_truncated_dn_keys(self): -+ # -+ # For all entries the DN index key gets truncated to -+ # 0 1 2 3 4 5 -+ # 12345678901234567890123456789012345678901234567890 -+ # @INDEX:@IDXDN:OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA -+ # -+ self.l.add({"dn": "OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA,DC=ORG", -+ "objectUUID": b"0123456789abcdef"}) -+ -+ self.l.add({"dn": "OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA,DC=GOV", -+ "objectUUID": b"0123456789abcde1"}) -+ -+ self.l.add({"dn": "OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA", -+ "objectUUID": b"0123456789abcde5"}) -+ self.checkGuids( -+ "@INDEX:@IDXDN:OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA", -+ b"0123456789abcde5") -+ -+ # Try to delete a non existent DN with a truncated key -+ try: -+ self.l.delete("OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA,DC=COM") -+ except ldb.LdbError as err: -+ enum = err.args[0] -+ self.assertEqual(enum, ldb.ERR_NO_SUCH_OBJECT) -+ # Ensure that non of the other truncated DN's got deleted -+ res = self.l.search( -+ base="OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA,DC=ORG") -+ self.assertEqual(len(res), 1) -+ -+ res = self.l.search( -+ base="OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA,DC=GOV") -+ self.assertEqual(len(res), 1) -+ -+ # Ensure that the non truncated DN did not get deleted -+ res = self.l.search( -+ base="OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA") -+ self.assertEqual(len(res), 1) -+ -+ # Check the indexes are correct -+ self.checkGuids( -+ "@INDEX:@IDXDN:OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA", -+ b"0123456789abcde5") -+ -+ # delete an existing entry -+ self.l.delete("OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA,DC=ORG") -+ -+ # Ensure it got deleted -+ res = self.l.search(base="OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA,DC=ORG") -+ self.assertEqual(len(res), 0) -+ -+ # Ensure that non of the other truncated DN's got deleted -+ res = self.l.search(base="OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA,DC=GOV") -+ self.assertEqual(len(res), 1) -+ -+ # Ensure the non truncated entry did not get deleted. -+ res = self.l.search(base="OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA") -+ self.assertEqual(len(res), 1) -+ -+ # Check the indexes are correct -+ self.checkGuids( -+ "@INDEX:@IDXDN:OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA", -+ b"0123456789abcde5") -+ -+ # delete an existing entry -+ self.l.delete("OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA,DC=GOV") -+ -+ # Ensure it got deleted -+ res = self.l.search(base="OU=A_LONG_DNXXXXXXXXXXX,DC=SAMBA,DC=GOV") -+ self.assertEqual(len(res), 0) -+ -+ # Ensure that non of the non truncated DN's got deleted -+ res = self.l.search(base="OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA") -+ self.assertEqual(len(res), 1) -+ # Check the indexes are correct -+ self.checkGuids( -+ "@INDEX:@IDXDN:OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA", -+ b"0123456789abcde5") -+ -+ # delete an existing entry -+ self.l.delete("OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA") -+ -+ # Ensure it got deleted -+ res = self.l.search(base="OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBAxxx") -+ self.assertEqual(len(res), 0) -+ self.checkGuids( -+ "@INDEX:@IDXDN:OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA", -+ None) -+ -+ def test_search_truncated_dn_keys(self): -+ # -+ # For all entries the DN index key gets truncated to -+ # 0 1 2 3 4 5 -+ # 12345678901234567890123456789012345678901234567890 -+ # @INDEX:@IDXDN:OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA -+ # -+ self.l.add({"dn": "OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA,DC=ORG", -+ "objectUUID": b"0123456789abcdef"}) -+ -+ self.l.add({"dn": "OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA,DC=GOV", -+ "objectUUID": b"0123456789abcde1"}) -+ -+ self.l.add({"dn": "OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA", -+ "objectUUID": b"0123456789abcde5"}) -+ self.checkGuids( -+ "@INDEX:@IDXDN:OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA", -+ b"0123456789abcde5") -+ -+ res = self.l.search(base="OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA,DC=ORG") -+ self.assertEqual(len(res), 1) -+ -+ res = self.l.search(base="OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA,DC=GOV") -+ self.assertEqual(len(res), 1) -+ -+ res = self.l.search(base="OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA") -+ self.assertEqual(len(res), 1) -+ -+ res = self.l.search(base="OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA,DC=COM") -+ self.assertEqual(len(res), 0) -+ -+ res = self.l.search(base="OU=A_LONG_DNXXXXXXXXXXXX,DC=SAMBA,DC=GOV") -+ self.assertEqual(len(res), 0) -+ -+ # Non existent, key one less than truncation limit -+ res = self.l.search(base="OU=A_LONG_DNXXXXXXXXXXXXXX,DC=SAMBA") -+ self.assertEqual(len(res), 0) -+ -+ def test_search_dn_filter_truncated_dn_keys(self): -+ # -+ # For all entries the DN index key gets truncated to -+ # 0 1 2 3 4 5 -+ # 12345678901234567890123456789012345678901234567890 -+ # @INDEX:@IDXDN:OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA -+ # -+ self.l.add({"dn": "OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA,DC=ORG", -+ "objectUUID": b"0123456789abcdef"}) -+ -+ self.l.add({"dn": "OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA,DC=GOV", -+ "objectUUID": b"0123456789abcde1"}) -+ -+ self.l.add({"dn": "OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA", -+ "objectUUID": b"0123456789abcde5"}) -+ self.checkGuids( -+ "@INDEX:@IDXDN:OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA", -+ b"0123456789abcde5") -+ -+ res = self.l.search( -+ expression="dn=OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA,DC=ORG") -+ self.assertEqual(len(res), 1) -+ -+ res = self.l.search( -+ expression="dn=OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA,DC=GOV") -+ self.assertEqual(len(res), 1) -+ -+ res = self.l.search( -+ expression="dn=OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA") -+ self.assertEqual(len(res), 1) -+ -+ res = self.l.search( -+ expression="dn=OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA,DC=COM") -+ self.assertEqual(len(res), 0) -+ -+ res = self.l.search( -+ expression="dn=OU=A_LONG_DNXXXXXXXXXXXX,DC=SAMBA,DC=GOV") -+ self.assertEqual(len(res), 0) -+ -+ # Non existent, key one less than truncation limit -+ res = self.l.search( -+ expression="dn=OU=A_LONG_DNXXXXXXXXXXXXXX,DC=SAMBA") -+ self.assertEqual(len(res), 0) -+ -+ def test_search_one_level_truncated_dn_keys(self): -+ # -+ # Except for the base DN's -+ # all entries the DN index key gets truncated to -+ # 0 1 2 3 4 5 -+ # 12345678901234567890123456789012345678901234567890 -+ # @INDEX:@IDXDN:OU=??,OU=A_LONG_DN_ONE_LVLX,DC=SAMBA -+ # The base DN-s truncate to -+ # @INDEX:@IDXDN:OU=A_LONG_DN_ONE_LVLX,DC=SAMBA,DC=OR -+ # -+ self.l.add({"dn": "OU=A_LONG_DN_ONE_LVLX,DC=SAMBA,DC=OR1", -+ "objectUUID": b"0123456789abcdef"}) -+ self.l.add({"dn": "OU=A_LONG_DN_ONE_LVLX,DC=SAMBA,DC=OR2", -+ "objectUUID": b"0123456789abcd1f"}) -+ -+ self.l.add({"dn": "OU=01,OU=A_LONG_DN_ONE_LVLX,DC=SAMBA,DC=OR1", -+ "objectUUID": b"0123456789abcde1"}) -+ self.l.add({"dn": "OU=01,OU=A_LONG_DN_ONE_LVLX,DC=SAMBA,DC=OR2", -+ "objectUUID": b"0123456789abcd11"}) -+ -+ self.l.add({"dn": "OU=02,OU=A_LONG_DN_ONE_LVLX,DC=SAMBA,DC=OR1", -+ "objectUUID": b"0123456789abcde2"}) -+ self.l.add({"dn": "OU=02,OU=A_LONG_DN_ONE_LVLX,DC=SAMBA,DC=OR2", -+ "objectUUID": b"0123456789abcdf2"}) -+ -+ self.l.add({"dn": "OU=03,OU=A_LONG_DN_ONE_LVLX,DC=SAMBA,DC=OR1", -+ "objectUUID": b"0123456789abcde3"}) -+ self.l.add({"dn": "OU=03,OU=A_LONG_DN_ONE_LVLX,DC=SAMBA,DC=OR2", -+ "objectUUID": b"0123456789abcd13"}) -+ -+ # This key is not truncated as it's the max_key_len -+ self.l.add({"dn": "OU=01,OU=A_LONG_DN_ONE_LVLX,DC=SAMBA", -+ "objectUUID": b"0123456789abcde7"}) -+ self.checkGuids( -+ "@INDEX:@IDXDN:OU=01,OU=A_LONG_DN_ONE_LVLX,DC=SAMBA", -+ b"0123456789abcde7") -+ -+ res = self.l.search(base="OU=A_LONG_DN_ONE_LVLX,DC=SAMBA,DC=OR1", -+ scope=ldb.SCOPE_ONELEVEL) -+ self.assertEqual(len(res), 3) -+ self.assertTrue( -+ contains(res, "OU=01,OU=A_LONG_DN_ONE_LVLX,DC=SAMBA,DC=OR1")) -+ self.assertTrue( -+ contains(res, "OU=02,OU=A_LONG_DN_ONE_LVLX,DC=SAMBA,DC=OR1")) -+ self.assertTrue( -+ contains(res, "OU=03,OU=A_LONG_DN_ONE_LVLX,DC=SAMBA,DC=OR1")) -+ -+ res = self.l.search(base="OU=A_LONG_DN_ONE_LVLX,DC=SAMBA,DC=OR2", -+ scope=ldb.SCOPE_ONELEVEL) -+ self.assertEqual(len(res), 3) -+ self.assertTrue( -+ contains(res, "OU=01,OU=A_LONG_DN_ONE_LVLX,DC=SAMBA,DC=OR2")) -+ self.assertTrue( -+ contains(res, "OU=02,OU=A_LONG_DN_ONE_LVLX,DC=SAMBA,DC=OR2")) -+ self.assertTrue( -+ contains(res, "OU=03,OU=A_LONG_DN_ONE_LVLX,DC=SAMBA,DC=OR2")) -+ -+ res = self.l.search(base="OU=A_LONG_DN_ONE_LVLX,DC=SAMBA", -+ scope=ldb.SCOPE_ONELEVEL) -+ self.assertEqual(len(res), 1) -+ self.assertTrue( -+ contains(res, "OU=01,OU=A_LONG_DN_ONE_LVLX,DC=SAMBA")) -+ -+ def test_search_sub_tree_truncated_dn_keys(self): -+ # -+ # Except for the base DN's -+ # all entries the DN index key gets truncated to -+ # 0 1 2 3 4 5 -+ # 12345678901234567890123456789012345678901234567890 -+ # @INDEX:@IDXDN:OU=??,OU=A_LONG_DN_SUB_TREE,DC=SAMBA -+ # The base DN-s truncate to -+ # @INDEX:@IDXDN:OU=A_LONG_DN_SUB_TREE,DC=SAMBA,DC=OR -+ # -+ self.l.add({"dn": "OU=A_LONG_DN_SUB_TREE,DC=SAMBA,DC=OR1", -+ "objectUUID": b"0123456789abcdef"}) -+ self.l.add({"dn": "OU=A_LONG_DN_SUB_TREE,DC=SAMBA,DC=OR2", -+ "objectUUID": b"0123456789abcde4"}) -+ self.l.add({"dn": "OU=A_LONG_DN_SUB_TREE,DC=SAMBA,DC=OR3", -+ "objectUUID": b"0123456789abcde8"}) -+ -+ self.l.add({"dn": "OU=01,OU=A_LONG_DN_SUB_TREE,DC=SAMBA,DC=OR1", -+ "objectUUID": b"0123456789abcde1"}) -+ self.l.add({"dn": "OU=01,OU=A_LONG_DN_SUB_TREE,DC=SAMBA,DC=OR2", -+ "objectUUID": b"0123456789abcde5"}) -+ -+ self.l.add({"dn": "OU=02,OU=A_LONG_DN_SUB_TREE,DC=SAMBA,DC=OR1", -+ "objectUUID": b"0123456789abcde2"}) -+ self.l.add({"dn": "OU=02,OU=A_LONG_DN_SUB_TREE,DC=SAMBA,DC=OR2", -+ "objectUUID": b"0123456789abcde6"}) -+ -+ self.l.add({"dn": "OU=03,OU=A_LONG_DN_SUB_TREE,DC=SAMBA,DC=OR1", -+ "objectUUID": b"0123456789abcde3"}) -+ -+ self.l.add({"dn": "OU=03,OU=A_LONG_DN_SUB_TREE,DC=SAMBA,DC=OR2", -+ "objectUUID": b"0123456789abcde7"}) -+ -+ self.l.add({"dn": "OU=04,OU=A_LONG_DN_SUB_TREE,DC=SAMBA,DC=OR4", -+ "objectUUID": b"0123456789abcde9"}) -+ -+ res = self.l.search(base="OU=A_LONG_DN_SUB_TREE,DC=SAMBA,DC=OR1", -+ scope=ldb.SCOPE_SUBTREE) -+ self.assertEqual(len(res), 4) -+ self.assertTrue( -+ contains(res, "OU=A_LONG_DN_SUB_TREE,DC=SAMBA,DC=OR1")) -+ self.assertTrue( -+ contains(res, "OU=01,OU=A_LONG_DN_SUB_TREE,DC=SAMBA,DC=OR1")) -+ self.assertTrue( -+ contains(res, "OU=02,OU=A_LONG_DN_SUB_TREE,DC=SAMBA,DC=OR1")) -+ self.assertTrue( -+ contains(res, "OU=03,OU=A_LONG_DN_SUB_TREE,DC=SAMBA,DC=OR1")) -+ -+ res = self.l.search(base="OU=A_LONG_DN_SUB_TREE,DC=SAMBA,DC=OR2", -+ scope=ldb.SCOPE_SUBTREE) -+ self.assertEqual(len(res), 4) -+ self.assertTrue( -+ contains(res, "OU=A_LONG_DN_SUB_TREE,DC=SAMBA,DC=OR2")) -+ self.assertTrue( -+ contains(res, "OU=01,OU=A_LONG_DN_SUB_TREE,DC=SAMBA,DC=OR2")) -+ self.assertTrue( -+ contains(res, "OU=02,OU=A_LONG_DN_SUB_TREE,DC=SAMBA,DC=OR2")) -+ self.assertTrue( -+ contains(res, "OU=03,OU=A_LONG_DN_SUB_TREE,DC=SAMBA,DC=OR2")) -+ -+ res = self.l.search(base="OU=A_LONG_DN_SUB_TREE,DC=SAMBA,DC=OR3", -+ scope=ldb.SCOPE_SUBTREE) -+ self.assertEqual(len(res), 1) -+ self.assertTrue( -+ contains(res, "OU=A_LONG_DN_SUB_TREE,DC=SAMBA,DC=OR3")) -+ -+ res = self.l.search(base="OU=A_LONG_DN_SUB_TREE,DC=SAMBA,DC=OR4", -+ scope=ldb.SCOPE_SUBTREE) -+ self.assertEqual(len(res), 1) -+ self.assertTrue( -+ contains(res, "OU=04,OU=A_LONG_DN_SUB_TREE,DC=SAMBA,DC=OR4")) -+ -+ def test_search_base_truncated_dn_keys(self): -+ # -+ # For all entries the DN index key gets truncated to -+ # 0 1 2 3 4 5 -+ # 12345678901234567890123456789012345678901234567890 -+ # @INDEX:@IDXDN:OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA -+ # -+ self.l.add({"dn": "OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA,DC=ORG", -+ "objectUUID": b"0123456789abcdef"}) -+ -+ self.l.add({"dn": "OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA,DC=GOV", -+ "objectUUID": b"0123456789abcde1"}) -+ -+ self.l.add({"dn": "OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA", -+ "objectUUID": b"0123456789abcde5"}) -+ self.checkGuids( -+ "@INDEX:@IDXDN:OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA", -+ b"0123456789abcde5") -+ -+ res = self.l.search( -+ base="OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA,DC=ORG", -+ scope=ldb.SCOPE_BASE) -+ self.assertEqual(len(res), 1) -+ -+ res = self.l.search( -+ base="OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA,DC=GOV", -+ scope=ldb.SCOPE_BASE) -+ self.assertEqual(len(res), 1) -+ -+ res = self.l.search( -+ base="OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA", -+ scope=ldb.SCOPE_BASE) -+ self.assertEqual(len(res), 1) -+ -+ res = self.l.search( -+ base="OU=A_LONG_DNXXXXXXXXXXXXXXX,DC=SAMBA,DC=COM", -+ scope=ldb.SCOPE_BASE) -+ self.assertEqual(len(res), 0) -+ -+ res = self.l.search( -+ base="OU=A_LONG_DNXXXXXXXXXXXX,DC=SAMBA,DC=GOV", -+ scope=ldb.SCOPE_BASE) -+ self.assertEqual(len(res), 0) -+ -+ # Non existent, key one less than truncation limit -+ res = self.l.search( -+ base="OU=A_LONG_DNXXXXXXXXXXXXXX,DC=SAMBA", -+ scope=ldb.SCOPE_BASE) -+ self.assertEqual(len(res), 0) -+ -+ # -+ # Test non unique index searched with truncated keys -+ # -+ def test_index_truncated_keys(self): -+ # 0 1 2 3 4 5 -+ # 12345678901234567890123456789012345678901234567890 -+ # @INDEX:NOTUNIQUE:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -+ -+ eq_max = b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" -+ gt_max = b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" -+ lt_max = b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" -+ # > than max length and differs in values that will be truncated -+ gt_max_b = b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab" -+ -+ # Add two entries with the same value, key length = max so no -+ # truncation. -+ self.l.add({"dn": "OU=01,OU=SEARCH_NON_UNIQUE,DC=SAMBA,DC=ORG", -+ "notUnique": eq_max, -+ "objectUUID": b"0123456789abcde0"}) -+ self.checkGuids( -+ "@INDEX:NOTUNIQUE:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", -+ b"0123456789abcde0") -+ -+ self.l.add({"dn": "OU=02,OU=SEARCH_NON_UNIQUE,DC=SAMBA,DC=ORG", -+ "notUnique": eq_max, -+ "objectUUID": b"0123456789abcde1"}) -+ self.checkGuids( -+ "@INDEX:NOTUNIQUE:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", -+ b"0123456789abcde0" + b"0123456789abcde1") -+ -+ # -+ # An entry outside the tree -+ # -+ self.l.add({"dn": "OU=10,OU=SEARCH_NON_UNIQUE01,DC=SAMBA,DC=ORG", -+ "notUnique": eq_max, -+ "objectUUID": b"0123456789abcd11"}) -+ self.checkGuids( -+ "@INDEX:NOTUNIQUE:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", -+ b"0123456789abcd11" + b"0123456789abcde0" + b"0123456789abcde1") -+ -+ # Key longer than max so should get truncated to same key as -+ # the previous two entries -+ self.l.add({"dn": "OU=03,OU=SEARCH_NON_UNIQUE,DC=SAMBA,DC=ORG", -+ "notUnique": gt_max, -+ "objectUUID": b"0123456789abcde2"}) -+ -+ # Key longer than max so should get truncated to same key as -+ # the previous entries but differs in the chars after max length -+ self.l.add({"dn": "OU=23,OU=SEARCH_NON_UNIQUE,DC=SAMBA,DC=ORG", -+ "notUnique": gt_max_b, -+ "objectUUID": b"0123456789abcd22"}) -+ # -+ # An entry outside the tree -+ # -+ self.l.add({"dn": "OU=11,OU=SEARCH_NON_UNIQUE01,DC=SAMBA,DC=ORG", -+ "notUnique": gt_max, -+ "objectUUID": b"0123456789abcd12"}) -+ -+ # Key shorter than max -+ # -+ self.l.add({"dn": "OU=04,OU=SEARCH_NON_UNIQUE,DC=SAMBA,DC=ORG", -+ "notUnique": lt_max, -+ "objectUUID": b"0123456789abcde3"}) -+ self.checkGuids( -+ "@INDEX:NOTUNIQUE:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", -+ b"0123456789abcde3") -+ # -+ # An entry outside the tree -+ # -+ self.l.add({"dn": "OU=12,OU=SEARCH_NON_UNIQUE01,DC=SAMBA,DC=ORG", -+ "notUnique": lt_max, -+ "objectUUID": b"0123456789abcd13"}) -+ self.checkGuids( -+ "@INDEX:NOTUNIQUE:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", -+ b"0123456789abcd13" + b"0123456789abcde3") -+ -+ # -+ # search for target is max value not truncated -+ # should return ou's 01, 02 -+ # -+ expression = "(notUnique=" + eq_max.decode('ascii') + ")" -+ res = self.l.search(base="OU=SEARCH_NON_UNIQUE,DC=SAMBA,DC=ORG", -+ scope=ldb.SCOPE_ONELEVEL, -+ expression=expression) -+ self.assertEqual(len(res), 2) -+ self.assertTrue( -+ contains(res, "OU=01,OU=SEARCH_NON_UNIQUE,DC=SAMBA,DC=ORG")) -+ self.assertTrue( -+ contains(res, "OU=02,OU=SEARCH_NON_UNIQUE,DC=SAMBA,DC=ORG")) -+ # -+ # search for target is max value not truncated -+ # search one level up the tree, scope is ONE_LEVEL -+ # So should get no matches -+ # -+ expression = "(notUnique=" + eq_max.decode('ascii') + ")" -+ res = self.l.search(base="DC=SAMBA,DC=ORG", -+ scope=ldb.SCOPE_ONELEVEL, -+ expression=expression) -+ self.assertEqual(len(res), 0) -+ # -+ # search for target is max value not truncated -+ # search one level up the tree, scope is SUBTREE -+ # So should get 3 matches -+ # -+ res = self.l.search(base="DC=SAMBA,DC=ORG", -+ scope=ldb.SCOPE_SUBTREE, -+ expression=expression) -+ self.assertEqual(len(res), 3) -+ self.assertTrue( -+ contains(res, "OU=01,OU=SEARCH_NON_UNIQUE,DC=SAMBA,DC=ORG")) -+ self.assertTrue( -+ contains(res, "OU=02,OU=SEARCH_NON_UNIQUE,DC=SAMBA,DC=ORG")) -+ self.assertTrue( -+ contains(res, "OU=10,OU=SEARCH_NON_UNIQUE01,DC=SAMBA,DC=ORG")) -+ # -+ # search for target is max value + 1 so truncated -+ # should return ou 23 as it's gt_max_b being searched for -+ # -+ expression = "(notUnique=" + gt_max_b.decode('ascii') + ")" -+ res = self.l.search(base="OU=SEARCH_NON_UNIQUE,DC=SAMBA,DC=ORG", -+ scope=ldb.SCOPE_ONELEVEL, -+ expression=expression) -+ self.assertEqual(len(res), 1) -+ self.assertTrue( -+ contains(res, "OU=23,OU=SEARCH_NON_UNIQUE,DC=SAMBA,DC=ORG")) -+ -+ # -+ # search for target is max value + 1 so truncated -+ # should return ou 03 as it's gt_max being searched for -+ # -+ expression = "(notUnique=" + gt_max.decode('ascii') + ")" -+ res = self.l.search(base="OU=SEARCH_NON_UNIQUE,DC=SAMBA,DC=ORG", -+ scope=ldb.SCOPE_ONELEVEL, -+ expression=expression) -+ self.assertEqual(len(res), 1) -+ self.assertTrue( -+ contains(res, "OU=03,OU=SEARCH_NON_UNIQUE,DC=SAMBA,DC=ORG")) -+ -+ # -+ # scope one level and one level up one level up should get no matches -+ # -+ res = self.l.search(base="DC=SAMBA,DC=ORG", -+ scope=ldb.SCOPE_ONELEVEL, -+ expression=expression) -+ self.assertEqual(len(res), 0) -+ # -+ # scope sub tree and one level up one level up should get 2 matches -+ # -+ res = self.l.search(base="DC=SAMBA,DC=ORG", -+ scope=ldb.SCOPE_SUBTREE, -+ expression=expression) -+ self.assertEqual(len(res), 2) -+ self.assertTrue( -+ contains(res, "OU=03,OU=SEARCH_NON_UNIQUE,DC=SAMBA,DC=ORG")) -+ self.assertTrue( -+ contains(res, "OU=11,OU=SEARCH_NON_UNIQUE01,DC=SAMBA,DC=ORG")) -+ -+ # -+ # search for target is max value - 1 so not truncated -+ # should return ou 04 -+ # -+ expression = "(notUnique=" + lt_max.decode('ascii') + ")" -+ res = self.l.search(base="OU=SEARCH_NON_UNIQUE,DC=SAMBA,DC=ORG", -+ scope=ldb.SCOPE_ONELEVEL, -+ expression=expression) -+ self.assertEqual(len(res), 1) -+ self.assertTrue( -+ contains(res, "OU=04,OU=SEARCH_NON_UNIQUE,DC=SAMBA,DC=ORG")) -+ -+ # -+ # scope one level and one level up one level up should get no matches -+ # -+ res = self.l.search(base="DC=SAMBA,DC=ORG", -+ scope=ldb.SCOPE_ONELEVEL, -+ expression=expression) -+ self.assertEqual(len(res), 0) -+ -+ # -+ # scope sub tree and one level up one level up should get 2 matches -+ # -+ res = self.l.search(base="DC=SAMBA,DC=ORG", -+ scope=ldb.SCOPE_SUBTREE, -+ expression=expression) -+ self.assertEqual(len(res), 2) -+ self.assertTrue( -+ contains(res, "OU=04,OU=SEARCH_NON_UNIQUE,DC=SAMBA,DC=ORG")) -+ self.assertTrue( -+ contains(res, "OU=12,OU=SEARCH_NON_UNIQUE01,DC=SAMBA,DC=ORG")) -+ -+ # -+ # Test adding to non unique index with identical multivalued index -+ # attributes -+ # -+ def test_index_multi_valued_identical_keys(self): -+ # 0 1 2 3 4 5 -+ # 12345678901234567890123456789012345678901234567890 -+ # @INDEX:NOTUNIQUE:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -+ as_eq_max = b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" -+ bs_eq_max = b"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" -+ -+ try: -+ self.l.add({"dn": "OU=01,OU=SEARCH_NON_UNIQUE,DC=SAMBA,DC=ORG", -+ "notUnique": [bs_eq_max, as_eq_max, as_eq_max], -+ "objectUUID": b"0123456789abcde0"}) -+ self.fail("Exception not thrown") -+ except ldb.LdbError as e: -+ code = e.args[0] -+ self.assertEqual(ldb.ERR_ATTRIBUTE_OR_VALUE_EXISTS, code) -+ -+ # -+ # Test non unique index with multivalued index attributes -+ # searched with non truncated keys -+ # -+ def test_search_index_multi_valued_truncated_keys(self): -+ # 0 1 2 3 4 5 -+ # 12345678901234567890123456789012345678901234567890 -+ # @INDEX:NOTUNIQUE:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -+ -+ aa_gt_max = b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" -+ ab_gt_max = b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab" -+ bb_gt_max = b"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" -+ -+ self.l.add({"dn": "OU=01,OU=SEARCH_NON_UNIQUE,DC=SAMBA,DC=ORG", -+ "notUnique": [aa_gt_max, ab_gt_max, bb_gt_max], -+ "objectUUID": b"0123456789abcde0"}) -+ -+ expression = "(notUnique=" + aa_gt_max.decode('ascii') + ")" -+ res = self.l.search(base="OU=SEARCH_NON_UNIQUE,DC=SAMBA,DC=ORG", -+ scope=ldb.SCOPE_ONELEVEL, -+ expression=expression) -+ self.assertEqual(len(res), 1) -+ self.assertTrue( -+ contains(res, "OU=01,OU=SEARCH_NON_UNIQUE,DC=SAMBA,DC=ORG")) -+ -+ expression = "(notUnique=" + ab_gt_max.decode('ascii') + ")" -+ res = self.l.search(base="OU=SEARCH_NON_UNIQUE,DC=SAMBA,DC=ORG", -+ scope=ldb.SCOPE_ONELEVEL, -+ expression=expression) -+ self.assertEqual(len(res), 1) -+ self.assertTrue( -+ contains(res, "OU=01,OU=SEARCH_NON_UNIQUE,DC=SAMBA,DC=ORG")) -+ -+ expression = "(notUnique=" + bb_gt_max.decode('ascii') + ")" -+ res = self.l.search(base="OU=SEARCH_NON_UNIQUE,DC=SAMBA,DC=ORG", -+ scope=ldb.SCOPE_ONELEVEL, -+ expression=expression) -+ self.assertEqual(len(res), 1) -+ self.assertTrue( -+ contains(res, "OU=01,OU=SEARCH_NON_UNIQUE,DC=SAMBA,DC=ORG")) -+ -+ # -+ # Test deletion of records with non unique index with multivalued index -+ # attributes -+ # replicate this to test modify with modify flags i.e. DELETE, REPLACE -+ # -+ def test_delete_index_multi_valued_truncated_keys(self): -+ # 0 1 2 3 4 5 -+ # 12345678901234567890123456789012345678901234567890 -+ # @INDEX:NOTUNIQUE:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -+ -+ aa_gt_max = b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" -+ ab_gt_max = b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab" -+ bb_gt_max = b"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" -+ cc_gt_max = b"cccccccccccccccccccccccccccccccccc" -+ -+ self.l.add({"dn": "OU=01,OU=DELETE_NON_UNIQUE,DC=SAMBA,DC=ORG", -+ "notUnique": [aa_gt_max, ab_gt_max, bb_gt_max], -+ "objectUUID": b"0123456789abcde0"}) -+ self.l.add({"dn": "OU=02,OU=DELETE_NON_UNIQUE,DC=SAMBA,DC=ORG", -+ "notUnique": [aa_gt_max, ab_gt_max, cc_gt_max], -+ "objectUUID": b"0123456789abcde1"}) -+ -+ res = self.l.search( -+ base="DC=SAMBA,DC=ORG", -+ expression="(notUnique=" + aa_gt_max.decode("ascii") + ")") -+ self.assertEqual(2, len(res)) -+ self.assertTrue( -+ contains(res, "OU=01,OU=DELETE_NON_UNIQUE,DC=SAMBA,DC=ORG")) -+ self.assertTrue( -+ contains(res, "OU=02,OU=DELETE_NON_UNIQUE,DC=SAMBA,DC=ORG")) -+ -+ self.l.delete("OU=02,OU=DELETE_NON_UNIQUE,DC=SAMBA,DC=ORG") -+ -+ self.l.delete("OU=01,OU=DELETE_NON_UNIQUE,DC=SAMBA,DC=ORG") -+ -+ # -+ # Test modification of records with non unique index with multivalued index -+ # attributes -+ # -+ def test_modify_index_multi_valued_truncated_keys(self): -+ # 0 1 2 3 4 5 -+ # 12345678901234567890123456789012345678901234567890 -+ # @INDEX:NOTUNIQUE:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -+ -+ aa_gt_max = b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" -+ ab_gt_max = b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab" -+ bb_gt_max = b"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" -+ cc_gt_max = b"cccccccccccccccccccccccccccccccccc" -+ -+ self.l.add({"dn": "OU=01,OU=MODIFY_NON_UNIQUE,DC=SAMBA,DC=ORG", -+ "notUnique": [aa_gt_max, ab_gt_max, bb_gt_max], -+ "objectUUID": b"0123456789abcde0"}) -+ self.l.add({"dn": "OU=02,OU=MODIFY_NON_UNIQUE,DC=SAMBA,DC=ORG", -+ "notUnique": [aa_gt_max, ab_gt_max, cc_gt_max], -+ "objectUUID": b"0123456789abcde1"}) -+ -+ res = self.l.search( -+ base="DC=SAMBA,DC=ORG", -+ expression="(notUnique=" + aa_gt_max.decode("ascii") + ")") -+ self.assertEquals(2, len(res)) -+ self.assertTrue( -+ contains(res, "OU=01,OU=MODIFY_NON_UNIQUE,DC=SAMBA,DC=ORG")) -+ self.assertTrue( -+ contains(res, "OU=02,OU=MODIFY_NON_UNIQUE,DC=SAMBA,DC=ORG")) -+ -+ # -+ # Modify that does not change the indexed attribute -+ # -+ msg = ldb.Message() -+ msg.dn = ldb.Dn(self.l, "OU=01,OU=MODIFY_NON_UNIQUE,DC=SAMBA,DC=ORG") -+ msg["notUnique"] = ldb.MessageElement( -+ [aa_gt_max, ab_gt_max, bb_gt_max], -+ ldb.FLAG_MOD_REPLACE, -+ "notUnique") -+ self.l.modify(msg) -+ # -+ # As the modify is replacing the attribute with the same contents -+ # there should be no changes to the indexes. -+ # -+ -+ # -+ # Modify that removes a value from the indexed attribute -+ # -+ msg = ldb.Message() -+ msg.dn = ldb.Dn(self.l, "OU=01,OU=MODIFY_NON_UNIQUE,DC=SAMBA,DC=ORG") -+ msg["notUnique"] = ldb.MessageElement( -+ [aa_gt_max, bb_gt_max], -+ ldb.FLAG_MOD_REPLACE, -+ "notUnique") -+ self.l.modify(msg) -+ -+ # -+ # Modify that does a constrained delete the indexed attribute -+ # -+ msg = ldb.Message() -+ msg.dn = ldb.Dn(self.l, "OU=02,OU=MODIFY_NON_UNIQUE,DC=SAMBA,DC=ORG") -+ msg["notUnique"] = ldb.MessageElement( -+ [ab_gt_max], -+ ldb.FLAG_MOD_DELETE, -+ "notUnique") -+ self.l.modify(msg) -+ -+ # -+ # Modify that does an unconstrained delete the indexed attribute -+ # -+ msg = ldb.Message() -+ msg.dn = ldb.Dn(self.l, "OU=02,OU=MODIFY_NON_UNIQUE,DC=SAMBA,DC=ORG") -+ msg["notUnique"] = ldb.MessageElement( -+ [], -+ ldb.FLAG_MOD_DELETE, -+ "notUnique") -+ self.l.modify(msg) -+ -+ # -+ # Modify that adds a value to the indexed attribute -+ # -+ msg = ldb.Message() -+ msg.dn = ldb.Dn(self.l, "OU=02,OU=MODIFY_NON_UNIQUE,DC=SAMBA,DC=ORG") -+ msg["notUnique"] = ldb.MessageElement( -+ [cc_gt_max], -+ ldb.FLAG_MOD_ADD, -+ "notUnique") -+ self.l.modify(msg) -+ -+ # -+ # Modify that adds a values to the indexed attribute -+ # -+ msg = ldb.Message() -+ msg.dn = ldb.Dn(self.l, "OU=02,OU=MODIFY_NON_UNIQUE,DC=SAMBA,DC=ORG") -+ msg["notUnique"] = ldb.MessageElement( -+ [aa_gt_max, ab_gt_max], -+ ldb.FLAG_MOD_ADD, -+ "notUnique") -+ self.l.modify(msg) -+ -+ # -+ # Test Sub tree searches when checkBaseOnSearch is enabled and the -+ # DN indexes are truncated and collide. -+ # -+ def test_check_base_on_search_truncated_dn_keys(self): -+ # -+ # Except for the base DN's -+ # all entries the DN index key gets truncated to -+ # 0 1 2 3 4 5 -+ # 12345678901234567890123456789012345678901234567890 -+ # @INDEX:@IDXDN:OU=??,OU=CHECK_BASE_DN_XXXX,DC=SAMBA -+ # The base DN-s truncate to -+ # @INDEX:@IDXDN:OU=CHECK_BASE_DN_XXXX,DC=SAMBA,DC=OR -+ # -+ checkbaseonsearch = {"dn": "@OPTIONS", -+ "checkBaseOnSearch": b"TRUE"} -+ self.l.add(checkbaseonsearch) -+ -+ self.l.add({"dn": "OU=CHECK_BASE_DN_XXXX,DC=SAMBA,DC=OR1", -+ "objectUUID": b"0123456789abcdef"}) -+ self.l.add({"dn": "OU=CHECK_BASE_DN_XXXX,DC=SAMBA,DC=OR2", -+ "objectUUID": b"0123456789abcdee"}) -+ -+ self.l.add({"dn": "OU=01,OU=CHECK_BASE_DN_XXXX,DC=SAMBA,DC=OR1", -+ "objectUUID": b"0123456789abcdec"}) -+ self.l.add({"dn": "OU=01,OU=CHECK_BASE_DN_XXXX,DC=SAMBA,DC=OR2", -+ "objectUUID": b"0123456789abcdeb"}) -+ self.l.add({"dn": "OU=01,OU=CHECK_BASE_DN_XXXX,DC=SAMBA,DC=OR3", -+ "objectUUID": b"0123456789abcded"}) -+ -+ self.l.add({"dn": "OU=02,OU=CHECK_BASE_DN_XXXX,DC=SAMBA,DC=OR1", -+ "objectUUID": b"0123456789abcde0"}) -+ self.l.add({"dn": "OU=02,OU=CHECK_BASE_DN_XXXX,DC=SAMBA,DC=OR2", -+ "objectUUID": b"0123456789abcde1"}) -+ self.l.add({"dn": "OU=02,OU=CHECK_BASE_DN_XXXX,DC=SAMBA,DC=OR3", -+ "objectUUID": b"0123456789abcde2"}) -+ -+ res = self.l.search(base="OU=CHECK_BASE_DN_XXXX,DC=SAMBA,DC=OR1", -+ scope=ldb.SCOPE_SUBTREE) -+ self.assertEqual(len(res), 3) -+ self.assertTrue( -+ contains(res, "OU=CHECK_BASE_DN_XXXX,DC=SAMBA,DC=OR1")) -+ self.assertTrue( -+ contains(res, "OU=01,OU=CHECK_BASE_DN_XXXX,DC=SAMBA,DC=OR1")) -+ self.assertTrue( -+ contains(res, "OU=02,OU=CHECK_BASE_DN_XXXX,DC=SAMBA,DC=OR1")) -+ -+ res = self.l.search(base="OU=CHECK_BASE_DN_XXXX,DC=SAMBA,DC=OR2", -+ scope=ldb.SCOPE_SUBTREE) -+ self.assertEqual(len(res), 3) -+ self.assertTrue( -+ contains(res, "OU=CHECK_BASE_DN_XXXX,DC=SAMBA,DC=OR2")) -+ self.assertTrue( -+ contains(res, "OU=01,OU=CHECK_BASE_DN_XXXX,DC=SAMBA,DC=OR2")) -+ self.assertTrue( -+ contains(res, "OU=02,OU=CHECK_BASE_DN_XXXX,DC=SAMBA,DC=OR2")) -+ -+ try: -+ res = self.l.search(base="OU=CHECK_BASE_DN_XXXX,DC=SAMBA,DC=OR3", -+ scope=ldb.SCOPE_SUBTREE) -+ self.fail("Expected exception no thrown") -+ except ldb.LdbError as e: -+ code = e.args[0] -+ self.assertEqual(ldb.ERR_NO_SUCH_OBJECT, code) -+ -+if __name__ == '__main__': -+ import unittest -+ unittest.TestProgram() -diff --git a/lib/ldb/wscript b/lib/ldb/wscript -index 6a204c0e42a..e14fa63ec2c 100644 ---- a/lib/ldb/wscript -+++ b/lib/ldb/wscript -@@ -374,7 +374,7 @@ def test(ctx): - if not os.path.exists(tmp_dir): - os.mkdir(tmp_dir) - pyret = samba_utils.RUN_PYTHON_TESTS( -- ['tests/python/api.py'], -+ ['tests/python/api.py', 'tests/python/index.py'], - extra_env={'SELFTEST_PREFIX': test_prefix}) - print("Python testsuite returned %d" % pyret) - --- -2.14.3 - - -From 418a6caaf3a72c1c33e767487d4f106d7b98c5ab Mon Sep 17 00:00:00 2001 -From: Andrew Bartlett <abart...@samba.org> -Date: Mon, 26 Mar 2018 16:01:13 +1300 -Subject: [PATCH 2/5] ldb_tdb: Ensure we can not commit an index that is - corrupt due to partial re-index - -The re-index traverse can abort part-way though and we need to ensure -that the transaction is never committed as that will leave an un-useable db. - -BUG: https://bugzilla.samba.org/show_bug.cgi?id=13335 - -Signed-off-by: Andrew Bartlett <abart...@samba.org> -Reviewed-by: Gary Lockyer <g...@catalyst.net.nz> -(cherry picked from commit e481e4f30f4dc540f6f129b4f2faea48ee195673) ---- - lib/ldb/ldb_tdb/ldb_tdb.c | 30 ++++++++++++++++++++++++++++++ - lib/ldb/ldb_tdb/ldb_tdb.h | 2 ++ - 2 files changed, 32 insertions(+) - -diff --git a/lib/ldb/ldb_tdb/ldb_tdb.c b/lib/ldb/ldb_tdb/ldb_tdb.c -index 16e4b8ea26e..a530a454b29 100644 ---- a/lib/ldb/ldb_tdb/ldb_tdb.c -+++ b/lib/ldb/ldb_tdb/ldb_tdb.c -@@ -410,6 +410,10 @@ static int ltdb_modified(struct ldb_module *module, struct ldb_dn *dn) - ret = ltdb_cache_reload(module); - } - -+ if (ret != LDB_SUCCESS) { -+ ltdb->reindex_failed = true; -+ } -+ - return ret; - } - -@@ -1404,9 +1408,17 @@ static int ltdb_start_trans(struct ldb_module *module) - - ltdb_index_transaction_start(module); - -+ ltdb->reindex_failed = false; -+ - return LDB_SUCCESS; - } - -+/* -+ * Forward declaration to allow prepare_commit to in fact abort the -+ * transaction -+ */ -+static int ltdb_del_trans(struct ldb_module *module); -+ - static int ltdb_prepare_commit(struct ldb_module *module) - { - int ret; -@@ -1417,6 +1429,24 @@ static int ltdb_prepare_commit(struct ldb_module *module) - return LDB_SUCCESS; - } - -+ /* -+ * Check if the last re-index failed. -+ * -+ * This can happen if for example a duplicate value was marked -+ * unique. We must not write a partial re-index into the DB. -+ */ -+ if (ltdb->reindex_failed) { -+ /* -+ * We must instead abort the transaction so we get the -+ * old values and old index back -+ */ -+ ltdb_del_trans(module); -+ ldb_set_errstring(ldb_module_get_ctx(module), -+ "Failure during re-index, so " -+ "transaction must be aborted."); -+ return LDB_ERR_OPERATIONS_ERROR; -+ } -+ - ret = ltdb_index_transaction_commit(module); - if (ret != LDB_SUCCESS) { - tdb_transaction_cancel(ltdb->tdb); -diff --git a/lib/ldb/ldb_tdb/ldb_tdb.h b/lib/ldb/ldb_tdb/ldb_tdb.h -index 7e182495928..9591ee59bf1 100644 ---- a/lib/ldb/ldb_tdb/ldb_tdb.h -+++ b/lib/ldb/ldb_tdb/ldb_tdb.h -@@ -37,6 +37,8 @@ struct ltdb_private { - - bool read_only; - -+ bool reindex_failed; -+ - const struct ldb_schema_syntax *GUID_index_syntax; - }; - --- -2.14.3 - - -From 34ae16944ec51ff6848d7222b58c9a88921a434c Mon Sep 17 00:00:00 2001 -From: Gary Lockyer <g...@catalyst.net.nz> -Date: Tue, 6 Mar 2018 09:13:31 +1300 -Subject: [PATCH 3/5] lib ldb tests: Prepare to run api and index test on tdb - and lmdb - -BUG: https://bugzilla.samba.org/show_bug.cgi?id=13335 - -Signed-off-by: Gary Lockyer <g...@catalyst.net.nz> -Reviewed-by: Andrew Bartlett <abart...@samba.org> -(cherry picked from commit 06d9566ef7005588de18c5a1d07a5b9cd179d17b) ---- - lib/ldb/tests/python/api.py | 145 +++++++++++++++++++++++++----------------- - lib/ldb/tests/python/index.py | 29 ++++++++- - 2 files changed, 114 insertions(+), 60 deletions(-) - -diff --git a/lib/ldb/tests/python/api.py b/lib/ldb/tests/python/api.py -index 409f446f1ea..eb6f4544ffb 100755 ---- a/lib/ldb/tests/python/api.py -+++ b/lib/ldb/tests/python/api.py -@@ -12,6 +12,9 @@ import shutil - - PY3 = sys.version_info > (3, 0) - -+TDB_PREFIX = "tdb://" -+MDB_PREFIX = "mdb://" -+ - - def tempdir(): - import tempfile -@@ -44,13 +47,36 @@ class NoContextTests(TestCase): - encoded2 = ldb.binary_encode('test\\x') - self.assertEqual(encoded2, encoded) - --class SimpleLdb(TestCase): -+ -+class LdbBaseTest(TestCase): -+ def setUp(self): -+ super(LdbBaseTest, self).setUp() -+ try: -+ if self.prefix is None: -+ self.prefix = TDB_PREFIX -+ except AttributeError: -+ self.prefix = TDB_PREFIX -+ -+ def tearDown(self): -+ super(LdbBaseTest, self).tearDown() -+ -+ def url(self): -+ return self.prefix + self.filename -+ -+ def flags(self): -+ if self.prefix == MDB_PREFIX: -+ return ldb.FLG_NOSYNC -+ else: -+ return 0 -+ -+ -+class SimpleLdb(LdbBaseTest): - - def setUp(self): - super(SimpleLdb, self).setUp() - self.testdir = tempdir() - self.filename = os.path.join(self.testdir, "test.ldb") -- self.ldb = ldb.Ldb(self.filename) -+ self.ldb = ldb.Ldb(self.url(), flags=self.flags()) - - def tearDown(self): - shutil.rmtree(self.testdir) -@@ -58,16 +84,15 @@ class SimpleLdb(TestCase): - # Ensure the LDB is closed now, so we close the FD - del(self.ldb) - -- - def test_connect(self): -- ldb.Ldb(self.filename) -+ ldb.Ldb(self.url(), flags=self.flags()) - - def test_connect_none(self): - ldb.Ldb() - - def test_connect_later(self): - x = ldb.Ldb() -- x.connect(self.filename) -+ x.connect(self.url(), flags=self.flags()) - - def test_repr(self): - x = ldb.Ldb() -@@ -82,7 +107,7 @@ class SimpleLdb(TestCase): - self.assertEqual([], x.modules()) - - def test_modules_tdb(self): -- x = ldb.Ldb(self.filename) -+ x = ldb.Ldb(self.url(), flags=self.flags()) - self.assertEqual("[<ldb module 'tdb'>]", repr(x.modules())) - - def test_firstmodule_none(self): -@@ -90,53 +115,53 @@ class SimpleLdb(TestCase): - self.assertEqual(x.firstmodule, None) - - def test_firstmodule_tdb(self): -- x = ldb.Ldb(self.filename) -+ x = ldb.Ldb(self.url(), flags=self.flags()) - mod = x.firstmodule - self.assertEqual(repr(mod), "<ldb module 'tdb'>") - - def test_search(self): -- l = ldb.Ldb(self.filename) -+ l = ldb.Ldb(self.url(), flags=self.flags()) - self.assertEqual(len(l.search()), 0) - - def test_search_controls(self): -- l = ldb.Ldb(self.filename) -+ l = ldb.Ldb(self.url(), flags=self.flags()) - self.assertEqual(len(l.search(controls=["paged_results:0:5"])), 0) - - def test_search_attrs(self): -- l = ldb.Ldb(self.filename) -+ l = ldb.Ldb(self.url(), flags=self.flags()) - self.assertEqual(len(l.search(ldb.Dn(l, ""), ldb.SCOPE_SUBTREE, "(dc=*)", ["dc"])), 0) - - def test_search_string_dn(self): -- l = ldb.Ldb(self.filename) -+ l = ldb.Ldb(self.url(), flags=self.flags()) - self.assertEqual(len(l.search("", ldb.SCOPE_SUBTREE, "(dc=*)", ["dc"])), 0) - - def test_search_attr_string(self): -- l = ldb.Ldb(self.filename) -+ l = ldb.Ldb(self.url(), flags=self.flags()) - self.assertRaises(TypeError, l.search, attrs="dc") - self.assertRaises(TypeError, l.search, attrs=b"dc") - - def test_opaque(self): -- l = ldb.Ldb(self.filename) -+ l = ldb.Ldb(self.url(), flags=self.flags()) - l.set_opaque("my_opaque", l) - self.assertTrue(l.get_opaque("my_opaque") is not None) - self.assertEqual(None, l.get_opaque("unknown")) - - def test_search_scope_base_empty_db(self): -- l = ldb.Ldb(self.filename) -+ l = ldb.Ldb(self.url(), flags=self.flags()) - self.assertEqual(len(l.search(ldb.Dn(l, "dc=foo1"), - ldb.SCOPE_BASE)), 0) - - def test_search_scope_onelevel_empty_db(self): -- l = ldb.Ldb(self.filename) -+ l = ldb.Ldb(self.url(), flags=self.flags()) - self.assertEqual(len(l.search(ldb.Dn(l, "dc=foo1"), - ldb.SCOPE_ONELEVEL)), 0) - - def test_delete(self): -- l = ldb.Ldb(self.filename) -+ l = ldb.Ldb(self.url(), flags=self.flags()) - self.assertRaises(ldb.LdbError, lambda: l.delete(ldb.Dn(l, "dc=foo2"))) - - def test_delete_w_unhandled_ctrl(self): -- l = ldb.Ldb(self.filename) -+ l = ldb.Ldb(self.url(), flags=self.flags()) - m = ldb.Message() - m.dn = ldb.Dn(l, "dc=foo1") - m["b"] = [b"a"] -@@ -145,10 +170,10 @@ class SimpleLdb(TestCase): - l.delete(m.dn) - - def test_contains(self): -- name = self.filename -- l = ldb.Ldb(name) -+ name = self.url() -+ l = ldb.Ldb(name, flags=self.flags()) - self.assertFalse(ldb.Dn(l, "dc=foo3") in l) -- l = ldb.Ldb(name) -+ l = ldb.Ldb(name, flags=self.flags()) - m = ldb.Message() - m.dn = ldb.Dn(l, "dc=foo3") - m["b"] = ["a"] -@@ -160,23 +185,23 @@ class SimpleLdb(TestCase): - l.delete(m.dn) - - def test_get_config_basedn(self): -- l = ldb.Ldb(self.filename) -+ l = ldb.Ldb(self.url(), flags=self.flags()) - self.assertEqual(None, l.get_config_basedn()) - - def test_get_root_basedn(self): -- l = ldb.Ldb(self.filename) -+ l = ldb.Ldb(self.url(), flags=self.flags()) - self.assertEqual(None, l.get_root_basedn()) - - def test_get_schema_basedn(self): -- l = ldb.Ldb(self.filename) -+ l = ldb.Ldb(self.url(), flags=self.flags()) - self.assertEqual(None, l.get_schema_basedn()) - - def test_get_default_basedn(self): -- l = ldb.Ldb(self.filename) -+ l = ldb.Ldb(self.url(), flags=self.flags()) - self.assertEqual(None, l.get_default_basedn()) - - def test_add(self): -- l = ldb.Ldb(self.filename) -+ l = ldb.Ldb(self.url(), flags=self.flags()) - m = ldb.Message() - m.dn = ldb.Dn(l, "dc=foo4") - m["bla"] = b"bla" -@@ -188,7 +213,7 @@ class SimpleLdb(TestCase): - l.delete(ldb.Dn(l, "dc=foo4")) - - def test_search_iterator(self): -- l = ldb.Ldb(self.filename) -+ l = ldb.Ldb(self.url(), flags=self.flags()) - s = l.search_iterator() - s.abandon() - try: -@@ -288,7 +313,7 @@ class SimpleLdb(TestCase): - l.delete(ldb.Dn(l, "dc=foo5")) - - def test_add_text(self): -- l = ldb.Ldb(self.filename) -+ l = ldb.Ldb(self.url(), flags=self.flags()) - m = ldb.Message() - m.dn = ldb.Dn(l, "dc=foo4") - m["bla"] = "bla" -@@ -300,7 +325,7 @@ class SimpleLdb(TestCase): - l.delete(ldb.Dn(l, "dc=foo4")) - - def test_add_w_unhandled_ctrl(self): -- l = ldb.Ldb(self.filename) -+ l = ldb.Ldb(self.url(), flags=self.flags()) - m = ldb.Message() - m.dn = ldb.Dn(l, "dc=foo4") - m["bla"] = b"bla" -@@ -308,7 +333,7 @@ class SimpleLdb(TestCase): - self.assertRaises(ldb.LdbError, lambda: l.add(m,["search_options:1:2"])) - - def test_add_dict(self): -- l = ldb.Ldb(self.filename) -+ l = ldb.Ldb(self.url(), flags=self.flags()) - m = {"dn": ldb.Dn(l, "dc=foo5"), - "bla": b"bla"} - self.assertEqual(len(l.search()), 0) -@@ -319,7 +344,7 @@ class SimpleLdb(TestCase): - l.delete(ldb.Dn(l, "dc=foo5")) - - def test_add_dict_text(self): -- l = ldb.Ldb(self.filename) -+ l = ldb.Ldb(self.url(), flags=self.flags()) - m = {"dn": ldb.Dn(l, "dc=foo5"), - "bla": "bla"} - self.assertEqual(len(l.search()), 0) -@@ -330,7 +355,7 @@ class SimpleLdb(TestCase): - l.delete(ldb.Dn(l, "dc=foo5")) - - def test_add_dict_string_dn(self): -- l = ldb.Ldb(self.filename) -+ l = ldb.Ldb(self.url(), flags=self.flags()) - m = {"dn": "dc=foo6", "bla": b"bla"} - self.assertEqual(len(l.search()), 0) - l.add(m) -@@ -340,7 +365,7 @@ class SimpleLdb(TestCase): - l.delete(ldb.Dn(l, "dc=foo6")) - - def test_add_dict_bytes_dn(self): -- l = ldb.Ldb(self.filename) -+ l = ldb.Ldb(self.url(), flags=self.flags()) - m = {"dn": b"dc=foo6", "bla": b"bla"} - self.assertEqual(len(l.search()), 0) - l.add(m) -@@ -350,7 +375,7 @@ class SimpleLdb(TestCase): - l.delete(ldb.Dn(l, "dc=foo6")) - - def test_rename(self): -- l = ldb.Ldb(self.filename) -+ l = ldb.Ldb(self.url(), flags=self.flags()) - m = ldb.Message() - m.dn = ldb.Dn(l, "dc=foo7") - m["bla"] = b"bla" -@@ -363,7 +388,7 @@ class SimpleLdb(TestCase): - l.delete(ldb.Dn(l, "dc=bar")) - - def test_rename_string_dns(self): -- l = ldb.Ldb(self.filename) -+ l = ldb.Ldb(self.url(), flags=self.flags()) - m = ldb.Message() - m.dn = ldb.Dn(l, "dc=foo8") - m["bla"] = b"bla" -@@ -377,7 +402,7 @@ class SimpleLdb(TestCase): - l.delete(ldb.Dn(l, "dc=bar")) - - def test_empty_dn(self): -- l = ldb.Ldb(self.filename) -+ l = ldb.Ldb(self.url(), flags=self.flags()) - self.assertEqual(0, len(l.search())) - m = ldb.Message() - m.dn = ldb.Dn(l, "dc=empty") -@@ -394,7 +419,7 @@ class SimpleLdb(TestCase): - self.assertEqual(0, len(rm[0])) - - def test_modify_delete(self): -- l = ldb.Ldb(self.filename) -+ l = ldb.Ldb(self.url(), flags=self.flags()) - m = ldb.Message() - m.dn = ldb.Dn(l, "dc=modifydelete") - m["bla"] = [b"1234"] -@@ -417,7 +442,7 @@ class SimpleLdb(TestCase): - l.delete(ldb.Dn(l, "dc=modifydelete")) - - def test_modify_delete_text(self): -- l = ldb.Ldb(self.filename) -+ l = ldb.Ldb(self.url(), flags=self.flags()) - m = ldb.Message() - m.dn = ldb.Dn(l, "dc=modifydelete") - m.text["bla"] = ["1234"] -@@ -440,7 +465,7 @@ class SimpleLdb(TestCase): - l.delete(ldb.Dn(l, "dc=modifydelete")) - - def test_modify_add(self): -- l = ldb.Ldb(self.filename) -+ l = ldb.Ldb(self.url(), flags=self.flags()) - m = ldb.Message() - m.dn = ldb.Dn(l, "dc=add") - m["bla"] = [b"1234"] -@@ -458,7 +483,7 @@ class SimpleLdb(TestCase): - l.delete(ldb.Dn(l, "dc=add")) - - def test_modify_add_text(self): -- l = ldb.Ldb(self.filename) -+ l = ldb.Ldb(self.url(), flags=self.flags()) - m = ldb.Message() - m.dn = ldb.Dn(l, "dc=add") - m.text["bla"] = ["1234"] -@@ -476,7 +501,7 @@ class SimpleLdb(TestCase): - l.delete(ldb.Dn(l, "dc=add")) - - def test_modify_replace(self): -- l = ldb.Ldb(self.filename) -+ l = ldb.Ldb(self.url(), flags=self.flags()) - m = ldb.Message() - m.dn = ldb.Dn(l, "dc=modify2") - m["bla"] = [b"1234", b"456"] -@@ -496,7 +521,7 @@ class SimpleLdb(TestCase): - l.delete(ldb.Dn(l, "dc=modify2")) - - def test_modify_replace_text(self): -- l = ldb.Ldb(self.filename) -+ l = ldb.Ldb(self.url(), flags=self.flags()) - m = ldb.Message() - m.dn = ldb.Dn(l, "dc=modify2") - m.text["bla"] = ["1234", "456"] -@@ -516,7 +541,7 @@ class SimpleLdb(TestCase): - l.delete(ldb.Dn(l, "dc=modify2")) - - def test_modify_flags_change(self): -- l = ldb.Ldb(self.filename) -+ l = ldb.Ldb(self.url(), flags=self.flags()) - m = ldb.Message() - m.dn = ldb.Dn(l, "dc=add") - m["bla"] = [b"1234"] -@@ -542,7 +567,7 @@ class SimpleLdb(TestCase): - l.delete(ldb.Dn(l, "dc=add")) - - def test_modify_flags_change_text(self): -- l = ldb.Ldb(self.filename) -+ l = ldb.Ldb(self.url(), flags=self.flags()) - m = ldb.Message() - m.dn = ldb.Dn(l, "dc=add") - m.text["bla"] = ["1234"] -@@ -568,7 +593,7 @@ class SimpleLdb(TestCase): - l.delete(ldb.Dn(l, "dc=add")) - - def test_transaction_commit(self): -- l = ldb.Ldb(self.filename) -+ l = ldb.Ldb(self.url(), flags=self.flags()) - l.transaction_start() - m = ldb.Message(ldb.Dn(l, "dc=foo9")) - m["foo"] = [b"bar"] -@@ -577,7 +602,7 @@ class SimpleLdb(TestCase): - l.delete(m.dn) - - def test_transaction_cancel(self): -- l = ldb.Ldb(self.filename) -+ l = ldb.Ldb(self.url(), flags=self.flags()) - l.transaction_start() - m = ldb.Message(ldb.Dn(l, "dc=foo10")) - m["foo"] = [b"bar"] -@@ -588,12 +613,12 @@ class SimpleLdb(TestCase): - def test_set_debug(self): - def my_report_fn(level, text): - pass -- l = ldb.Ldb(self.filename) -+ l = ldb.Ldb(self.url(), flags=self.flags()) - l.set_debug(my_report_fn) - - def test_zero_byte_string(self): - """Testing we do not get trapped in the \0 byte in a property string.""" -- l = ldb.Ldb(self.filename) -+ l = ldb.Ldb(self.url(), flags=self.flags()) - l.add({ - "dn" : b"dc=somedn", - "objectclass" : b"user", -@@ -605,10 +630,10 @@ class SimpleLdb(TestCase): - self.assertEqual(b"foo\0bar", res[0]["displayname"][0]) - - def test_no_crash_broken_expr(self): -- l = ldb.Ldb(self.filename) -+ l = ldb.Ldb(self.url(), flags=self.flags()) - self.assertRaises(ldb.LdbError,lambda: l.search("", ldb.SCOPE_SUBTREE, "&(dc=*)(dn=*)", ["dc"])) - --class SearchTests(TestCase): -+class SearchTests(LdbBaseTest): - def tearDown(self): - shutil.rmtree(self.testdir) - super(SearchTests, self).tearDown() -@@ -621,7 +646,9 @@ class SearchTests(TestCase): - super(SearchTests, self).setUp() - self.testdir = tempdir() - self.filename = os.path.join(self.testdir, "search_test.ldb") -- self.l = ldb.Ldb(self.filename, options=["modules:rdn_name"]) -+ self.l = ldb.Ldb(self.url(), -+ flags=self.flags(), -+ options=["modules:rdn_name"]) - - self.l.add({"dn": "@ATTRIBUTES", - "DC": "CASE_INSENSITIVE"}) -@@ -1030,7 +1057,6 @@ class SearchTests(TestCase): - self.assertEqual(len(res11), 1) - - -- - class IndexedSearchTests(SearchTests): - """Test searches using the index, to ensure the index doesn't - break things""" -@@ -1091,6 +1117,7 @@ class GUIDIndexedSearchTests(SearchTests): - self.IDXGUID = True - self.IDXONE = True - -+ - class GUIDIndexedDNFilterSearchTests(SearchTests): - """Test searches using the index, to ensure the index doesn't - break things""" -@@ -1126,7 +1153,7 @@ class GUIDAndOneLevelIndexedSearchTests(SearchTests): - self.IDXONE = True - - --class AddModifyTests(TestCase): -+class AddModifyTests(LdbBaseTest): - def tearDown(self): - shutil.rmtree(self.testdir) - super(AddModifyTests, self).tearDown() -@@ -1138,7 +1165,9 @@ class AddModifyTests(TestCase): - super(AddModifyTests, self).setUp() - self.testdir = tempdir() - self.filename = os.path.join(self.testdir, "add_test.ldb") -- self.l = ldb.Ldb(self.filename, options=["modules:rdn_name"]) -+ self.l = ldb.Ldb(self.url(), -+ flags=self.flags(), -+ options=["modules:rdn_name"]) - self.l.add({"dn": "DC=SAMBA,DC=ORG", - "name": b"samba.org", - "objectUUID": b"0123456789abcdef"}) -@@ -1266,6 +1295,7 @@ class AddModifyTests(TestCase): - "x": "z", "y": "a", - "objectUUID": b"0123456789abcde3"}) - -+ - class IndexedAddModifyTests(AddModifyTests): - """Test searches using the index, to ensure the index doesn't - break things""" -@@ -1378,7 +1408,6 @@ class TransIndexedAddModifyTests(IndexedAddModifyTests): - super(TransIndexedAddModifyTests, self).tearDown() - - -- - class DnTests(TestCase): - - def setUp(self): -@@ -1985,13 +2014,13 @@ class ModuleTests(TestCase): - l = ldb.Ldb(self.filename) - self.assertEqual(["init"], ops) - --class LdbResultTests(TestCase): -+class LdbResultTests(LdbBaseTest): - - def setUp(self): - super(LdbResultTests, self).setUp() - self.testdir = tempdir() - self.filename = os.path.join(self.testdir, "test.ldb") -- self.l = ldb.Ldb(self.filename) -+ self.l = ldb.Ldb(self.url(), flags=self.flags()) - self.l.add({"dn": "DC=SAMBA,DC=ORG", "name": b"samba.org"}) - self.l.add({"dn": "OU=ADMIN,DC=SAMBA,DC=ORG", "name": b"Admins"}) - self.l.add({"dn": "OU=USERS,DC=SAMBA,DC=ORG", "name": b"Users"}) -@@ -2099,7 +2128,7 @@ class LdbResultTests(TestCase): - del(self.l) - gc.collect() - -- child_ldb = ldb.Ldb(self.filename) -+ child_ldb = ldb.Ldb(self.url(), flags=self.flags()) - # start a transaction - child_ldb.transaction_start() - -@@ -2170,7 +2199,7 @@ class LdbResultTests(TestCase): - del(self.l) - gc.collect() - -- child_ldb = ldb.Ldb(self.filename) -+ child_ldb = ldb.Ldb(self.url(), flags=self.flags()) - # start a transaction - child_ldb.transaction_start() - -diff --git a/lib/ldb/tests/python/index.py b/lib/ldb/tests/python/index.py -index cd3735b5625..d8a84f26b4c 100755 ---- a/lib/ldb/tests/python/index.py -+++ b/lib/ldb/tests/python/index.py -@@ -37,6 +37,9 @@ import shutil - - PY3 = sys.version_info > (3, 0) - -+TDB_PREFIX = "tdb://" -+MDB_PREFIX = "mdb://" -+ - - def tempdir(): - import tempfile -@@ -57,7 +60,29 @@ def contains(result, dn): - return False - - --class MaxIndexKeyLengthTests(TestCase): -+class LdbBaseTest(TestCase): -+ def setUp(self): -+ super(LdbBaseTest, self).setUp() -+ try: -+ if self.prefix is None: -+ self.prefix = TDB_PREFIX -+ except AttributeError: -+ self.prefix = TDB_PREFIX -+ -+ def tearDown(self): -+ super(LdbBaseTest, self).tearDown() -+ -+ def url(self): -+ return self.prefix + self.filename -+ -+ def flags(self): -+ if self.prefix == MDB_PREFIX: -+ return ldb.FLG_NOSYNC -+ else: -+ return 0 -+ -+ -+class MaxIndexKeyLengthTests(LdbBaseTest): - def checkGuids(self, key, guids): - # - # This check relies on the current implementation where the indexes -@@ -94,7 +119,7 @@ class MaxIndexKeyLengthTests(TestCase): - self.testdir = tempdir() - self.filename = os.path.join(self.testdir, "key_len_test.ldb") - # Note that the maximum key length is set to 50 -- self.l = ldb.Ldb(self.filename, -+ self.l = ldb.Ldb(self.url(), - options=[ - "modules:rdn_name", - "max_key_len_for_self_test:50"]) --- -2.14.3 - - -From e32ade946bfceb81bd0671b1af698ffd13d4c40b Mon Sep 17 00:00:00 2001 -From: Andrew Bartlett <abart...@samba.org> -Date: Mon, 26 Mar 2018 16:07:45 +1300 -Subject: [PATCH 4/5] ldb: Add test to show a reindex failure must not leave - the DB corrupt - -BUG: https://bugzilla.samba.org/show_bug.cgi?id=13335 - -Signed-off-by: Andrew Bartlett <abart...@samba.org> -Reviewed-by: Gary Lockyer <g...@catalyst.net.nz> - -Autobuild-User(master): Andrew Bartlett <abart...@samba.org> -Autobuild-Date(master): Thu Apr 5 07:53:10 CEST 2018 on sn-devel-144 - -(cherry picked from commit 653a0a1ba932fc0cc567253f3e153b2928505ba2) ---- - lib/ldb/tests/python/api.py | 160 ++++++++++++++++++++++++++++++++++++++++++++ - 1 file changed, 160 insertions(+) - -diff --git a/lib/ldb/tests/python/api.py b/lib/ldb/tests/python/api.py -index eb6f4544ffb..1167517fd5c 100755 ---- a/lib/ldb/tests/python/api.py -+++ b/lib/ldb/tests/python/api.py -@@ -1408,6 +1408,166 @@ class TransIndexedAddModifyTests(IndexedAddModifyTests): - super(TransIndexedAddModifyTests, self).tearDown() - - -+class BadIndexTests(LdbBaseTest): -+ def setUp(self): -+ super(BadIndexTests, self).setUp() -+ self.testdir = tempdir() -+ self.filename = os.path.join(self.testdir, "test.ldb") -+ self.ldb = ldb.Ldb(self.url(), flags=self.flags()) -+ if hasattr(self, 'IDXGUID'): -+ self.ldb.add({"dn": "@INDEXLIST", -+ "@IDXATTR": [b"x", b"y", b"ou"], -+ "@IDXGUID": [b"objectUUID"], -+ "@IDX_DN_GUID": [b"GUID"]}) -+ else: -+ self.ldb.add({"dn": "@INDEXLIST", -+ "@IDXATTR": [b"x", b"y", b"ou"]}) -+ -+ super(BadIndexTests, self).setUp() -+ -+ def test_unique(self): -+ self.ldb.add({"dn": "x=x,dc=samba,dc=org", -+ "objectUUID": b"0123456789abcde1", -+ "y": "1"}) -+ self.ldb.add({"dn": "x=y,dc=samba,dc=org", -+ "objectUUID": b"0123456789abcde2", -+ "y": "1"}) -+ self.ldb.add({"dn": "x=z,dc=samba,dc=org", -+ "objectUUID": b"0123456789abcde3", -+ "y": "1"}) -+ -+ res = self.ldb.search(expression="(y=1)", -+ base="dc=samba,dc=org") -+ self.assertEquals(len(res), 3) -+ -+ # Now set this to unique index, but forget to check the result -+ try: -+ self.ldb.add({"dn": "@ATTRIBUTES", -+ "y": "UNIQUE_INDEX"}) -+ self.fail() -+ except ldb.LdbError: -+ pass -+ -+ # We must still have a working index -+ res = self.ldb.search(expression="(y=1)", -+ base="dc=samba,dc=org") -+ self.assertEquals(len(res), 3) -+ -+ def test_unique_transaction(self): -+ self.ldb.add({"dn": "x=x,dc=samba,dc=org", -+ "objectUUID": b"0123456789abcde1", -+ "y": "1"}) -+ self.ldb.add({"dn": "x=y,dc=samba,dc=org", -+ "objectUUID": b"0123456789abcde2", -+ "y": "1"}) -+ self.ldb.add({"dn": "x=z,dc=samba,dc=org", -+ "objectUUID": b"0123456789abcde3", -+ "y": "1"}) -+ -+ res = self.ldb.search(expression="(y=1)", -+ base="dc=samba,dc=org") -+ self.assertEquals(len(res), 3) -+ -+ self.ldb.transaction_start() -+ -+ # Now set this to unique index, but forget to check the result -+ try: -+ self.ldb.add({"dn": "@ATTRIBUTES", -+ "y": "UNIQUE_INDEX"}) -+ except ldb.LdbError: -+ pass -+ -+ try: -+ self.ldb.transaction_commit() -+ self.fail() -+ -+ except ldb.LdbError as err: -+ enum = err.args[0] -+ self.assertEqual(enum, ldb.ERR_OPERATIONS_ERROR) -+ -+ # We must still have a working index -+ res = self.ldb.search(expression="(y=1)", -+ base="dc=samba,dc=org") -+ -+ self.assertEquals(len(res), 3) -+ -+ def test_casefold(self): -+ self.ldb.add({"dn": "x=x,dc=samba,dc=org", -+ "objectUUID": b"0123456789abcde1", -+ "y": "a"}) -+ self.ldb.add({"dn": "x=y,dc=samba,dc=org", -+ "objectUUID": b"0123456789abcde2", -+ "y": "A"}) -+ self.ldb.add({"dn": "x=z,dc=samba,dc=org", -+ "objectUUID": b"0123456789abcde3", -+ "y": ["a", "A"]}) -+ -+ res = self.ldb.search(expression="(y=a)", -+ base="dc=samba,dc=org") -+ self.assertEquals(len(res), 2) -+ -+ self.ldb.add({"dn": "@ATTRIBUTES", -+ "y": "CASE_INSENSITIVE"}) -+ -+ # We must still have a working index -+ res = self.ldb.search(expression="(y=a)", -+ base="dc=samba,dc=org") -+ -+ if hasattr(self, 'IDXGUID'): -+ self.assertEquals(len(res), 3) -+ else: -+ # We should not return this entry twice, but sadly -+ # we have not yet fixed -+ # https://bugzilla.samba.org/show_bug.cgi?id=13361 -+ self.assertEquals(len(res), 4) -+ -+ def test_casefold_transaction(self): -+ self.ldb.add({"dn": "x=x,dc=samba,dc=org", -+ "objectUUID": b"0123456789abcde1", -+ "y": "a"}) -+ self.ldb.add({"dn": "x=y,dc=samba,dc=org", -+ "objectUUID": b"0123456789abcde2", -+ "y": "A"}) -+ self.ldb.add({"dn": "x=z,dc=samba,dc=org", -+ "objectUUID": b"0123456789abcde3", -+ "y": ["a", "A"]}) -+ -+ res = self.ldb.search(expression="(y=a)", -+ base="dc=samba,dc=org") -+ self.assertEquals(len(res), 2) -+ -+ self.ldb.transaction_start() -+ -+ self.ldb.add({"dn": "@ATTRIBUTES", -+ "y": "CASE_INSENSITIVE"}) -+ -+ self.ldb.transaction_commit() -+ -+ # We must still have a working index -+ res = self.ldb.search(expression="(y=a)", -+ base="dc=samba,dc=org") -+ -+ if hasattr(self, 'IDXGUID'): -+ self.assertEquals(len(res), 3) -+ else: -+ # We should not return this entry twice, but sadly -+ # we have not yet fixed -+ # https://bugzilla.samba.org/show_bug.cgi?id=13361 -+ self.assertEquals(len(res), 4) -+ -+ -+ def tearDown(self): -+ super(BadIndexTests, self).tearDown() -+ -+ -+class GUIDBadIndexTests(BadIndexTests): -+ """Test Bad index things with GUID index mode""" -+ def setUp(self): -+ self.IDXGUID = True -+ -+ super(GUIDBadIndexTests, self).setUp() -+ -+ - class DnTests(TestCase): - - def setUp(self): --- -2.14.3 - - -From 7fef1bb5b2f8c126430c72b42a595552cc1fd48f Mon Sep 17 00:00:00 2001 -From: Gary Lockyer <g...@catalyst.net.nz> -Date: Wed, 28 Feb 2018 11:47:22 +1300 -Subject: [PATCH 5/5] ldb_tdb: Do not fail in GUID index mode if there is a - duplicate attribute - -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 <g...@catalyst.net.nz> -Reviewed-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> -Reviewed-by: Andrew Bartlett <abart...@samba.org> - -BUG: https://bugzilla.samba.org/show_bug.cgi?id=13335 - -(cherry picked from commit 5c1504b94d1417894176811f18c5d450de22cfd2) ---- - lib/ldb/ldb_tdb/ldb_index.c | 64 ++++++++++++++++++++++++++++++++++++++++----- - 1 file changed, 57 insertions(+), 7 deletions(-) - -diff --git a/lib/ldb/ldb_tdb/ldb_index.c b/lib/ldb/ldb_tdb/ldb_index.c -index 99fef23662f..ee2027319e3 100644 ---- a/lib/ldb/ldb_tdb/ldb_index.c -+++ b/lib/ldb/ldb_tdb/ldb_index.c -@@ -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) { --- -2.14.3 -