Diff
108 commits, 123 files changed
+5086 -2574

Ticket 49293 - inttypes in nunc-stans
William Brown • 5 years ago  
Ticket 49298 - fix complier warn
William Brown • 6 years ago  
Ticket 49298 - fix missing header
William Brown • 6 years ago  
Ticket 49231 - force EXTERNAL always
William Brown • 6 years ago  
Ticket 49231 - fix sasl mech handling
William Brown • 6 years ago  
file modified
+9 -7
@@ -943,6 +943,7 @@

  	ldap/admin/src/scripts/50refintprecedence.ldif \

  	ldap/admin/src/scripts/50retroclprecedence.ldif \

  	ldap/admin/src/scripts/50rootdnaccesscontrolplugin.ldif \

+ 	ldap/admin/src/scripts/50pbkdf2pwdstorageplugin.ldif \

  	ldap/admin/src/scripts/50contentsync.ldif \

  	ldap/admin/src/scripts/60upgradeschemafiles.pl \

  	ldap/admin/src/scripts/60upgradeconfigfiles.pl \
@@ -2014,7 +2015,8 @@

  

  # We need to link a lot of plugins for this test.

  test_slapd_LDADD = 	libslapd.la \

- 					libpwdstorage-plugin.la

+ 					libpwdstorage-plugin.la \

+ 					$(NSS_LINK) $(NSPR_LINK)

  test_slapd_LDFLAGS = $(AM_CPPFLAGS) $(CMOCKA_LINKS)

  ### WARNING: Slap.h needs cert.h, which requires the -I/lib/ldaputil!!!

  ### WARNING: Slap.h pulls ssl.h, which requires nss!!!!
@@ -2034,35 +2036,35 @@

  	src/libsds/test/test_fixtures.c

  

  test_libsds_LDFLAGS = $(ASAN_DEFINES) $(PROFILING_LINKS) $(CMOCKA_LINKS)

- test_libsds_LDADD = libsds.la

+ test_libsds_LDADD = libsds.la $(NSPR_LINK)

  test_libsds_CPPFLAGS = $(AM_CPPFLAGS) $(CMOCKA_INCLUDES) $(SDS_CPPFLAGS)

  

  benchmark_sds_SOURCES =  src/libsds/test/benchmark.c \

  	$(libavl_a_SOURCES)

  benchmark_sds_LDFLAGS = $(ASAN_DEFINES) $(PROFILING_LINKS) $(CMOCKA_LINKS)

- benchmark_sds_LDADD = libsds.la

+ benchmark_sds_LDADD = libsds.la $(NSPR_LINK)

  benchmark_sds_CPPFLAGS = $(AM_CPPFLAGS) $(CMOCKA_INCLUDES) $(SDS_CPPFLAGS) $(DS_INCLUDES)

  

  benchmark_par_sds_SOURCES = src/libsds/test/benchmark_parwrap.c \

  	src/libsds/test/benchmark_par.c \

  	$(libavl_a_SOURCES)

  benchmark_par_sds_LDFLAGS = $(ASAN_DEFINES) $(PROFILING_LINKS) $(CMOCKA_LINKS)

- benchmark_par_sds_LDADD = libsds.la

+ benchmark_par_sds_LDADD = libsds.la $(NSPR_LINK)

  benchmark_par_sds_CPPFLAGS = $(AM_CPPFLAGS) $(CMOCKA_INCLUDES) $(SDS_CPPFLAGS) $(DS_INCLUDES)

  

  test_nuncstans_SOURCES = src/nunc-stans/test/test_nuncstans.c

  test_nuncstans_CPPFLAGS = $(AM_CPPFLAGS) $(CMOCKA_INCLUDES) $(NUNCSTANS_CPPFLAGS)

- test_nuncstans_LDADD = libnunc-stans.la libsds.la

+ test_nuncstans_LDADD = libnunc-stans.la libsds.la $(NSPR_LINK)

  test_nuncstans_LDFLAGS = $(ASAN_DEFINES) $(PROFILING_LINKS) $(CMOCKA_LINKS) $(EVENT_LINK)

  

  test_nuncstans_stress_large_SOURCES = src/nunc-stans/test/test_nuncstans_stress_large.c src/nunc-stans/test/test_nuncstans_stress_core.c

  test_nuncstans_stress_large_CPPFLAGS = $(AM_CPPFLAGS) $(CMOCKA_INCLUDES) $(NUNCSTANS_CPPFLAGS)

- test_nuncstans_stress_large_LDADD = libnunc-stans.la libsds.la

+ test_nuncstans_stress_large_LDADD = libnunc-stans.la libsds.la $(NSPR_LINK)

  test_nuncstans_stress_large_LDFLAGS = $(ASAN_DEFINES) $(PROFILING_LINKS) $(CMOCKA_LINKS) $(EVENT_LINK)

  

  test_nuncstans_stress_small_SOURCES = src/nunc-stans/test/test_nuncstans_stress_small.c src/nunc-stans/test/test_nuncstans_stress_core.c

  test_nuncstans_stress_small_CPPFLAGS = $(AM_CPPFLAGS) $(CMOCKA_INCLUDES) $(NUNCSTANS_CPPFLAGS)

- test_nuncstans_stress_small_LDADD = libnunc-stans.la libsds.la

+ test_nuncstans_stress_small_LDADD = libnunc-stans.la libsds.la $(NSPR_LINK)

  test_nuncstans_stress_small_LDFLAGS = $(ASAN_DEFINES) $(PROFILING_LINKS) $(CMOCKA_LINKS) $(EVENT_LINK)

  

  

file modified
+1 -1
@@ -10,7 +10,7 @@

  # PACKAGE_VERSION is constructed from these

  VERSION_MAJOR=1

  VERSION_MINOR=3

- VERSION_MAINT=6.4

+ VERSION_MAINT=6.15

  # NOTE: VERSION_PREREL is automatically set for builds made out of a git tree

  VERSION_PREREL=

  VERSION_DATE=$(date -u +%Y%m%d)

file modified
+9 -2
@@ -39,7 +39,11 @@

  AC_HEADER_DIRENT

  AC_HEADER_STDC

  AC_HEADER_SYS_WAIT

- AC_CHECK_HEADERS([arpa/inet.h errno.h fcntl.h malloc.h netdb.h netinet/in.h stdlib.h string.h strings.h sys/file.h sys/socket.h sys/time.h syslog.h unistd.h inttypes.h mntent.h sys/sysinfo.h])

+ AC_CHECK_HEADERS([arpa/inet.h errno.h fcntl.h malloc.h netdb.h netinet/in.h stdlib.h string.h strings.h sys/file.h sys/socket.h sys/time.h syslog.h unistd.h mntent.h sys/sysinfo.h sys/endian.h endian.h])

+ 

+ # These are *required* headers without option.

+ AC_CHECK_HEADERS([inttypes.h], [], AC_MSG_ERROR([unable to locate required header inttypes.h]))

+ 

  

  # Checks for typedefs, structures, and compiler characteristics.

  AC_HEADER_STAT
@@ -66,7 +70,10 @@

  AC_FUNC_STRERROR_R

  AC_FUNC_STRFTIME

  AC_FUNC_VPRINTF

- AC_CHECK_FUNCS([clock_gettime endpwent ftruncate getcwd gethostbyname inet_ntoa localtime_r memmove memset mkdir munmap putenv rmdir setrlimit socket strcasecmp strchr strcspn strdup strerror strncasecmp strpbrk strrchr strstr strtol tzset])

+ AC_CHECK_FUNCS([endpwent ftruncate getcwd gethostbyname inet_ntoa localtime_r memmove memset mkdir munmap putenv rmdir setrlimit socket strcasecmp strchr strcspn strdup strerror strncasecmp strpbrk strrchr strstr strtol tzset])

+ 

+ # These functions are *required* without option.

+ AC_CHECK_FUNCS([clock_gettime], [], AC_MSG_ERROR([unable to locate required symbol clock_gettime]))

  

  # This will detect if we need to add the LIBADD_DL value for us.

  LT_LIB_DLLOAD

@@ -8,6 +8,7 @@

  #

  import pytest

  import six

+ import ldap

  from lib389.tasks import *

  from lib389.utils import *

  from lib389.topologies import topology_st
@@ -27,7 +28,7 @@

  

  def test_betxt_7bit(topology_st):

      '''

-     Test that the 7-bit plugin correctly rejects an invlaid update

+     Test that the 7-bit plugin correctly rejects an invalid update

      '''

  

      log.info('Running test_betxt_7bit...')
@@ -142,7 +143,8 @@

      # Enable and configure memberOf plugin

      topology_st.standalone.plugins.enable(name=PLUGIN_MEMBER_OF)

      try:

-         topology_st.standalone.modify_s(PLUGIN_DN, [(ldap.MOD_REPLACE, 'memberofgroupattr', 'member')])

+         topology_st.standalone.modify_s(PLUGIN_DN, [(ldap.MOD_REPLACE, 'memberofgroupattr', 'member'),

+                                                     (ldap.MOD_REPLACE, 'memberofAutoAddOC', 'referral')])

      except ldap.LDAPError as e:

          log.fatal('test_betxn_memberof: Failed to update config(member): error ' + e.message['desc'])

          assert False
@@ -194,6 +196,62 @@

      log.info('test_betxn_memberof: PASSED')

  

  

+ def test_betxn_modrdn_memberof(topology_st):

+     """Test modrdn operartions and memberOf

+ 

+     :id: 70d0b96e-b693-4bf7-bbf5-102a66ac5994

+ 

+     :setup: Standalone instance

+ 

+     :steps: 1. Enable and configure memberOf plugin

+             2. Set memberofgroupattr="member" and memberofAutoAddOC="nsContainer"

+             3. Create group and user outside of memberOf plugin scope

+             4. Do modrdn to move group into scope

+             5. Do modrdn to move group into scope (again)

+ 

+     :expectedresults:

+             1. memberOf plugin plugin should be ON

+             2. Set memberofgroupattr="member" and memberofAutoAddOC="nsContainer" should PASS

+             3. Creating group and user should PASS

+             4. Modrdn should fail with objectclass violation

+             5. Second modrdn should also fail with objectclass violation

+     """

+ 

+     peoplebase = 'ou=people,%s' % DEFAULT_SUFFIX

+     memberof = MemberOfPlugin(topology_st.standalone)

+     memberof.enable()

+     memberof.set_autoaddoc('nsContainer')  # Bad OC

+     memberof.set('memberOfEntryScope', peoplebase)

+     memberof.set('memberOfAllBackends', 'on')

+     topology_st.standalone.restart()

+ 

+     groups = Groups(topology_st.standalone, DEFAULT_SUFFIX)

+     group = groups.create(properties={

+         'cn': 'group',

+     })

+ 

+     # Create user and add it to group

+     users = UserAccounts(topology_st.standalone, basedn=DEFAULT_SUFFIX)

+     user = users.create(properties=TEST_USER_PROPERTIES)

+     if not ds_is_older('1.3.7'):

+         user.remove('objectClass', 'nsMemberOf')

+ 

+     group.add_member(user.dn)

+ 

+     # Attempt modrdn that should fail, but the original entry should stay in the cache

+     with pytest.raises(ldap.OBJECTCLASS_VIOLATION):

+         group.rename('cn=group_to_people', newsuperior=peoplebase)

+ 

+     # Should fail, but not with NO_SUCH_OBJECT as the original entry should still be in the cache

+     with pytest.raises(ldap.OBJECTCLASS_VIOLATION):

+         group.rename('cn=group_to_people', newsuperior=peoplebase)

+ 

+     #

+     # Done

+     #

+     log.info('test_betxn_modrdn_memberof: PASSED')

+ 

+ 

  if __name__ == '__main__':

      # Run isolated

      # -s for DEBUG mode

@@ -0,0 +1,274 @@

+ # --- BEGIN COPYRIGHT BLOCK ---

+ # Copyright (C) 2017 Red Hat, Inc.

+ # All rights reserved.

+ #

+ # License: GPL (version 3 or any later version).

+ # See LICENSE for details.

+ # --- END COPYRIGHT BLOCK ---

+ #

+ import pytest

+ from lib389._mapped_object import DSLdapObject

+ from lib389.utils import *

+ from lib389.topologies import topology_st as topo

+ 

+ DEBUGGING = os.getenv("DEBUGGING", default=False)

+ if DEBUGGING:

+     logging.getLogger(__name__).setLevel(logging.DEBUG)

+ else:

+     logging.getLogger(__name__).setLevel(logging.INFO)

+ log = logging.getLogger(__name__)

+ 

+ 

+ def test_threads_basic(topo):

+     """Check that a number of threads are able to be autotuned

+ 

+     :ID: 371fb9c4-9607-4a4b-a4a2-6f00809d6257

+     :feature: Autotuning

+     :setup: Standalone instance

+     :steps: 1. Set nsslapd-threadnumber to -1

+             2. Get the number of CPUs on the system and match it with the value from docs

+             3. Check that nsslapd-threadnumber is equal to the documented expected value

+     :expectedresults: nsslapd-threadnumber is equal to the documented expected value

+     """

+ 

+     log.info("Set nsslapd-threadnumber: -1 to enable autotuning")

+     topo.standalone.config.set("nsslapd-threadnumber", "-1")

+ 

+     log.info("Assert nsslapd-threadnumber is equal to the documented expected value")

+     assert topo.standalone.config.get_attr_val("nsslapd-threadnumber") > 0

+ 

+ 

+ @pytest.mark.parametrize("invalid_value", ('-2', '0', 'invalid'))

+ def test_threads_invalid_value(topo, invalid_value):

+     """Check nsslapd-threadnumber for an invalid values

+ 

+     :ID: 1979eddf-8222-4c9d-809d-269c26de636e

+     :feature: Autotuning

+     :setup: Standalone instance

+     :steps: 1. Set nsslapd-threadnumber to -2, 0, 513, invalid_str

+     :expectedresults: The operation should fail

+     """

+ 

+     log.info("Set nsslapd-threadnumber: {}. Operation should fail".format(invalid_value))

+     with pytest.raises(ldap.OPERATIONS_ERROR):

+         topo.standalone.config.set("nsslapd-threadnumber", invalid_value)

+ 

+ 

+ def test_threads_back_from_manual_value(topo):

+     """Check that thread autotuning works after manual tuning

+ 

+     :ID: 4b674016-e5ca-426b-a9c0-a94745a7dd25

+     :feature: Autotuning

+     :setup: Standalone instance

+     :steps: 1. Set nsslapd-threadnumber to -1 and save the autotuned value

+             2. Decrease nsslapd-threadnumber by 2

+             3. Set nsslapd-threadnumber to -1

+             4. Check that nsslapd-threadnumber is back to autotuned value

+     :expectedresults: nsslapd-threadnumber is set back to the autotuned value

+     """

+ 

+     log.info("Set nsslapd-threadnumber: -1 to enable autotuning and save the new value")

+     topo.standalone.config.set("nsslapd-threadnumber", "-1")

+     autotuned_value = topo.standalone.config.get_attr_val("nsslapd-threadnumber")

+ 

+     log.info("Set nsslapd-threadnumber to the autotuned value decreased by 2")

+     new_value = str(int(autotuned_value) - 2)

+     topo.standalone.config.set("nsslapd-threadnumber", new_value)

+     assert topo.standalone.config.get_attr_val("nsslapd-threadnumber") == new_value

+ 

+     log.info("Set nsslapd-threadnumber: -1 to enable autotuning")

+     topo.standalone.config.set("nsslapd-threadnumber", "-1")

+ 

+     log.info("Assert nsslapd-threadnumber is back to the autotuned value")

+     assert topo.standalone.config.get_attr_val("nsslapd-threadnumber") == autotuned_value

+ 

+ 

+ @pytest.mark.parametrize("autosize,autosize_split", (('', ''), ('', '0'), ('10', '40'), ('', '40'),

+                                                      ('10', ''), ('10', '40'), ('10', '0')))

+ def test_cache_autosize_non_zero(topo, autosize, autosize_split):

+     """Check that autosizing works works properly in different combinations

+ 

+     :ID: 83fa099c-a6c9-457a-82db-0982b67e8598

+     :feature: Autotuning

+     :setup: Standalone instance

+     :steps: 1. Set in the cn=config,cn=ldbm database,cn=plugins,cn=config:

+                nsslapd-cache-autosize, nsslapd-cache-autosize-split to the next value pairs:

+                 ('', ''), ('', '0'), ('10', '40'), ('', '40'),

+                 ('10', ''), ('10', '40'), ('10', '0')

+                '' - for deleting the value (set to default)

+             2. Try to modify nsslapd-dbcachesize and nsslapd-cachememsize to

+                some real value, it should be rejected

+             2. Restart the instance

+             3. Check nsslapd-dbcachesize and nsslapd-cachememsize

+     :expectedresults: Modify operation was rejected,

+                       nsslapd-dbcachesize and nsslapd-cachememsize were set to

+                       value in the expected range between 512KB and max int on the system

+     """

+ 

+     config_ldbm = DSLdapObject(topo.standalone, DN_CONFIG_LDBM)

+     userroot_ldbm = DSLdapObject(topo.standalone, DN_USERROOT_LDBM)

+ 

+     cachesize = '33333333'

+ 

+     dbcachesize_val = config_ldbm.get_attr_val('nsslapd-dbcachesize')

+     cachenensize_val = userroot_ldbm.get_attr_val('nsslapd-cachememsize')

+     autosize_val = config_ldbm.get_attr_val('nsslapd-cache-autosize')

+     autosize_split_val = config_ldbm.get_attr_val('nsslapd-cache-autosize-split')

+ 

+     log.info("Check nsslapd-dbcachesize and nsslapd-cachememsize before the test")

+     log.info("nsslapd-dbcachesize == {}".format(dbcachesize_val))

+     log.info("nsslapd-cachememsize == {}".format(cachenensize_val))

+     log.info("nsslapd-cache-autosize == {}".format(autosize_val))

+     log.info("nsslapd-cache-autosize-split == {}".format(autosize_split_val))

+ 

+     if autosize:

+         log.info("Set nsslapd-cache-autosize to {}".format(autosize))

+         config_ldbm.set('nsslapd-cache-autosize', autosize)

+     else:

+         log.info("Delete nsslapd-cache-autosize")

+         try:

+             config_ldbm.remove('nsslapd-cache-autosize', autosize_val)

+         except ValueError:

+             log.info("nsslapd-cache-autosize wasn't found")

+ 

+     if autosize_split:

+         log.info("Set nsslapd-cache-autosize-split to {}".format(autosize_split))

+         config_ldbm.set('nsslapd-cache-autosize-split', autosize_split)

+     else:

+         log.info("Delete nsslapd-cache-autosize-split")

+         try:

+             config_ldbm.remove('nsslapd-cache-autosize-split', autosize_split_val)

+         except ValueError:

+             log.info("nsslapd-cache-autosize-split wasn't found")

+ 

+     log.info("Trying to set nsslapd-cachememsize to {}".format(cachesize))

+     with pytest.raises(ldap.UNWILLING_TO_PERFORM):

+         userroot_ldbm.set('nsslapd-cachememsize', cachesize)

+     log.info("Trying to set nsslapd-dbcachesize to {}".format(cachesize))

+     with pytest.raises(ldap.UNWILLING_TO_PERFORM):

+         config_ldbm.set('nsslapd-dbcachesize ', cachesize)

+     topo.standalone.restart()

+ 

+     dbcachesize_val = config_ldbm.get_attr_val('nsslapd-dbcachesize')

+     cachenensize_val = userroot_ldbm.get_attr_val('nsslapd-cachememsize')

+     autosize_val = config_ldbm.get_attr_val('nsslapd-cache-autosize')

+     autosize_split_val = config_ldbm.get_attr_val('nsslapd-cache-autosize-split')

+ 

+     log.info("Check nsslapd-dbcachesize and nsslapd-cachememsize in the appropriate range.")

+     log.info("nsslapd-dbcachesize == {}".format(dbcachesize_val))

+     log.info("nsslapd-cachememsize == {}".format(cachenensize_val))

+     log.info("nsslapd-cache-autosize == {}".format(autosize_val))

+     log.info("nsslapd-cache-autosize-split == {}".format(autosize_split_val))

+     assert int(dbcachesize_val) >= 512000

+     assert int(dbcachesize_val) <= sys.maxint

+     assert int(cachenensize_val) >= 512000

+     assert int(cachenensize_val) <= sys.maxint

+ 

+ 

+ @pytest.mark.parametrize("autosize_split", ('0', '', '40'))

+ def test_cache_autosize_basic_sane(topo, autosize_split):

+     """Check that autotuning cachesizes works properly with different values

+ 

+     :ID: 9dc363ef-f551-446d-8b83-8ac45dabb8df

+     :feature: Autotuning

+     :setup: Standalone instance

+     :steps: 1. Set in the cn=config,cn=ldbm database,cn=plugins,cn=config:

+                nsslapd-cache-autosize, nsslapd-cache-autosize-split to the next value pairs:

+                ('0', '0'), ('0', ''), ('0', '40')

+                '' - for deleting the value (set to default)

+             2. Set in the cn=config,cn=ldbm database,cn=plugins,cn=config:

+                nsslapd-dbcachesize: 0 and some same value

+             3. Set in the cn=UserRoot,cn=ldbm database,cn=plugins,cn=config:

+                nsslapd-cachememsize: 0 and some same value

+             4. Restart the instance

+             5. Check nsslapd-dbcachesize and nsslapd-cachememsize

+     :expectedresults: nsslapd-dbcachesize and nsslapd-cachememsize were set to

+                       value in the expected range between 512KB and max int on the system

+     """

+ 

+     config_ldbm = DSLdapObject(topo.standalone, DN_CONFIG_LDBM)

+     userroot_ldbm = DSLdapObject(topo.standalone, DN_USERROOT_LDBM)

+     config_ldbm.set('nsslapd-cache-autosize', '0')

+ 

+     # Test with caches with both real values and 0

+     for cachesize in ('0', '33333333'):

+         dbcachesize_val = config_ldbm.get_attr_val('nsslapd-dbcachesize')

+         cachenensize_val = userroot_ldbm.get_attr_val('nsslapd-cachememsize')

+         autosize_val = config_ldbm.get_attr_val('nsslapd-cache-autosize')

+         autosize_split_val = config_ldbm.get_attr_val('nsslapd-cache-autosize-split')

+ 

+         log.info("Check nsslapd-dbcachesize and nsslapd-cachememsize before the test")

+         log.info("nsslapd-dbcachesize == {}".format(dbcachesize_val))

+         log.info("nsslapd-cachememsize == {}".format(cachenensize_val))

+         log.info("nsslapd-cache-autosize == {}".format(autosize_val))

+         log.info("nsslapd-cache-autosize-split == {}".format(autosize_split_val))

+ 

+         if autosize_split:

+             log.info("Set nsslapd-cache-autosize-split to {}".format(autosize_split))

+             config_ldbm.set('nsslapd-cache-autosize-split', autosize_split)

+         else:

+             log.info("Delete nsslapd-cache-autosize-split")

+             try:

+                 config_ldbm.remove('nsslapd-cache-autosize-split', autosize_split_val)

+             except ValueError:

+                 log.info("nsslapd-cache-autosize-split wasn't found")

+ 

+         log.info("Set nsslapd-dbcachesize to {}".format(cachesize))

+         config_ldbm.set('nsslapd-dbcachesize', cachesize)

+         log.info("Set nsslapd-cachememsize to {}".format(cachesize))

+         userroot_ldbm.set('nsslapd-cachememsize', cachesize)

+         topo.standalone.restart()

+ 

+         dbcachesize_val = config_ldbm.get_attr_val('nsslapd-dbcachesize')

+         cachenensize_val = userroot_ldbm.get_attr_val('nsslapd-cachememsize')

+         autosize_val = config_ldbm.get_attr_val('nsslapd-cache-autosize')

+         autosize_split_val = config_ldbm.get_attr_val('nsslapd-cache-autosize-split')

+ 

+         log.info("Check nsslapd-dbcachesize and nsslapd-cachememsize in the appropriate range.")

+         log.info("nsslapd-dbcachesize == {}".format(dbcachesize_val))

+         log.info("nsslapd-cachememsize == {}".format(cachenensize_val))

+         log.info("nsslapd-cache-autosize == {}".format(autosize_val))

+         log.info("nsslapd-cache-autosize-split == {}".format(autosize_split_val))

+         assert int(dbcachesize_val) >= 512000

+         assert int(dbcachesize_val) <= sys.maxint

+         assert int(cachenensize_val) >= 512000

+         assert int(cachenensize_val) <= sys.maxint

+ 

+ 

+ @pytest.mark.parametrize("invalid_value", ('-2', '102', 'invalid'))

+ def test_cache_autosize_invalid_values(topo, invalid_value):

+     """Check that we can't set invalid values to autosize attributes

+ 

+     :ID: 2f0d01b5-ca91-4dc2-97bc-ad0ac8d08633

+     :feature: Autotuning

+     :setup: Standalone instance

+     :steps: 1. Stop the instance

+             2. Set in the cn=config,cn=ldbm database,cn=plugins,cn=config:

+                nsslapd-cache-autosize and nsslapd-cache-autosize-split 

+                to invalid values like (-2, 102, invalid_str)

+             3. Try to start the instance

+     :expectedresults: Start dirsrv operation should fail

+     """

+ 

+     config_ldbm = DSLdapObject(topo.standalone, DN_CONFIG_LDBM)

+     autosize_val = config_ldbm.get_attr_val('nsslapd-cache-autosize')

+     autosize_split_val = config_ldbm.get_attr_val('nsslapd-cache-autosize-split')

+ 

+     log.info("Set nsslapd-cache-autosize-split to {}".format(invalid_value))

+     with pytest.raises(ldap.UNWILLING_TO_PERFORM):

+         config_ldbm.set('nsslapd-cache-autosize-split', invalid_value)

+         topo.standalone.restart()

+     config_ldbm.remove('nsslapd-cache-autosize-split', autosize_split_val)

+ 

+     log.info("Set nsslapd-cache-autosize to {}".format(invalid_value))

+     with pytest.raises(ldap.UNWILLING_TO_PERFORM):

+         config_ldbm.set('nsslapd-cache-autosize', invalid_value)

+         topo.standalone.restart()

+     config_ldbm.remove('nsslapd-cache-autosize', autosize_val)

+ 

+ 

+ if __name__ == '__main__':

+     # Run isolated

+     # -s for DEBUG mode

+     CURRENT_FILE = os.path.realpath(__file__)

+     pytest.main("-s %s" % CURRENT_FILE)

@@ -0,0 +1,81 @@

+ # --- BEGIN COPYRIGHT BLOCK ---

+ # Copyright (C) 2017 Red Hat, Inc.

+ # All rights reserved.

+ #

+ # License: GPL (version 3 or any later version).

+ # See LICENSE for details.

+ # --- END COPYRIGHT BLOCK ---

+ #

+ import pytest

+ import os

+ import logging

+ import subprocess

+ 

+ from lib389.topologies import topology_st as topo

+ 

+ DEBUGGING = os.getenv("DEBUGGING", default=False)

+ if DEBUGGING:

+     logging.getLogger(__name__).setLevel(logging.DEBUG)

+ else:

+     logging.getLogger(__name__).setLevel(logging.INFO)

+ log = logging.getLogger(__name__)

+ 

+ def test_restore_config(topo):

+     """

+     Check that if a dse.ldif and backup are removed, that the server still starts.

+ 

+     :id: e1c38fa7-30bc-46f2-a934-f8336f387581

+     :setup: Standalone instance

+     :steps:

+         1. Stop the instance

+         2. Delete 'dse.ldif'

+         3. Start the instance

+     :expectedresults:

+         1. Steps 1 and 2 succeed.

+         2. Server will succeed to start with restored cfg.

+     """

+     topo.standalone.stop()

+ 

+     dse_path = topo.standalone.get_config_dir()

+ 

+     log.info(dse_path)

+ 

+     for i in ('dse.ldif', 'dse.ldif.startOK'):

+         p = os.path.join(dse_path, i)

+         os.remove(p)

+ 

+     # This will pass.

+     topo.standalone.start()

+ 

+ def test_removed_config(topo):

+     """

+     Check that if a dse.ldif and backup are removed, that the server

+     exits better than "segfault".

+ 

+     :id: b45272d1-c197-473e-872f-07257fcb2ec0

+     :setup: Standalone instance

+     :steps:

+         1. Stop the instance

+         2. Delete 'dse.ldif', 'dse.ldif.bak', 'dse.ldif.startOK'

+         3. Start the instance

+     :expectedresults:

+         1. Steps 1 and 2 succeed.

+         2. Server will fail to start, but will not crash.

+     """

+     topo.standalone.stop()

+ 

+     dse_path = topo.standalone.get_config_dir()

+ 

+     log.info(dse_path)

+ 

+     for i in ('dse.ldif', 'dse.ldif.bak', 'dse.ldif.startOK'):

+         p = os.path.join(dse_path, i)

+         os.remove(p)

+ 

+     # We actually can't check the log output, because it can't read dse.ldif,

+     # don't know where to write it yet! All we want is the server fail to

+     # start here, rather than infinite run + segfault.

+     with pytest.raises(subprocess.CalledProcessError):

+         topo.standalone.start()

+ 

+ 

empty or binary file added
@@ -0,0 +1,172 @@

+ import logging

+ import pytest

+ import os

+ import ldap

+ import time

+ import subprocess

+ 

+ from lib389 import Entry

+ from lib389.idm.user import UserAccounts

+ from lib389.idm.domain import Domain

+ from lib389.topologies import topology_st as topo

+ from lib389._constants import (DEFAULT_SUFFIX, DN_DM, PASSWORD, HOST_STANDALONE,

+                                SERVERID_STANDALONE, PORT_STANDALONE)

+ 

+ 

+ DEBUGGING = os.getenv("DEBUGGING", default=False)

+ if DEBUGGING:

+     logging.getLogger(__name__).setLevel(logging.DEBUG)

+ else:

+     logging.getLogger(__name__).setLevel(logging.INFO)

+ log = logging.getLogger(__name__)

+ 

+ TEST_USER_DN = "uid=test_user,ou=people,dc=example,dc=com"

+ OU_PEOPLE = 'ou=people,{}'.format(DEFAULT_SUFFIX)

+ 

+ PW_POLICY_CONT_PEOPLE = 'cn="cn=nsPwPolicyEntry,' \

+                         'ou=people,dc=example,dc=com",' \

+                         'cn=nsPwPolicyContainer,ou=people,dc=example,dc=com'

+ 

+ PW_POLICY_CONT_PEOPLE2 = 'cn="cn=nsPwPolicyEntry,' \

+                         'dc=example,dc=com",' \

+                         'cn=nsPwPolicyContainerdc=example,dc=com'

+ 

+ 

+ def check_user(inst):

+     """Search the test user and make sure it has the execpted attrs

+     """

+     try:

+         entries = inst.search_s('dc=example,dc=com', ldap.SCOPE_SUBTREE, "uid=test_user")

+         log.debug('user: \n' + str(entries[0]))

+         assert entries[0].hasAttr('ou'), "Entry is missing ou cos attribute"

+         assert entries[0].hasAttr('x-department'), "Entry is missing description cos attribute"

+         assert entries[0].hasAttr('x-en-ou'), "Entry is missing givenname cos attribute"

+     except ldap.LDAPError as e:

+         log.fatal('Failed to search for user: ' + str(e))

+         raise e

+ 

+ 

+ def setup_subtree_policy(topo):

+     """Set up subtree password policy

+     """

+ 

+     topo.standalone.config.set('nsslapd-pwpolicy-local', 'on')

+ 

+     log.info('Create password policy for subtree {}'.format(OU_PEOPLE))

+     try:

+         subprocess.call(['%s/ns-newpwpolicy.pl' % topo.standalone.get_sbin_dir(),

+                          '-D', DN_DM, '-w', PASSWORD,

+                          '-p', str(PORT_STANDALONE), '-h', HOST_STANDALONE,

+                          '-S', DEFAULT_SUFFIX, '-Z', SERVERID_STANDALONE])

+     except subprocess.CalledProcessError as e:

+         log.error('Failed to create pw policy policy for {}: error {}'.format(

+             OU_PEOPLE, e.message['desc']))

+         raise e

+ 

+     domain = Domain(topo.standalone, DEFAULT_SUFFIX)

+     domain.replace('pwdpolicysubentry', PW_POLICY_CONT_PEOPLE2)

+ 

+     time.sleep(1)

+ 

+ 

+ def setup_indirect_cos(topo):

+     """Setup indirect COS definition and template

+     """

+     cosDef = Entry(('cn=cosDefinition,dc=example,dc=com',

+                     {'objectclass': ['top', 'ldapsubentry',

+                                      'cossuperdefinition',

+                                      'cosIndirectDefinition'],

+                      'cosAttribute': ['ou merge-schemes',

+                                       'x-department merge-schemes',

+                                       'x-en-ou merge-schemes'],

+                      'cosIndirectSpecifier': 'seeAlso',

+                      'cn': 'cosDefinition'}))

+ 

+     cosTemplate = Entry(('cn=cosTemplate,dc=example,dc=com',

+                          {'objectclass': ['top',

+                                           'extensibleObject',

+                                           'cosTemplate'],

+                           'ou': 'My COS Org',

+                           'x-department': 'My COS x-department',

+                           'x-en-ou': 'my COS x-en-ou',

+                           'cn': 'cosTemplate'}))

+     try:

+         topo.standalone.add_s(cosDef)

+         topo.standalone.add_s(cosTemplate)

+     except ldap.LDAPError as e:

+         log.fatal('Failed to add cos: error ' + str(e))

+         raise e

+     time.sleep(1)

+ 

+ 

+ @pytest.fixture(scope="module")

+ def setup(topo, request):

+     """Add schema, and test user

+     """

+     log.info('Add custom schema...')

+     try:

+         ATTR_1 = (b"( 1.3.6.1.4.1.409.389.2.189 NAME 'x-department' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'user defined' )")

+         ATTR_2 = (b"( 1.3.6.1.4.1.409.389.2.187 NAME 'x-en-ou' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'user defined' )")

+         OC = (b"( xPerson-oid NAME 'xPerson' DESC '' SUP person STRUCTURAL MAY ( x-department $ x-en-ou ) X-ORIGIN 'user defined' )")

+         topo.standalone.modify_s("cn=schema", [(ldap.MOD_ADD, 'attributeTypes', ATTR_1),

+                                                (ldap.MOD_ADD, 'attributeTypes', ATTR_2),

+                                                (ldap.MOD_ADD, 'objectClasses', OC)])

+     except ldap.LDAPError as e:

+         log.fatal('Failed to add custom schema')

+         raise e

+     time.sleep(1)

+ 

+     log.info('Add test user...')

+     users = UserAccounts(topo.standalone, DEFAULT_SUFFIX)

+ 

+     user_properties = {

+         'uid': 'test_user',

+         'cn': 'test user',

+         'sn': 'user',

+         'uidNumber': '1000',

+         'gidNumber': '2000',

+         'homeDirectory': '/home/test_user',

+         'seeAlso': 'cn=cosTemplate,dc=example,dc=com'

+     }

+     user = users.create(properties=user_properties)

+ 

+     user.add('objectClass', 'xPerson')

+ 

+     # Setup COS

+     log.info("Setup indirect COS...")

+     setup_indirect_cos(topo)

+ 

+ 

+ def test_indirect_cos(topo, setup):

+     """Test indirect cos

+ 

+     :id: 890d5929-7d52-4a56-956e-129611b4649a

+     :setup: standalone

+     :steps:

+         1. Test cos is working for test user

+         2. Add subtree password policy

+         3. Test cos is working for test user

+     :expectedresults:

+         1. User has expected cos attrs

+         2. Substree password policy setup is successful

+         3  User still has expected cos attrs

+     """

+ 

+     # Step 1 - Search user and see if the COS attrs are included

+     log.info('Checking user...')

+     check_user(topo.standalone)

+ 

+     # Step 2 - Add subtree password policy (Second COS - operational attribute)

+     setup_subtree_policy(topo)

+ 

+     # Step 3 - Check user again now hat we have a mix of vattrs

+     log.info('Checking user...')

+     check_user(topo.standalone)

+ 

+ 

+ if __name__ == '__main__':

+     # Run isolated

+     # -s for DEBUG mode

+     CURRENT_FILE = os.path.realpath(__file__)

+     pytest.main("-s %s" % CURRENT_FILE)

+ 

@@ -0,0 +1,57 @@

+ # --- BEGIN COPYRIGHT BLOCK ---

+ # Copyright (C) 2017 Red Hat, Inc.

+ # All rights reserved.

+ #

+ # License: GPL (version 3 or any later version).

+ # See LICENSE for details.

+ # --- END COPYRIGHT BLOCK ---

+ #

+ import ldap

+ import pytest

+ from lib389.topologies import topology_m2

+ from lib389._constants import (DEFAULT_SUFFIX, HOST_MASTER_2, PORT_MASTER_2, TASK_WAIT)

+ 

+ from lib389.idm.user import (TEST_USER_PROPERTIES, UserAccounts)

+ 

+ def test_referral_during_tot(topology_m2):

+ 

+     master1 = topology_m2.ms["master1"]

+     master2 = topology_m2.ms["master2"]

+ 

+     # Create a bunch of entries on master1

+     ldif_dir = master1.get_ldif_dir()

+     import_ldif = ldif_dir + '/ref_during_tot_import.ldif'

+     master1.buildLDIF(10000, import_ldif)

+ 

+     master1.stop()

+     try:

+         master1.ldif2db(bename=None, excludeSuffixes=None, encrypt=False, suffixes=[DEFAULT_SUFFIX], import_file=import_ldif)

+     except:

+         pass

+     # master1.tasks.importLDIF(suffix=DEFAULT_SUFFIX, input_file=import_ldif, args={TASK_WAIT: True})

+     master1.start()

+     users = UserAccounts(master1, DEFAULT_SUFFIX, rdn='ou=Accounting')

+ 

+     u = users.create(properties=TEST_USER_PROPERTIES)

+     u.set('userPassword', 'password')

+ 

+     binddn = u.dn

+     bindpw = 'password'

+ 

+     # Now export them to master2

+     master1.agreement.init(DEFAULT_SUFFIX, HOST_MASTER_2, PORT_MASTER_2)

+ 

+     # While that's happening try to bind as a user to master 2

+     # This should trigger the referral code.

+     for i in range(0, 100):

+         conn = ldap.initialize(master2.toLDAPURL())

+         conn.set_option(ldap.OPT_REFERRALS, False)

+         try:

+             conn.simple_bind_s(binddn, bindpw)

+             conn.unbind_s()

+         except ldap.REFERRAL:

+             pass

+ 

+     # Done.

+ 

+ 

@@ -0,0 +1,55 @@

+ # --- BEGIN COPYRIGHT BLOCK ---

+ # Copyright (C) 2017 Red Hat, Inc.

+ # All rights reserved.

+ #

+ # License: GPL (version 3 or any later version).

+ # See LICENSE for details.

+ # --- END COPYRIGHT BLOCK ---

+ #

+ import pytest

+ from lib389.tasks import *

+ from lib389.utils import *

+ from lib389.topologies import topology_st

+ from lib389.idm.user import UserAccounts, TEST_USER_PROPERTIES

+ import ldap

+ 

+ # The irony of these names is not lost on me.

+ GOOD_PASSWORD = 'password'

+ BAD_PASSWORD = 'aontseunao'

+ 

+ logging.getLogger(__name__).setLevel(logging.INFO)

+ log = logging.getLogger(__name__)

+ 

+ def test_lockout_bypass(topology_st):

+     inst = topology_st.standalone

+ 

+     # Configure the lock policy

+     inst.config.set('passwordMaxFailure', '1')

+     inst.config.set('passwordLockoutDuration', '99999')

+     inst.config.set('passwordLockout', 'on')

+ 

+     # Create the account

+     users = UserAccounts(inst, DEFAULT_SUFFIX)

+     testuser = users.create(properties=TEST_USER_PROPERTIES)

+     testuser.set('userPassword', GOOD_PASSWORD)

+ 

+     conn = testuser.bind(GOOD_PASSWORD)

+     assert conn != None

+     conn.unbind_s()

+ 

+     # Bind with bad creds twice

+     # This is the failure.

+     with pytest.raises(ldap.INVALID_CREDENTIALS):

+         conn = testuser.bind(BAD_PASSWORD)

+     # Now we should not be able to ATTEMPT the bind. It doesn't matter that

+     # we disclose that we have hit the rate limit here, what matters is that

+     # it exists.

+     with pytest.raises(ldap.CONSTRAINT_VIOLATION):

+         conn = testuser.bind(BAD_PASSWORD)

+ 

+     # now bind with good creds

+     # Should be error 19 still.

+     with pytest.raises(ldap.CONSTRAINT_VIOLATION):

+         conn = testuser.bind(GOOD_PASSWORD)

+ 

+ 

@@ -0,0 +1,53 @@

+ # --- BEGIN COPYRIGHT BLOCK ---

+ # Copyright (C) 2016 Red Hat, Inc.

+ # All rights reserved.

+ #

+ # License: GPL (version 3 or any later version).

+ # See LICENSE for details.

+ # --- END COPYRIGHT BLOCK ---

+ #

+ import pytest

+ from lib389._constants import DEFAULT_SUFFIX

+ from lib389.topologies import topology_st

+ from lib389.idm.group import Groups

+ import ldap

+ from ldap.controls.psearch import PersistentSearchControl,EntryChangeNotificationControl

+ 

+ def _run_psearch(inst, msg_id):

+     results = []

+     while True:

+         try:

+             _, data, _, _, _, _ = inst.result4(msgid=msg_id, all=0, timeout=1.0, add_ctrls=1, add_intermediates=1,

+                                                                 resp_ctrl_classes={EntryChangeNotificationControl.controlType:EntryChangeNotificationControl})

+             # See if there are any entry changes

+             for dn, entry, srv_ctrls in data:

+                 ecn_ctrls = filter(lambda c: c.controlType == EntryChangeNotificationControl.controlType, srv_ctrls)

+                 if ecn_ctrls:

+                     inst.log.info('%s has changed!' % dn)

+                     results.append(dn)

+         except ldap.TIMEOUT:

+             # There are no more results, so we timeout.

+             inst.log.info('No more results')

+             return results

+ 

+ def test_psearch(topology_st):

+     # Create the search control

+     psc = PersistentSearchControl()

+     # do a search extended with the control

+     msg_id = topology_st.standalone.search_ext(base=DEFAULT_SUFFIX, scope=ldap.SCOPE_SUBTREE, attrlist=['*'], serverctrls=[psc])

+     # Get the result for the message id with result4

+     _run_psearch(topology_st.standalone, msg_id)

+     # Change an entry / add one

+     groups = Groups(topology_st.standalone, DEFAULT_SUFFIX)

+     group = groups.create(properties={'cn': 'group1', 'description': 'testgroup'})

+     # Now run the result again and see what's there.

+     results = _run_psearch(topology_st.standalone, msg_id)

+     # assert our group is in the changeset.

+     assert(group.dn == results[0])

+ 

+ 

+ if __name__ == '__main__':

+     # Run isolated

+     # -s for DEBUG mode

+     CURRENT_FILE = os.path.realpath(__file__)

+     pytest.main("-s %s" % CURRENT_FILE)

@@ -11,6 +11,12 @@

  from lib389.utils import *

  from lib389.topologies import topology_m4 as topo_m4

  

+ from lib389._constants import (BACKEND_NAME, DEFAULT_SUFFIX, LOG_REPLICA, REPLICA_RUV_FILTER,

+                                ReplicaRole, REPLICATION_BIND_DN, REPLICATION_BIND_PW,

+                                REPLICATION_BIND_METHOD, REPLICATION_TRANSPORT, defaultProperties,

+                                RA_NAME, RA_BINDDN, RA_BINDPW, RA_METHOD, RA_TRANSPORT_PROT,

+                                DN_DM, PASSWORD, LOG_DEFAULT, RA_ENABLED, RA_SCHEDULE)

+ 

  TEST_ENTRY_NAME = 'mmrepl_test'

  TEST_ENTRY_DN = 'uid={},{}'.format(TEST_ENTRY_NAME, DEFAULT_SUFFIX)

  NEW_SUFFIX_NAME = 'test_repl'
@@ -480,6 +486,43 @@

          m2.setLogLevel(LOG_DEFAULT)

  

  

+ def test_invalid_agmt(topo_m4):

+     """Test adding that an invalid agreement is properly rejected and does not crash the server

+ 

+     :id: 6c3b2a7e-edcd-4327-a003-6bd878ff722b

+     :setup: MMR with four masters

+     :steps:

+         1. Add invalid agreement (nsds5ReplicaEnabled set to invalid value)

+         2. Verify the server is still running

+     :expectedresults:

+         1. Invalid repl agreement should be rejected

+         2. Server should be still running

+     """

+     m1 = topo_m4.ms["master1"]

+ 

+     # Add invalid agreement (nsds5ReplicaEnabled set to invalid value)

+     AGMT_DN = 'cn=whatever,cn=replica,cn="dc=example,dc=com",cn=mapping tree,cn=config'

+     try:

+         invalid_props = {RA_ENABLED: 'True',  # Invalid value

+                          RA_SCHEDULE: '0001-2359 0123456'}

+         m1.agreement.create(suffix=DEFAULT_SUFFIX, host='localhost', port=389, properties=invalid_props)

+     except ldap.UNWILLING_TO_PERFORM:

+         m1.log.info('Invalid repl agreement correctly rejected')

+     except ldap.LDAPError as e:

+         m1.log.fatal('Got unexpected error adding invalid agreement: ' + str(e))

+         assert False

+     else:

+         m1.log.fatal('Invalid agreement was incorrectly accepted by the server')

+         assert False

+ 

+     # Verify the server is still running

+     try:

+         m1.simple_bind_s(DN_DM, PASSWORD)

+     except ldap.LDAPError as e:

+         m1.log.fatal('Failed to bind: ' + str(e))

+         assert False

+ 

+ 

  if __name__ == '__main__':

      # Run isolated

      # -s for DEBUG mode

@@ -0,0 +1,148 @@

+ # --- BEGIN COPYRIGHT BLOCK ---

+ # Copyright (C) 2017 Red Hat, Inc.

+ # All rights reserved.

+ #

+ # License: GPL (version 3 or any later version).

+ # See LICENSE for details.

+ # --- END COPYRIGHT BLOCK ---

+ #

+ 

+ import pytest

+ import os

+ from lib389.topologies import topology_st

+ 

+ 

+ def test_sasl_allowed_mechs(topology_st):

+     """Test the alloweed sasl mechanism feature

+ 

+     :ID: ab7d9f86-8cfe-48c3-8baa-739e599f006a

+     :feature: Allowed sasl mechanisms

+     :steps: 1.  Get the default list of mechanisms

+             2.  Set allowed mechanism PLAIN, and verify it's correctly listed

+             3.  Restart server, and verify list is still correct

+             4.  Test EXTERNAL is properly listed

+             5.  Add GSSAPI to the existing list, and verify it's correctly listed

+             6.  Restart server and verify list is still correct

+             7.  Add ANONYMOUS to the existing list, and veirfy it's correctly listed

+             8.  Restart server and verify list is still correct

+             9.  Remove GSSAPI and verify it's correctly listed

+             10. Restart server and verify list is still correct

+             11. Reset allowed list to nothing, verify "all" the mechanisms are returned

+             12. Restart server and verify list is still correct

+ 

+     :expectedresults: The supported mechanisms supported what is set for the allowed

+                       mechanisms

+     """

+     standalone = topology_st.standalone

+ 

+     # Get the supported mechs. This should contain PLAIN, GSSAPI, EXTERNAL at least

+     standalone.log.info("Test we have some of the default mechanisms")

+     orig_mechs = standalone.rootdse.supported_sasl()

+     print(orig_mechs)

+     assert('GSSAPI' in orig_mechs)

+     assert('PLAIN' in orig_mechs)

+     assert('EXTERNAL' in orig_mechs)

+ 

+     # Now edit the supported mechs. Check them again.

+     standalone.log.info("Edit mechanisms to allow just PLAIN")

+     standalone.config.set('nsslapd-allowed-sasl-mechanisms', 'PLAIN')

+     limit_mechs = standalone.rootdse.supported_sasl()

+     assert('PLAIN' in limit_mechs)

+     assert('EXTERNAL' in limit_mechs)  # Should always be in the allowed list, even if not set.

+     assert('GSSAPI' not in limit_mechs)  # Should not be there!

+ 

+     # Restart the server a few times and make sure nothing changes

+     standalone.log.info("Restart server and make sure we still have correct allowed mechs")

+     standalone.restart()

+     standalone.restart()

+     limit_mechs = standalone.rootdse.supported_sasl()

+     assert('PLAIN' in limit_mechs)

+     assert('EXTERNAL' in limit_mechs)

+     assert('GSSAPI' not in limit_mechs)

+ 

+     # Set EXTERNAL, even though its always supported

+     standalone.log.info("Edit mechanisms to allow just PLAIN and EXTERNAL")

+     standalone.config.set('nsslapd-allowed-sasl-mechanisms', 'PLAIN, EXTERNAL')

+     limit_mechs = standalone.rootdse.supported_sasl()

+     assert('PLAIN' in limit_mechs)

+     assert('EXTERNAL' in limit_mechs)

+     assert('GSSAPI' not in limit_mechs)

+ 

+     # Now edit the supported mechs. Check them again.

+     standalone.log.info("Edit mechanisms to allow just PLAIN and GSSAPI")

+     standalone.config.set('nsslapd-allowed-sasl-mechanisms', 'PLAIN, GSSAPI')

+     limit_mechs = standalone.rootdse.supported_sasl()

+     assert('PLAIN' in limit_mechs)

+     assert('EXTERNAL' in limit_mechs)

+     assert('GSSAPI' in limit_mechs)

+     assert(len(limit_mechs) == 3)

+ 

+     # Restart server twice and make sure the allowed list is the same

+     standalone.restart()

+     standalone.restart()  # For ticket 49379 (test double restart)

+     limit_mechs = standalone.rootdse.supported_sasl()

+     assert('PLAIN' in limit_mechs)

+     assert('EXTERNAL' in limit_mechs)

+     assert('GSSAPI' in limit_mechs)

+     assert(len(limit_mechs) == 3)

+ 

+     # Add ANONYMOUS to the supported mechs and test again.

+     standalone.log.info("Edit mechanisms to allow just PLAIN, GSSAPI, and ANONYMOUS")

+     standalone.config.set('nsslapd-allowed-sasl-mechanisms', 'PLAIN, GSSAPI, ANONYMOUS')

+     limit_mechs = standalone.rootdse.supported_sasl()

+     assert('PLAIN' in limit_mechs)

+     assert('EXTERNAL' in limit_mechs)

+     assert('GSSAPI' in limit_mechs)

+     assert('ANONYMOUS' in limit_mechs)

+     assert(len(limit_mechs) == 4)

+ 

+     # Restart server and make sure the allowed list is the same

+     standalone.restart()

+     standalone.restart()  # For ticket 49379 (test double restart)

+     limit_mechs = standalone.rootdse.supported_sasl()

+     assert('PLAIN' in limit_mechs)

+     assert('EXTERNAL' in limit_mechs)

+     assert('GSSAPI' in limit_mechs)

+     assert('ANONYMOUS' in limit_mechs)

+     assert(len(limit_mechs) == 4)

+ 

+     # Remove GSSAPI

+     standalone.log.info("Edit mechanisms to allow just PLAIN and ANONYMOUS")

+     standalone.config.set('nsslapd-allowed-sasl-mechanisms', 'PLAIN, ANONYMOUS')

+     limit_mechs = standalone.rootdse.supported_sasl()

+     assert('PLAIN' in limit_mechs)

+     assert('EXTERNAL' in limit_mechs)

+     assert('GSSAPI' not in limit_mechs)

+     assert('ANONYMOUS' in limit_mechs)

+     assert(len(limit_mechs) == 3)

+ 

+     # Restart server and make sure the allowed list is the same

+     standalone.restart()

+     limit_mechs = standalone.rootdse.supported_sasl()

+     assert('PLAIN' in limit_mechs)

+     assert('EXTERNAL' in limit_mechs)

+     assert('GSSAPI' not in limit_mechs)

+     assert('ANONYMOUS' in limit_mechs)

+     assert(len(limit_mechs) == 3)

+ 

+     # Do a config reset

+     standalone.log.info("Reset allowed mechaisms")

+     standalone.config.reset('nsslapd-allowed-sasl-mechanisms')

+ 

+     # check the supported list is the same as our first check.

+     standalone.log.info("Check that we have the original set of mechanisms")

+     final_mechs = standalone.rootdse.supported_sasl()

+     assert(set(final_mechs) == set(orig_mechs))

+ 

+     # Check it after a restart

+     standalone.log.info("Check that we have the original set of mechanisms after a restart")

+     standalone.restart()

+     final_mechs = standalone.rootdse.supported_sasl()

+     assert(set(final_mechs) == set(orig_mechs))

+ 

+ 

+ if __name__ == '__main__':

+     # Run isolated

+     # -s for DEBUG mode

+     CURRENT_FILE = os.path.realpath(__file__)

+     pytest.main("-s %s" % CURRENT_FILE)

@@ -15,9 +15,11 @@

  from lib389.utils import *

  from lib389.sasl import PlainSASL

  from lib389.idm.services import ServiceAccounts

+ from lib389._constants import (SECUREPORT_STANDALONE1, DEFAULT_SUFFIX)

  

  log = logging.getLogger(__name__)

  

+ 

  def test_sasl_plain(topology_st):

  

      standalone = topology_st.standalone
@@ -38,7 +40,7 @@

      standalone.rsa.create()

      # Set the secure port and nsslapd-security

      # Could this fail with selinux?

-     standalone.config.set('nsslapd-secureport', '%s' % SECUREPORT_STANDALONE1 )

+     standalone.config.set('nsslapd-secureport', '%s' % SECUREPORT_STANDALONE1)

      standalone.config.set('nsslapd-security', 'on')

      # Do we need to restart to allow starttls?

      standalone.restart()
@@ -65,12 +67,14 @@

      # I can not solve. I think it's leaking state across connections in start_tls_s?

  

      # Check that it works with TLS

-     conn = standalone.openConnection(saslmethod='PLAIN', sasltoken=auth_tokens, starttls=True, connOnly=True, certdir=standalone.get_cert_dir(), reqcert=ldap.OPT_X_TLS_NEVER)

+     conn = standalone.openConnection(saslmethod='PLAIN', sasltoken=auth_tokens, starttls=True, connOnly=True,

+                                     certdir=standalone.get_cert_dir(), reqcert=ldap.OPT_X_TLS_NEVER)

      conn.close()

  

      # Check that it correct fails our bind if we don't have the password.

      auth_tokens = PlainSASL("dn:%s" % sa.dn, 'password-wrong')

      with pytest.raises(ldap.INVALID_CREDENTIALS):

-         standalone.openConnection(saslmethod='PLAIN', sasltoken=auth_tokens, starttls=False, connOnly=True, certdir=standalone.get_cert_dir(), reqcert=ldap.OPT_X_TLS_NEVER)

+         standalone.openConnection(saslmethod='PLAIN', sasltoken=auth_tokens, starttls=True, connOnly=True,

+                                   certdir=standalone.get_cert_dir(), reqcert=ldap.OPT_X_TLS_NEVER)

  

      # Done!

@@ -19,6 +19,10 @@

      # topology_st.standalone.config.enable_log('audit')

      # topology_st.standalone.config.enable_log('auditfail')

      # This will trigger a mod delete then add.

+ 

+     topology_st.standalone.modify_s('cn=config,cn=ldbm database,cn=plugins,cn=config',

+                                     [(ldap.MOD_REPLACE, 'nsslapd-cache-autosize', '0')])

+ 

      try:

          modlist = [(ldap.MOD_DELETE, 'nsslapd-cachememsize', None), (ldap.MOD_ADD, 'nsslapd-cachememsize', '1')]

          topology_st.standalone.modify_s("cn=%s,cn=ldbm database,cn=plugins,cn=config" % DEFAULT_BENAME,

@@ -32,7 +32,12 @@

      A.agreement.pause(AtoC)

      C.agreement.pause(CtoA)

  

+     # Enable memberOf on Master B

      B.plugins.enable(name=PLUGIN_MEMBER_OF)

+ 

+     # Set the auto OC to an objectclass that does NOT allow memberOf

+     B.modify_s('cn=MemberOf Plugin,cn=plugins,cn=config',

+                [(ldap.MOD_REPLACE, 'memberofAutoAddOC', 'referral')])

      B.restart(timeout=10)

  

      # add a few entries allowing memberof

@@ -2,8 +2,7 @@

  import ldap

  import logging

  import pytest

- from lib389 import DirSrv, Entry, tools, tasks

- from lib389.tools import DirSrvTools

+ from lib389 import Entry

  from lib389._constants import *

  from lib389.properties import *

  from lib389.tasks import *
@@ -19,6 +18,15 @@

  

  USER_DN = 'uid=user,' + DEFAULT_SUFFIX

  ROLE_DN = 'cn=Filtered_Role_That_Includes_Empty_Role,' + DEFAULT_SUFFIX

+ filters = ['nsrole=cn=empty,dc=example,dc=com',

+            '(nsrole=cn=empty,dc=example,dc=com)',

+            '(&(nsrole=cn=empty,dc=example,dc=com))',

+            '(!(nsrole=cn=empty,dc=example,dc=com))',

+            '(&(|(objectclass=person)(sn=app*))(userpassword=*))',

+            '(&(|(objectclass=person)(nsrole=cn=empty,dc=example,dc=com))(userpassword=*))',

+            '(&(|(nsrole=cn=empty,dc=example,dc=com)(sn=app*))(userpassword=*))',

+            '(&(|(objectclass=person)(sn=app*))(nsrole=cn=empty,dc=example,dc=com))',

+            '(&(|(&(cn=*)(objectclass=person)(nsrole=cn=empty,dc=example,dc=com)))(uid=*))']

  

  

  def test_ticket49122(topo):
@@ -29,18 +37,6 @@

      topo.standalone.plugins.enable(name=PLUGIN_ROLES)

      topo.standalone.restart()

  

-     # Add invalid role

-     try:

-         topo.standalone.add_s(Entry((

-             ROLE_DN, {'objectclass': ['top', 'ldapsubentry', 'nsroledefinition',

-                                       'nscomplexroledefinition', 'nsfilteredroledefinition'],

-                       'cn': 'Filtered_Role_That_Includes_Empty_Role',

-                       'nsRoleFilter': '(!(nsrole=cn=This_Is_An_Empty_Managed_NsRoleDefinition,dc=example,dc=com))',

-                       'description': 'A filtered role with filter that will crash the server'})))

-     except ldap.LDAPError as e:

-         topo.standalone.log.fatal('Failed to add filtered role: error ' + e.message['desc'])

-         assert False

- 

      # Add test user

      try:

          topo.standalone.add_s(Entry((
@@ -51,16 +47,39 @@

          assert False

  

      if DEBUGGING:

-         # Add debugging steps(if any)...

          print("Attach gdb")

          time.sleep(20)

  

-     # Search for the role

-     try:

-         topo.standalone.search_s(USER_DN, ldap.SCOPE_SUBTREE, 'objectclass=*', ['nsrole'])

-     except ldap.LDAPError as e:

-         topo.standalone.log.fatal('Search failed: error ' + str(e))

-         assert False

+     # Loop over filters

+     for role_filter in filters:

+         log.info('Testing filter: ' + role_filter)

+ 

+         # Add invalid role

+         try:

+             topo.standalone.add_s(Entry((

+                 ROLE_DN, {'objectclass': ['top', 'ldapsubentry', 'nsroledefinition',

+                                           'nscomplexroledefinition', 'nsfilteredroledefinition'],

+                           'cn': 'Filtered_Role_That_Includes_Empty_Role',

+                           'nsRoleFilter': role_filter,

+                           'description': 'A filtered role with filter that will crash the server'})))

+         except ldap.LDAPError as e:

+             topo.standalone.log.fatal('Failed to add filtered role: error ' + e.message['desc'])

+             assert False

+ 

+         # Search for the role

+         try:

+             topo.standalone.search_s(USER_DN, ldap.SCOPE_SUBTREE, 'objectclass=*', ['nsrole'])

+         except ldap.LDAPError as e:

+             topo.standalone.log.fatal('Search failed: error ' + str(e))

+             assert False

+ 

+         # Cleanup

+         try:

+             topo.standalone.delete_s(ROLE_DN)

+         except ldap.LDAPError as e:

+             topo.standalone.log.fatal('delete failed: error ' + str(e))

+             assert False

+         time.sleep(1)

  

      topo.standalone.log.info('Test Passed')

  

@@ -15,7 +15,10 @@

  else:

      logging.getLogger(__name__).setLevel(logging.INFO)

  log = logging.getLogger(__name__)

+ 

  DEFAULT_LEVEL = "16384"

+ COMB_LEVEL = "73864"  # 65536+8192+128+8 = 73864

+ COMB_DEFAULT_LEVEL = "90248"  # 65536+8192+128+8+16384 = 90248

  

  

  def set_level(topo, level):
@@ -103,6 +106,39 @@

          log.fatal('Connection logging is still on')

          assert False

  

+     # Set a combined level that includes the default level

+     set_level(topo, COMB_DEFAULT_LEVEL)

+     level = get_level(topo)

+     if level != COMB_DEFAULT_LEVEL:

+         log.fatal('Incorrect combined logging level with default level: %s expected %s' %

+                   (level, COMB_DEFAULT_LEVEL))

+         assert False

+ 

+     # Set a combined level that does not includes the default level

+     set_level(topo, COMB_LEVEL)

+     level = get_level(topo)

+     if level != COMB_LEVEL:

+         log.fatal('Incorrect combined logging level without default level: %s expected %s' %

+                   (level, COMB_LEVEL))

+         assert False

+ 

+     # Check our level is present after a restart - previous level was COMB_LEVEL

+     topo.standalone.restart()

+     log_size = get_log_size(topo)  # Grab the log size for our next check

+     level = get_level(topo)  # This should trigger connection logging

+     if level != COMB_LEVEL:

+         log.fatal('Incorrect combined logging level with default level: %s expected %s' %

+                   (level, COMB_LEVEL))

+         assert False

+ 

+     # Now check the actual levels are still working

+     new_size = get_log_size(topo)

+     if new_size == log_size:

+         # Size should be different

+         log.fatal('Combined logging is not working')

+         assert False

+ 

+ 

  if __name__ == '__main__':

      # Run isolated

      # -s for DEBUG mode

@@ -0,0 +1,140 @@

+ import time

+ import ldap

+ import logging

+ import pytest

+ from lib389 import DirSrv, Entry, tools, tasks

+ from lib389.tools import DirSrvTools

+ from lib389._constants import *

+ from lib389.properties import *

+ from lib389.tasks import *

+ from lib389.utils import *

+ from lib389.topologies import topology_st as topo

+ 

+ DEBUGGING = os.getenv("DEBUGGING", default=False)

+ if DEBUGGING:

+     logging.getLogger(__name__).setLevel(logging.DEBUG)

+ else:

+     logging.getLogger(__name__).setLevel(logging.INFO)

+ log = logging.getLogger(__name__)

+ 

+ COS_BRANCH = 'ou=cos_scope,' + DEFAULT_SUFFIX

+ COS_DEF = 'cn=cos_definition,' + COS_BRANCH

+ COS_TEMPLATE = 'cn=cos_template,' + COS_BRANCH

+ INVALID_USER_WITH_COS = 'cn=cos_user_no_mail,' + COS_BRANCH

+ VALID_USER_WITH_COS = 'cn=cos_user_with_mail,' + COS_BRANCH

+ 

+ NO_COS_BRANCH = 'ou=no_cos_scope,' + DEFAULT_SUFFIX

+ INVALID_USER_WITHOUT_COS = 'cn=no_cos_user_no_mail,' + NO_COS_BRANCH

+ VALID_USER_WITHOUT_COS = 'cn=no_cos_user_with_mail,' + NO_COS_BRANCH

+ 

+ def test_ticket49249(topo):

+     """Write your testcase here...

+ 

+     Also, if you need any testcase initialization,

+     please, write additional fixture for that(include finalizer).

+     """

+     # Add the branches

+     try:

+         topo.standalone.add_s(Entry((COS_BRANCH, {

+             'objectclass': 'top extensibleObject'.split(),

+             'ou': 'cos_scope'

+         })))

+     except ldap.LDAPError as e:

+         log.error('Failed to add cos_scope: error ' + e.message['desc'])

+         assert False

+ 

+     try:

+         topo.standalone.add_s(Entry((NO_COS_BRANCH, {

+             'objectclass': 'top extensibleObject'.split(),

+             'ou': 'no_cos_scope'

+         })))

+     except ldap.LDAPError as e:

+         log.error('Failed to add no_cos_scope: error ' + e.message['desc'])

+         assert False

+ 

+     try:

+         topo.standalone.add_s(Entry((COS_TEMPLATE, {

+             'objectclass': 'top ldapsubentry costemplate extensibleObject'.split(),

+             'cn': 'cos_template',

+             'cosPriority': '1',

+             'cn': 'cn=nsPwTemplateEntry,ou=level1,dc=example,dc=com',

+             'mailAlternateAddress': 'hello@world'

+         })))

+     except ldap.LDAPError as e:

+         log.error('Failed to add cos_template: error ' + e.message['desc'])

+         assert False

+ 

+     try:

+         topo.standalone.add_s(Entry((COS_DEF, {

+             'objectclass': 'top ldapsubentry cosSuperDefinition cosPointerDefinition'.split(),

+             'cn': 'cos_definition',

+             'costemplatedn': COS_TEMPLATE,

+             'cosAttribute': 'mailAlternateAddress default'

+         })))

+     except ldap.LDAPError as e:

+         log.error('Failed to add cos_definition: error ' + e.message['desc'])

+         assert False

+ 

+     try:

+         # This entry is not allowed to have mailAlternateAddress

+         topo.standalone.add_s(Entry((INVALID_USER_WITH_COS, {

+             'objectclass': 'top person'.split(),

+             'cn': 'cos_user_no_mail',

+             'sn': 'cos_user_no_mail'

+         })))

+     except ldap.LDAPError as e:

+         log.error('Failed to add cos_user_no_mail: error ' + e.message['desc'])

+         assert False

+ 

+     try:

+         # This entry is allowed to have mailAlternateAddress

+         topo.standalone.add_s(Entry((VALID_USER_WITH_COS, {

+             'objectclass': 'top mailGroup'.split(),

+             'cn': 'cos_user_with_mail'

+         })))

+     except ldap.LDAPError as e:

+         log.error('Failed to add cos_user_no_mail: error ' + e.message['desc'])

+         assert False

+ 

+     try:

+         # This entry is not allowed to have mailAlternateAddress

+         topo.standalone.add_s(Entry((INVALID_USER_WITHOUT_COS, {

+             'objectclass': 'top person'.split(),

+             'cn': 'no_cos_user_no_mail',

+             'sn': 'no_cos_user_no_mail'

+         })))

+     except ldap.LDAPError as e:

+         log.error('Failed to add no_cos_user_no_mail: error ' + e.message['desc'])

+         assert False

+ 

+     try:

+         # This entry is  allowed to have mailAlternateAddress

+         topo.standalone.add_s(Entry((VALID_USER_WITHOUT_COS, {

+             'objectclass': 'top mailGroup'.split(),

+             'cn': 'no_cos_user_with_mail'

+         })))

+     except ldap.LDAPError as e:

+         log.error('Failed to add no_cos_user_with_mail: error ' + e.message['desc'])

+         assert False

+ 

+     try:

+         entries = topo.standalone.search_s(SUFFIX, ldap.SCOPE_SUBTREE, '(mailAlternateAddress=*)')

+         assert len(entries) == 1

+         assert entries[0].hasValue('mailAlternateAddress', 'hello@world')

+     except ldap.LDAPError as e:

+         log.fatal('Unable to retrieve cos_user_with_mail (only entry with mailAlternateAddress) : error %s' % (USER1_DN, e.message['desc']))

+         assert False

+ 

+     assert not topo.standalone.ds_error_log.match(".*cos attribute mailAlternateAddress failed schema.*")

+ 

+     if DEBUGGING:

+         # Add debugging steps(if any)...

+         pass

+ 

+ 

+ if __name__ == '__main__':

+     # Run isolated

+     # -s for DEBUG mode

+     CURRENT_FILE = os.path.realpath(__file__)

+     pytest.main("-s %s" % CURRENT_FILE)

+ 

@@ -0,0 +1,50 @@

+ # --- BEGIN COPYRIGHT BLOCK ---

+ # Copyright (C) 2017 Red Hat, Inc.

+ # All rights reserved.

+ #

+ # License: GPL (version 3 or any later version).

+ # See LICENSE for details.

+ # --- END COPYRIGHT BLOCK ---

+ #

+ 

+ import pytest

+ import ldap

+ 

+ from lib389.topologies import topology_st

+ # This pulls in logging I think

+ from lib389.utils import *

+ from lib389.sasl import PlainSASL

+ from lib389.idm.services import ServiceAccounts

+ 

+ log = logging.getLogger(__name__)

+ 

+ def test_49273_corrupt_dbversion(topology_st):

+     """

+     ticket 49273 was caused by a disk space full, which corrupted

+     the users DBVERSION files. We can't prevent this, but we can handle

+     the error better than "crash".

+     """

+ 

+     standalone = topology_st.standalone

+ 

+     # Stop the instance

+     standalone.stop()

+     # Corrupt userRoot dbversion

+     dbvf = os.path.join(standalone.ds_paths.db_dir, 'userRoot/DBVERSION')

+     with open(dbvf, 'w') as f:

+         # This will trunc the file

+         f.write('')

+     # Start up

+     try:

+         # post_open false, means ds state is OFFLINE, which allows

+         # dspaths below to use defaults rather than ldap check.

+         standalone.start(timeout=20, post_open=False)

+     except:

+         pass

+     # Trigger an update of the running server state, to move it OFFLINE.

+     standalone.status()

+ 

+     # CHeck error log?

+     error_lines = standalone.ds_error_log.match('.*Could not parse file.*')

+     assert(len(error_lines) > 0)

+ 

@@ -0,0 +1,79 @@

+ import logging

+ import pytest

+ import os

+ import time

+ import ldap

+ from lib389._constants import *

+ from lib389.topologies import topology_st as topo

+ from lib389 import Entry

+ 

+ DEBUGGING = os.getenv("DEBUGGING", default=False)

+ if DEBUGGING:

+     logging.getLogger(__name__).setLevel(logging.DEBUG)

+ else:

+     logging.getLogger(__name__).setLevel(logging.INFO)

+ log = logging.getLogger(__name__)

+ 

+ 

+ USER_CN='user_'

+ def _user_get_dn(no):

+     cn = '%s%d' % (USER_CN, no)

+     dn = 'cn=%s,ou=people,%s' % (cn, SUFFIX)

+     return (cn, dn)

+ 

+ def add_user(server, no, desc='dummy', sleep=True):

+     (cn, dn) = _user_get_dn(no)

+     log.fatal('Adding user (%s): ' % dn)

+     server.add_s(Entry((dn, {'objectclass': ['top', 'person', 'inetuser', 'userSecurityInformation'],

+                              'cn': [cn],

+                              'description': [desc],

+                              'sn': [cn],

+                              'description': ['add on that host']})))

+     if sleep:

+         time.sleep(2)

+ 

+ def test_ticket49471(topo):

+     """Specify a test case purpose or name here

+ 

+     :id: 457ab172-9455-4eb2-89a0-150e3de5993f

+     :setup: Fill in set up configuration here

+     :steps:

+         1. Fill in test case steps here

+         2. And indent them like this (RST format requirement)

+     :expectedresults:

+         1. Fill in the result that is expected

+         2. For each test step

+     """

+ 

+     # If you need any test suite initialization,

+     # please, write additional fixture for that (including finalizer).

+     # Topology for suites are predefined in lib389/topologies.py.

+ 

+     # If you need host, port or any other data about instance,

+     # Please, use the instance object attributes for that (for example, topo.ms["master1"].serverid)

+ 

+     S1 = topo.standalone

+     add_user(S1, 1)

+ 

+     Filter = "(description:2.16.840.1.113730.3.3.2.1.1.6:=\*on\*)"

+     ents = S1.search_s(SUFFIX, ldap.SCOPE_SUBTREE, Filter)

+     assert len(ents) == 1

+ 

+     #

+     # The following is for the test 49491

+     # skipped here else it crashes in ASAN

+     #Filter = "(description:2.16.840.1.113730.3.3.2.1.1.6:=\*host)"

+     #ents = S1.search_s(SUFFIX, ldap.SCOPE_SUBTREE, Filter)

+     #assert len(ents) == 1

+ 

+     if DEBUGGING:

+         # Add debugging steps(if any)...

+         pass

+ 

+ 

+ if __name__ == '__main__':

+     # Run isolated

+     # -s for DEBUG mode

+     CURRENT_FILE = os.path.realpath(__file__)

+     pytest.main("-s %s" % CURRENT_FILE)

+ 

file modified
+48 -17
@@ -809,9 +809,9 @@

  print "Restarts:                     $serverRestartCount\n";

  

  if(%cipher){

- 	print " Secure Protocol Versions:\n";

+ 	print "Secure Protocol Versions:\n";

  	foreach my $key (sort { $b cmp $a } keys %cipher) {

- 		print "  - $key - $cipher{$key}\n";

+ 		print "  - $key ($cipher{$key} connections)\n";

  	}

  	print "\n";

  }
@@ -1099,23 +1099,23 @@

  print "\n";

  print "Binds:                        $bindCount\n";

  print "Unbinds:                      $unbindCount\n";

+ print "------------------------------";

+ print "-" x length $bindCount;

+ print "\n";

  print " - LDAP v2 Binds:             $v2BindCount\n";

  print " - LDAP v3 Binds:             $v3BindCount\n";

- print " - AUTOBINDs:                 $autobindCount\n";

+ print " - AUTOBINDs(LDAPI):          $autobindCount\n";

  print " - SSL Client Binds:          $sslClientBindCount\n";

  print " - Failed SSL Client Binds:   $sslClientFailedCount\n";

  print " - SASL Binds:                $saslBindCount\n";

  if ($saslBindCount > 0){

  	my $saslmech = $hashes->{saslmech};

  	foreach my $saslb ( sort {$saslmech->{$b} <=> $saslmech->{$a} } (keys %{$saslmech}) ){

- 		printf "    %-4s - %s\n",$saslb, $saslmech->{$saslb};

+ 		printf "   - %-4s: %s\n",$saslb, $saslmech->{$saslb};

  	}

  }

- 

  print " - Directory Manager Binds:   $rootDNBindCount\n";

  print " - Anonymous Binds:           $anonymousBindCount\n";

- my $otherBindCount = $bindCount -($rootDNBindCount + $anonymousBindCount);

- print " - Other Binds:               $otherBindCount\n\n";

  

  ##########################################################################

  #                       Verbose Logging Section                          #
@@ -1195,9 +1195,9 @@

  }

  

  ####################################

- #			   #

+ #                                  #

  #     Print Failed Logins          #

- #				   #

+ #                                  #

  ####################################

  

  if ($verb eq "yes" || $usage =~ /f/ ){
@@ -1754,7 +1754,7 @@

  			($end) = $endTime =~ /\D*(\S*)/;

  		}

  	}

- 	if ($_ =~ /connection from *([0-9A-Fa-f\.\:]+)/i ) {

+ 	if ($_ =~ /connection from *([0-9A-Za-z\.\:]+)/i ) {

  		my $skip = "yes";

  		for (my $excl =0; $excl < $#excludeIP; $excl++){

  			if ($excludeIP[$excl] eq $1){
@@ -2085,7 +2085,7 @@

  	}

  	if (m/ connection from/){

  		my $ip;

- 		if ($_ =~ /connection from *([0-9A-Fa-f\.\:]+)/i ){

+ 		if ($_ =~ /connection from *([0-9A-Za-z\.\:]+)/i ){

  			$ip = $1;

  			for (my $xxx =0; $xxx < $#excludeIP; $xxx++){

  				if ($excludeIP[$xxx] eq $ip){$exc = "yes";}
@@ -2117,7 +2117,7 @@

  		($connID) = $_ =~ /conn=(\d*)\s/;

  		handleConnClose($connID);

  	}

- 	if (m/ BIND/ && $_ =~ /dn=\"(.*)\" method=128/i ){

+ 	if (m/ BIND / && $_ =~ /dn=\"(.*)\" method=128/i ){

  		my $binddn = $1;

  		if($reportStats){ inc_stats('bind',$s_stats,$m_stats); }

  		$bindCount++;
@@ -2253,7 +2253,7 @@

  	}

  	if ($usage =~ /g/ || $usage =~ /c/ || $usage =~ /i/ || $usage =~ /f/ || $usage =~ /u/ || $usage =~ /U/ || $verb eq "yes"){

  		$exc = "no";

- 		if ($_ =~ /connection from *([0-9A-fa-f\.\:]+)/i ) {

+ 		if ($_ =~ /connection from *([0-9A-Za-z\.\:]+)/i ) {

  			for (my $xxx = 0; $xxx < $#excludeIP; $xxx++){

  				if ($1 eq $excludeIP[$xxx]){

  					$exc = "yes";
@@ -2531,25 +2531,56 @@

  			}

  		}

  	}

- 	if (/ BIND / && /method=sasl/i){

+ 	if (/ BIND / && $_ =~ /dn=\"(.*)\" method=sasl/i){

+ 		my $binddn = $1;

+ 		my ($conn, $op);

  		$saslBindCount++;

+ 		$bindCount++;

  		if ($_ =~ /mech=(.*)/i ){

  			my $mech = $1;

  			$hashes->{saslmech}->{$mech}++;

- 			my ($conn, $op);

  			if ($_ =~ /conn= *([0-9A-Z]+) +op= *([0-9\-]+)/i){

  				$conn = $1;

  				$op = $2;

  				$hashes->{saslconnop}->{$conn-$op} = $mech;

  			}

  		}

- 		if (/ mech=ANONYMOUS/){

- 			$anonymousBindCount++;

+ 		if ($binddn ne ""){

+ 			if($binddn eq $rootDN){ $rootDNBindCount++; }

+ 			if($usage =~ /f/ || $usage =~ /u/ || $usage =~ /U/ || $usage =~ /b/ || $verb eq "yes"){

+ 				$tmpp = $binddn;

+ 				$tmpp =~ tr/A-Z/a-z/;

+ 				$hashes->{bindlist}->{$tmpp}++;

+ 				$hashes->{bind_conn_op}->{"$serverRestartCount,$conn,$op"} = $tmpp;

+ 			}

+ 		}

+ 	}

+ 	if (/ RESULT err=/ && / tag=97 nentries=0 etime=/ && $_ =~ /dn=\"(.*)\"/i){

+ 		# Check if this is a sasl bind, if see we need to add the RESULT's dn as a bind dn

+ 		my $binddn = $1;

+ 		my ($conn, $op);

+ 		if ($_ =~ /conn= *([0-9A-Z]+) +op= *([0-9\-]+)/i){

+ 			$conn = $1;

+ 			$op = $2;

+ 			if (exists $hashes->{saslconnop}->{$conn-$op} && $hashes->{saslconnop}->{$conn-$op} ne ""){

+ 				# This was a SASL BIND - record the dn

+ 				if ($binddn ne ""){

+ 					if($binddn eq $rootDN){ $rootDNBindCount++; }

+ 					if($usage =~ /f/ || $usage =~ /u/ || $usage =~ /U/ || $usage =~ /b/ || $verb eq "yes"){

+ 						$tmpp = $binddn;

+ 						$tmpp =~ tr/A-Z/a-z/;

+ 						$hashes->{bindlist}->{$tmpp}++;

+ 						$hashes->{bind_conn_op}->{"$serverRestartCount,$conn,$op"} = $tmpp;

+ 					}

+ 				}

+ 			}

  		}

  	}

  	if (/ RESULT err=14 tag=97 / && / SASL bind in progress/){

  		# Drop the sasl bind count since this is step in the bind process

  		$saslBindCount--;

+ 		$bindCount--;

+ 		$v3BindCount--;

  		my ($conn, $op);

  		if ($_ =~ /conn= *([0-9A-Z]+) +op= *([0-9\-]+)/i){

  			$conn = $1;

@@ -41,9 +41,11 @@

      my $bigfmt64 = "NNA6nC";

      my $fmt = $fmt32;

      my $bigfmt = $bigfmt32;

+     my $packfmt = $packfmt32;

      if (length($val) > 20) {

          $fmt = $fmt64;

          $bigfmt = $bigfmt64;

+         $packfmt = $packfmt64;

      } elsif ($is64) {

          # cannot convert 32-bit to 64-bit - just delete the entry and continue

          debug(1, "Cannot convert 32-bit nsState value $hex to 64-bit - deleting entry " .
@@ -59,7 +61,6 @@

          $packfmt64 = "NNA6nCx7";

      }

  

-     my $packfmt = $packfmt32;

      if ($is64) {

          $packfmt = $packfmt64;

      }

@@ -0,0 +1,12 @@

+ dn: cn=PBKDF2_SHA256,cn=Password Storage Schemes,cn=plugins,cn=config

+ objectclass: top

+ objectclass: nsSlapdPlugin

+ cn: PBKDF2_SHA256

+ nsslapd-pluginpath: libpwdstorage-plugin

+ nsslapd-plugininitfunc: pbkdf2_sha256_pwd_storage_scheme_init

+ nsslapd-plugintype: pwdstoragescheme

+ nsslapd-pluginenabled: on

+ nsslapd-pluginDescription: DESC

+ nsslapd-pluginVersion: PACKAGE_VERSION

+ nsslapd-pluginVendor: VENDOR

+ nsslapd-pluginid: ID

@@ -13,11 +13,12 @@

  

  usage()

  {

-     echo "Usage: db2bak [archivedir] [-Z serverID] [-q] [-h]"

+     echo "Usage: db2bak [archivedir] [-Z serverID] [-q] [-v] [-h]"

      echo "Options:"

      echo "        archivedir   - Directory where the backup should be stored"

      echo "        -Z serverID  - Server instance identifier"

      echo "        -q           - Quiet mode - suppresses output"

+     echo "        -v           - Display version"

      echo "        -h           - Display usage"  

  }

  

@@ -25,7 +25,7 @@

  $i = 0;

  

  sub usage {

-     print(STDERR "Usage: db2bak.pl [-v] [-Z serverID] [-D rootdn] { -w password | -w - | -j filename } [-a backupdir]\n");

+     print(STDERR "Usage: db2bak.pl [-Z serverID] [-D rootdn] { -w password | -w - | -j filename } [-a backupdir]\n");

      print(STDERR "                 [-t dbtype] [-P protocol] [-h]\n");

      print(STDERR "Options:\n");

      print(STDERR "        -D rootdn     - Directory Manager\n");
@@ -33,8 +33,8 @@

      print(STDERR "        -w -          - Prompt for Directory Manager's password\n");

      print(STDERR "        -Z serverID   - Server instance identifier\n");

      print(STDERR "        -j filename   - Read Directory Manager's password from file\n");

-     print(STDERR "        -A backupdir  - Backup directory (backupdir/ID-<date_time>)\n");

-     print(STDERR "        -a backupdir  - Backup directory\n");

+     print(STDERR "        -A backupdir  - Backup directory symlink(backupdir/ID-<date_time>)\n");

+     print(STDERR "        -a backupdir  - Backup directory symlink\n");

      print(STDERR "        -t dbtype     - Database type (default: ldbm database)\n");

      print(STDERR "        -P protocol   - STARTTLS, LDAPS, LDAPI, LDAP (default: uses most secure protocol available)\n");

      print(STDERR "        -h            - Display usage\n");
@@ -105,7 +105,12 @@

      } else {

          $symname = $archivedir;

      }

-     print("Back up directory: $archivedir\n");

+     if ($symname eq "") {

+         print("Back up directory: $archivedir\n");

+     } else {

+         print("Back up directory: $archivedir -> $mybakdir/$archivebase\n");

+     }

+ 

      # If an archive dir is specified, create it as a symlink pointing

      # to the default backup dir not to violate the selinux policy.

      $archivedir = "${mybakdir}/${archivebase}";

@@ -14,7 +14,7 @@

  usage ()

  {

      echo "Usage: db2index [-Z serverID] [-n backend | {-s includesuffix}* -t attribute[:indextypes[:matchingrules]]"

-     echo "                -T vlvTag] [-h]"

+     echo "                -T vlvTag] [-v] [-h]"

      echo "Options:"

      echo "        -Z serverID       - Server instance identifier"

      echo "        -n backend        - Backend database name.  Example: userRoot"
@@ -26,6 +26,7 @@

      echo "                          - matchingrules: comma separated matrules"

      echo "                                 Example: -t foo:eq,pres"

      echo "        -T vlvTag         - VLV index name"

+     echo "        -v                - Display version"

      echo "        -h                - Display usage" 

  }

  
@@ -52,6 +53,7 @@

      esac

  done

  

+ argnum=$#

  shift $(($OPTIND - 1))

  if [ $1 ]

  then
@@ -71,18 +73,18 @@

  

  idxall=0

  print_usage=0

- if [ -z $servid ] && [ $# -eq 0 ]; then

+ if [ -z $servid ] && [ $argnum -eq 0 ]; then

      idxall=1

- elif [ "$servid" ] && [ $# -eq 2 ]; then

+ elif [ "$servid" ] && [ $argnum -eq 2 ]; then

      idxall=1

  elif [ -z $benameopt ] && [ -z $includeSuffix ]; then

      print_usage=1

  fi

- if [ -z $servid ] && [ $# -lt 2 ]; then

+ if [ -z $servid ] && [ $argnum -lt 2 ]; then

      print_usage=1

- elif [ -n "$servid" ] && [ $# -lt 4 ]; then

+ elif [ -n "$servid" ] && [ $argnum -lt 4 ]; then

      print_usage=1

- elif [ -n "$servid" ] && [ $# -eq 4 ]; then

+ elif [ -n "$servid" ] && [ $argnum -eq 4 ]; then

      idxall=1

  fi

  

@@ -16,7 +16,7 @@

  usage()

  {

      echo "Usage: db2ldif [-Z serverID] {-n backend_instance}* | {-s includesuffix}* [{-x excludesuffix}*] [-a outputfile]"

-     echo "               [-E] [-r] [-u] [-U] [-m] [-1] [-q] [-h]"

+     echo "               [-E] [-r] [-u] [-U] [-m] [-1] [-q] [-v] [-h]"

      echo "Note: either \"-n backend\" or \"-s includesuffix\" is required."

      echo "Options:"

      echo "        -Z serverID       - Server instance identifier"
@@ -31,6 +31,7 @@

      echo "        -m                - Do not base64 encode values"

      echo "        -1                - Do not include version text"

      echo "        -q                - Quiet mode - suppresses output"

+     echo "        -v                - Display version"

      echo "        -h                - Display usage" 

  }

  

@@ -38,7 +38,7 @@

  $cwd = cwd();

  

  sub usage {

-     print(STDERR "Usage: db2ldif.pl [-v] [-Z serverID] [-D rootdn] { -w password | -w - | -j pwfilename }\n");

+     print(STDERR "Usage: db2ldif.pl [-Z serverID] [-D rootdn] { -w password | -w - | -j pwfilename }\n");

      print(STDERR "                  [-P protocol] {-n backendname}* | {-s include}* [{-x exclude}*] [-h]\n");

      print(STDERR "                  [-a filename] [-m] [-M] [-r] [-u] [-C] [-N] [-U] [-E] [-1] [-a filename]\n");

      print(STDERR "Options:\n");

@@ -146,7 +146,8 @@

              if e.errno == errno.EINTR:

                  continue # open was interrupted, try again

              else: # hard error

-                 raise Exception("%s [%d]" % (e.strerror, e.errno))

+                 print("%s [%d]" % (e.strerror, e.errno))

+                 sys.exit(1)

      return logf

  

  def is_proc_alive(procpid):
@@ -156,7 +157,8 @@

      except IOError as e:

          if e.errno != errno.ENOENT: # may not exist yet - that's ok

              # otherwise, probably permissions or other badness

-             raise Exception("could not open file %s - %s [%d]" % (procfile, e.strerror, e.errno))

+             print("could not open file %s - %s [%d]" % (procfile, e.strerror, e.errno))

+             sys.exit(1)

      # using /proc/pid failed, try kill

      if not retval:

          try:
@@ -177,7 +179,8 @@

          except IOError as e:

              if e.errno != errno.ENOENT: # may not exist yet - that's ok

                  # otherwise, probably permissions or other badness

-                 raise Exception("Could not read pid from file %s - %s [%d]" % (pidfile, e.strerror, e.errno))

+                 print("Could not read pid from file %s - %s [%d]" % (pidfile, e.strerror, e.errno))

+                 sys.exit(1)

          if line:

              procpid = int(line)

      return procpid
@@ -188,7 +191,8 @@

          pfd.write("%d\n" % os.getpid())

          pfd.close()

      except IOError as e:

-         raise Exception("Could not write pid to file %s - %s [%d]" % (pidfile, e.strerror, e.errno))

+         print("Could not write pid to file %s - %s [%d]" % (pidfile, e.strerror, e.errno))

+         sys.exit(1)

  

  def handle_script_pidfile(scriptpidfile):

      scriptpid = get_pid_from_file(scriptpidfile)
@@ -216,7 +220,8 @@

              if e.errno == errno.EINTR:

                  continue # read was interrupted, try again

              else: # hard error

-                 raise Exception("%s [%d]" % (e.strerror, e.errno))

+                 print("%s [%d]" % (e.strerror, e.errno))

+                 sys.exit(1)

      if line: # read something

          for plgfunc in plgfuncs:

              if not plgfunc(line):
@@ -312,7 +317,8 @@

              print("Failed to create log pipe: " + str(e))

              sys.exit(1)

      else:

-         raise Exception("%s [%d]" % (e.strerror, e.errno))

+         print("Failed to create log pipe - %s [error %d]" % (e.strerror, e.errno))

+         sys.exit(1)

  

  if debug:

      print("Listening to log pipe", logfname, "number of lines", maxlines)

@@ -13,7 +13,7 @@

  

  usage ()

  {

-     echo "Usage: vlvindex [-Z serverID] -n backendname | {-s includesuffix}* -T vlvTag [-d debuglevel] [-h]"

+     echo "Usage: vlvindex [-Z serverID] -n backendname | {-s includesuffix}* -T vlvTag [-d debuglevel] [-v] [-h]"

      echo "Note: either \"-n backend\" or \"-s includesuffix\" are required."

      echo "Options:"

      echo "        -Z serverID        - Server instance identifier"
@@ -21,6 +21,7 @@

      echo "        -s includessuffix  - Suffix to index"

      echo "        -T vlvTag          - VLV index name"

      echo "        -d debuglevel      - Debugging level"

+     echo "        -v                 - Display version"

      echo "        -h                 - Display usage"

  }

  

@@ -761,7 +761,7 @@

  objectclass: nsSlapdPlugin

  objectclass: rootDNPluginConfig

  cn: RootDN Access Control

- nsslapd-pluginpath: librootdn-access-plugin.so

+ nsslapd-pluginpath: librootdn-access-plugin

  nsslapd-plugininitfunc: rootdn_init

  nsslapd-plugintype: internalpreoperation

  nsslapd-pluginenabled: off

@@ -1083,17 +1083,30 @@

  			*/

  			if ( aclpb->aclpb_state & ACLPB_FOUND_A_ENTRY_TEST_RULE){

  				/* Do I have access on the entry  itself */

- 				if (acl_access_allowed (pb, e, NULL, 

- 						NULL, access) != LDAP_SUCCESS) {

+ 				if (acl_access_allowed (pb, e, NULL, NULL, access) != LDAP_SUCCESS) {

  					/* How was I denied ? 

  					** I could be denied on a DENY rule or because 

  					** there is no allow rule. If it's a DENY from

  					** a DENY rule, then we don't have access to 

  					** the entry ( nice trick to get in )

  					*/

- 					if ( aclpb->aclpb_state & 

- 							ACLPB_EXECUTING_DENY_HANDLES)

- 						return LDAP_INSUFFICIENT_ACCESS;

+                     if (aclpb->aclpb_state & ACLPB_EXECUTING_DENY_HANDLES) {

+                         aclEvalContext *c_ContextEval = &aclpb->aclpb_curr_entryEval_context;

+                         AclAttrEval *c_attrEval = NULL;

+                         /*

+                          * The entire entry is blocked, but previously evaluated allow aci's might

+                          * show some of the attributes as readable in the acl cache, so reset all

+                          * the cached attributes' status to FAIL.

+                          */

+                         for (size_t j = 0; j < c_ContextEval->acle_numof_attrs; j++) {

+                             c_attrEval = &c_ContextEval->acle_attrEval[j];

+                             c_attrEval->attrEval_r_status &= ~ACL_ATTREVAL_SUCCESS;

+                             c_attrEval->attrEval_r_status |= ACL_ATTREVAL_FAIL;

+                             c_attrEval->attrEval_s_status &= ~ACL_ATTREVAL_SUCCESS;

+                             c_attrEval->attrEval_s_status |= ACL_ATTREVAL_FAIL;

+                         }

+                         return LDAP_INSUFFICIENT_ACCESS;

+                     }

  					

  					/* The other case is I don't have an

  					** explicit allow rule -- which is fine.
@@ -2914,6 +2927,11 @@

  		result_reason->deciding_aci = NULL;

  		result_reason->reason = ACL_REASON_NO_MATCHED_RESOURCE_ALLOWS;

  

+         /* If we have deny handles we should process them */

+         if (aclpb->aclpb_num_deny_handles > 0) {

+             aclpb->aclpb_state &= ~ACLPB_EXECUTING_ALLOW_HANDLES;

+             aclpb->aclpb_state |= ACLPB_EXECUTING_DENY_HANDLES;

+         }

  		TNF_PROBE_1_DEBUG(acl__TestRights_end,"ACL","",

  							tnf_string,no_allows,"");	

  

@@ -806,9 +806,6 @@

  void        acl_destroy_aclpb_pool(void);

  int		acl_skip_access_check ( Slapi_PBlock *pb,  Slapi_Entry *e, int access );

  

- int                     aclext_alloc_lockarray(void);

- void              aclext_free_lockarray(void);

- 

  int			aclutil_str_append(char **str1, const char *str2);

  void		aclutil_print_err (int rv , const Slapi_DN *sdn,

  			const struct berval* val, char **errbuf);

@@ -22,7 +22,6 @@

  static int acl__put_aclpb_back_to_pool ( Acl_PBlock *aclpb );

  static Acl_PBlock * acl__malloc_aclpb(void);

  static void acl__free_aclpb ( Acl_PBlock **aclpb_ptr);

- static PRLock *aclext_get_lock(void);

  

  

  struct acl_pbqueue {
@@ -120,73 +119,6 @@

  	}

  }

  

- /****************************************************************************

-  * Global lock array so that private extension between connection and operation 

-  * co-exist

-  *

-  ******************************************************************************/

- struct ext_lockArray {

- 	PRLock 		**lockArray;

- 	int		 	numlocks;

- };

- 

- static struct ext_lockArray extLockArray;

- 

- /* PKBxxx: make this a configurable. Start with 2 * maxThreads */

- #define ACLEXT_MAX_LOCKS 40

- 

- int

- aclext_alloc_lockarray ( )

- {

- 

- 	int		i;

- 	PRLock	*lock;

- 

- 	extLockArray.lockArray = 

- 			(PRLock **) slapi_ch_calloc ( ACLEXT_MAX_LOCKS, sizeof ( PRLock *) );

- 

- 	for ( i = 0; i < ACLEXT_MAX_LOCKS; i++) {

- 		if (NULL == (lock = PR_NewLock()) ) {

- 			slapi_log_err(SLAPI_LOG_ERR, plugin_name, 

- 			   "aclext_alloc_lockarray - Unable to allocate locks used for private extension\n");

- 			return 1;

- 		}

- 		extLockArray.lockArray[i] = lock;

- 	}

- 	extLockArray.numlocks = ACLEXT_MAX_LOCKS;

- 	return 0;

- }

- 

- /*

-  * Not in use! The connection table still has references to these locks.

-  * We need to free these after freeing the connection table in daemon.c

-  */

- void

- aclext_free_lockarray()

- {

- 	int i = 0;

- 

- 	for ( i = 0; i < ACLEXT_MAX_LOCKS; i++) {

- 	    if(extLockArray.lockArray[i]){

- 	         PR_DestroyLock(extLockArray.lockArray[i]);

- 	         extLockArray.lockArray[i] = NULL;

- 	    }

- 	}

- 

- 	slapi_ch_free((void **)&extLockArray.lockArray);

- }

- 

- static PRUint32 slot_id =0;

- 

- static PRLock *

- aclext_get_lock(void)

- {

- 

- 	PRUint16 slot = slot_id % ACLEXT_MAX_LOCKS;

- 	slot_id++;

- 	return ( extLockArray.lockArray[slot] );

- 

- }

  /****************************************************************************/

  /* CONNECTION EXTENSION SPECIFIC											*/

  /****************************************************************************/
@@ -195,7 +127,7 @@

  {

  	struct acl_cblock *ext = NULL;

  	ext = (struct acl_cblock * ) slapi_ch_calloc (1, sizeof (struct acl_cblock ) );

- 	if (( ext->aclcb_lock = aclext_get_lock () ) == NULL ) {

+     if ((ext->aclcb_lock = PR_NewLock()) == NULL) {

   		slapi_log_err(SLAPI_LOG_ERR, plugin_name,

                		"acl_conn_ext_constructor - Unable to get Read/Write lock for CONNECTION extension\n");

  		slapi_ch_free ( (void **) &ext );
@@ -229,6 +161,7 @@

  	aclcb->aclcb_lock = NULL;

  	slapi_ch_free ( (void **) &aclcb );

  	PR_Unlock ( shared_lock );

+     PR_DestroyLock(shared_lock);

  }

  

  /****************************************************************************/

@@ -273,6 +273,39 @@

  }

  

  static void

+ _ger_swap_aclcb(struct acl_cblock *cb_1, struct acl_cblock *cb_2)

+ {

+     short swap_aclcb_aclsignature;

+     short swap_aclcb_state;

+     Slapi_DN *swap_aclcb_sdn;

+     aclEvalContext swap_aclcb_eval_context;

+ 

+     /* swap the data of two acl cblocks

+      * while holding th elock of one of them. This allows

+      * to temporarily use different content in the cblock

+      * without freeing data that might be accessed by another thread

+      * Since the lock in one cblock is used to give exclusive access

+      * the locks will not be swapped

+      */

+     swap_aclcb_aclsignature = cb_1->aclcb_aclsignature;

+     swap_aclcb_state = cb_1->aclcb_state;

+     swap_aclcb_sdn = cb_1->aclcb_sdn;

+     swap_aclcb_eval_context = cb_1->aclcb_eval_context;

+ 

+     cb_1->aclcb_aclsignature = cb_2->aclcb_aclsignature;

+     cb_1->aclcb_state = cb_2->aclcb_state;

+     cb_1->aclcb_sdn = cb_2->aclcb_sdn;

+     cb_1->aclcb_eval_context = cb_2->aclcb_eval_context;

+ 

+ 

+     cb_2->aclcb_aclsignature = swap_aclcb_aclsignature;

+     cb_2->aclcb_state = swap_aclcb_state;

+     cb_2->aclcb_sdn = swap_aclcb_sdn;

+     cb_2->aclcb_eval_context = swap_aclcb_eval_context;

+ 

+ }

+ 

+ static void

  _ger_release_gerpb (

  	Slapi_PBlock **gerpb,

  	void		 **aclcb,	/* original aclcb */
@@ -292,10 +325,12 @@

  		slapi_pblock_get ( pb, SLAPI_CONNECTION, &conn );

  		if (conn)

  		{

- 			struct aclcb *geraclcb;

- 			geraclcb = (struct aclcb *) acl_get_ext ( ACL_EXT_CONNECTION, conn );

- 			acl_conn_ext_destructor ( geraclcb, NULL, NULL );

- 			acl_set_ext ( ACL_EXT_CONNECTION, conn, *aclcb );

+             struct acl_cblock *geraclcb;

+             geraclcb = (struct acl_cblock *)acl_get_ext(ACL_EXT_CONNECTION, conn);

+             PR_Lock(geraclcb->aclcb_lock);

+             _ger_swap_aclcb(geraclcb, (struct acl_cblock *)*aclcb);

+             acl_conn_ext_destructor((struct acl_cblock *)*aclcb, NULL, NULL);

+             PR_Unlock(geraclcb->aclcb_lock);

  			*aclcb = NULL;

  		}

  	}
@@ -307,17 +342,18 @@

  	Slapi_Entry	    *e __attribute__((unused)),

  	const char 		*subjectndn,

  	Slapi_PBlock	**gerpb,

- 	void			**aclcb,	/* original aclcb */

+     void **save_aclcb, /* original aclcb */

  	char			**errbuf __attribute__((unused))

  	)

  {

  	Connection *conn;

- 	struct acl_cblock *geraclcb;

+     struct acl_cblock *ger_aclcb;

+     struct acl_cblock *conn_aclcb;

  	Acl_PBlock *geraclpb;

  	Operation *gerop;

  	int rc = LDAP_SUCCESS;

  

- 	*aclcb = NULL;

+     *save_aclcb = NULL;

  	*gerpb = slapi_pblock_new ();

  	if ( *gerpb == NULL )

  	{
@@ -343,15 +379,18 @@

  		slapi_pblock_set ( *gerpb, SLAPI_CONNECTION, conn );

  

  		/* Can't share the conn->aclcb because of different context */

- 		geraclcb = (struct acl_cblock *) acl_conn_ext_constructor ( NULL, NULL);

- 		if ( geraclcb == NULL )

+         ger_aclcb = (struct acl_cblock *)acl_conn_ext_constructor(NULL, NULL);

+         if (ger_aclcb == NULL)

  		{

  			rc = LDAP_NO_MEMORY;

  			goto bailout;

  		}

- 		slapi_sdn_set_ndn_byval ( geraclcb->aclcb_sdn, subjectndn );

- 		*aclcb = acl_get_ext ( ACL_EXT_CONNECTION, conn );

- 		acl_set_ext ( ACL_EXT_CONNECTION, conn, (void *) geraclcb );

+         slapi_sdn_set_ndn_byval(ger_aclcb->aclcb_sdn, subjectndn);

+         conn_aclcb = acl_get_ext(ACL_EXT_CONNECTION, conn);

+         PR_Lock(conn_aclcb->aclcb_lock);

+         _ger_swap_aclcb(conn_aclcb, ger_aclcb);

+         *save_aclcb = ger_aclcb;

+         PR_Unlock(conn_aclcb->aclcb_lock);

  	}

  

  	{
@@ -377,7 +416,7 @@

  bailout:

  	if ( rc != LDAP_SUCCESS )

  	{

- 		_ger_release_gerpb ( gerpb, aclcb, pb );

+         _ger_release_gerpb(gerpb, save_aclcb, pb);

  	}

  

  	return rc;

@@ -90,13 +90,6 @@

  		return 1;

  	} */

  

- 	/* create the mutex array */

- 	if ( 0 != aclext_alloc_lockarray ( ) ) {

- 		slapi_log_err(SLAPI_LOG_ERR, plugin_name,

- 			"aclinit_main - Unable to create the mutext array\n");

- 		return 1;

- 	}

- 

      /* Allocate the pool */

  	if ( 0 != acl_create_aclpb_pool () ) {

  		slapi_log_err(SLAPI_LOG_ERR, plugin_name,

@@ -286,7 +286,6 @@

  	ACL_DestroyPools();

  	aclanom__del_profile(1);

  	aclgroup_free();

- 	//aclext_free_lockarray();

  	acllist_free();

  

  	return  rc;

@@ -180,17 +180,32 @@

  	    } else {		/* final */

  		auto size_t attempts = MAX_CHAR_COMBINING;

  		auto char* limit = v.bv_val;

+ 		auto char *end;

  		auto struct berval** vkeys;

  		auto struct berval* vals[2];

  		auto struct berval key;

+ 

  		rc = -1;

  		vals[0] = &v;

  		vals[1] = NULL;

  		key.bv_val = (*k)->bv_val;

  		key.bv_len = (*k)->bv_len - 1;

- 		v.bv_val = (*vals)->bv_val + (*vals)->bv_len;

+ 		/* In the following lines it will loop to find

+ 		 * if the end of the attribute value matches the 'final' of the filter

+ 		 * Short summary:

+ 		 * vals contains the attribute value :for example "hello world"

+ 		 * key contain the key generated from the indexing of final part of the filter.

+ 		 * for example filter=(<attribut>=*ld), so key contains the indexing("ld").

+ 		 *

+ 		 * The loop will iterate over the attribute value (vals) from the end of string

+ 		 * to the begining. So it will try to index('d'), index('ld'), index('rld'), index('orld')...

+ 		 *

+ 		 * the key generate from the final part of the filter, then the loop stops => we are done

+ 		 */

+ 		end = v.bv_val + v.bv_len - 1;

+ 		v.bv_val = end;

  		while(1) {

- 		    v.bv_len = (*vals)->bv_len - (v.bv_val - (*vals)->bv_val);

+ 		    v.bv_len = end - v.bv_val + 1;

  		    vkeys = ix->ix_index (ix, vals, NULL);

  		    if (vkeys && vkeys[0]) {

  			auto const struct berval* vkey = vkeys[0];
@@ -301,19 +316,21 @@

      char* t = s;

      char* limit = s + val->bv_len;

      while (s < limit) {

- 	if (!memcmp (s, "\\2a", 3) ||

- 	    !memcmp (s, "\\2A", 3)) {

- 	    *t++ = WILDCARD;

- 	    s += 3;

- 	} else if (!memcmp (s, "\\5c", 3) ||

- 		   !memcmp (s, "\\5C", 3)) {

- 	    *t++ = '\\';

- 	    s += 3;

- 	} else {

- 	    if (t == s) LDAP_UTF8INC (t);

- 	    else t += LDAP_UTF8COPY (t, s);

- 	    LDAP_UTF8INC (s);

- 	}

+         if (((limit - s) >= 3) &&

+                 (!memcmp(s, "\\2a", 3) || !memcmp(s, "\\2A", 3))) {

+             *t++ = WILDCARD;

+             s += 3;

+         } else if ((limit - s) >= 3 &&

+                 (!memcmp(s, "\\5c", 3) || !memcmp(s, "\\5C", 3))) {

+             *t++ = '\\';

+             s += 3;

+         } else {

+             if (t == s)

+                 LDAP_UTF8INC(t);

+             else

+                 t += LDAP_UTF8COPY(t, s);

+             LDAP_UTF8INC(s);

+         }

      }

      val->bv_len = t - val->bv_val;

  }
@@ -389,14 +406,18 @@

      n = 0;

      s = pattern->bv_val;

      for (p = s; p < plimit; LDAP_UTF8INC(p)) {

- 	switch (*p) {

- 	  case WILDCARD:

- 	    result[n++] = ss_filter_value (s, p-s, &val);

- 	    while (++p != plimit && *p == WILDCARD);

- 	    s = p;

- 	    break;

- 	  default: break;

- 	}

+         switch (*p) {

+         case WILDCARD:

+             result[n++] = ss_filter_value(s, p - s, &val);

+             while (p != plimit && *p == WILDCARD) p++;

+             s = p;

+             break;

+         default:

+             break;

+         }

+         if (p >= plimit) {

+             break;

+         }

      }

      if (p != s || s == plimit) {

  	result[n++] = ss_filter_value (s, p-s, &val);

@@ -108,9 +108,6 @@

  #define COSTYPE_INDIRECT 3

  #define COS_DEF_ERROR_NO_TEMPLATES -2

  

- /* the global plugin handle */

- static volatile vattr_sp_handle *vattr_handle = NULL;

- 

  /* both variables are protected by change_lock */

  static int cos_cache_notify_flag = 0;

  static PRBool cos_cache_at_work = PR_FALSE;
@@ -278,7 +275,7 @@

  static Slapi_Mutex *stop_lock;

  static Slapi_CondVar *something_changed = NULL;

  static Slapi_CondVar *start_cond = NULL;

- 

+ static vattr_sp_handle *vattr_handle = NULL;

  

  /*

  	cos_cache_init
@@ -289,75 +286,70 @@

  */

  int cos_cache_init(void)

  {

- 	int ret = 0;

- 

- 	slapi_log_err(SLAPI_LOG_TRACE, COS_PLUGIN_SUBSYSTEM, "--> cos_cache_init\n");

- 

- 	slapi_vattrcache_cache_none();

- 	cache_lock = slapi_new_mutex();

- 	change_lock = slapi_new_mutex();

- 	stop_lock = slapi_new_mutex();

- 	something_changed = slapi_new_condvar(change_lock);

- 	keeprunning =1;

- 	start_lock = slapi_new_mutex();

- 	start_cond = slapi_new_condvar(start_lock);

- 	started = 0;

- 

- 	if (stop_lock == NULL ||

- 	    change_lock == NULL ||

- 	    cache_lock == NULL ||

- 	    stop_lock == NULL ||

- 	    start_lock == NULL ||

- 	    start_cond == NULL ||

- 	    something_changed == NULL)

- 	{

- 		slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM,

- 			   "cos_cache_init - Cannot create mutexes\n" );

- 				ret = -1;

- 		goto out;

- 	}

- 

- 	/* grab the views interface */

- 	if(slapi_apib_get_interface(Views_v1_0_GUID, &views_api))

- 	{

- 		/* lets be tolerant if views is disabled */

- 		views_api = 0;

- 	}

+     int ret = 0;

+ 

+     slapi_log_err(SLAPI_LOG_TRACE, COS_PLUGIN_SUBSYSTEM, "--> cos_cache_init\n");

+ 

+     slapi_vattrcache_cache_none();

+     cache_lock = slapi_new_mutex();

+     change_lock = slapi_new_mutex();

+     stop_lock = slapi_new_mutex();

+     something_changed = slapi_new_condvar(change_lock);

+     keeprunning = 1;

+     start_lock = slapi_new_mutex();

+     start_cond = slapi_new_condvar(start_lock);

+     started = 0;

+ 

+     if (stop_lock == NULL ||

+         change_lock == NULL ||

+         cache_lock == NULL ||

+         stop_lock == NULL ||

+         start_lock == NULL ||

+         start_cond == NULL ||

+         something_changed == NULL) {

+         slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM,

+                       "cos_cache_init - Cannot create mutexes\n");

+         ret = -1;

+         goto out;

+     }

  

- 	if (slapi_vattrspi_register((vattr_sp_handle **)&vattr_handle, 

- 	                            cos_cache_vattr_get, 

- 	                            cos_cache_vattr_compare, 

- 	                            cos_cache_vattr_types) != 0)

- 	{

- 		slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM,

- 		   "cos_cache_init - Cannot register as service provider\n" );

- 			ret = -1;

- 		goto out;

- 	}

+     if (slapi_vattrspi_register((vattr_sp_handle **)&vattr_handle,

+                                 cos_cache_vattr_get,

+                                 cos_cache_vattr_compare,

+                                 cos_cache_vattr_types) != 0) {

+         slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM, "cos_cache_init - Cannot register as service provider\n");

+         ret = -1;

+         goto out;

+     }

  

- 	if ( PR_CreateThread (PR_USER_THREAD, 

- 				cos_cache_wait_on_change, 

- 				NULL,

- 				PR_PRIORITY_NORMAL, 

- 				PR_GLOBAL_THREAD, 

- 				PR_UNJOINABLE_THREAD, 

- 				SLAPD_DEFAULT_THREAD_STACKSIZE) == NULL )

- 	{

- 		slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM,

- 			   "cos_cache_init - PR_CreateThread failed\n" );

- 		ret = -1;

- 		goto out;

- 	}

+     /* grab the views interface */

+     if (slapi_apib_get_interface(Views_v1_0_GUID, &views_api)) {

+         /* lets be tolerant if views is disabled */

+         views_api = 0;

+     }

  

- 		/* wait for that thread to get started */

- 		if (ret == 0) {

- 			slapi_lock_mutex(start_lock);

- 			while (!started) {

- 				while (slapi_wait_condvar(start_cond, NULL) == 0);

- 			}

- 			slapi_unlock_mutex(start_lock);

- 		}

+     if (PR_CreateThread(PR_USER_THREAD,

+                         cos_cache_wait_on_change,

+                         NULL,

+                         PR_PRIORITY_NORMAL,

+                         PR_GLOBAL_THREAD,

+                         PR_UNJOINABLE_THREAD,

+                         SLAPD_DEFAULT_THREAD_STACKSIZE) == NULL) {

+         slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM,

+                       "cos_cache_init - PR_CreateThread failed\n");

+         ret = -1;

+         goto out;

+     }

  

+     /* wait for that thread to get started */

+     if (ret == 0) {

+         slapi_lock_mutex(start_lock);

+         while (!started) {

+             while (slapi_wait_condvar(start_cond, NULL) == 0)

+                 ;

+         }

+         slapi_unlock_mutex(start_lock);

+     }

  

  out:

  	slapi_log_err(SLAPI_LOG_TRACE, COS_PLUGIN_SUBSYSTEM, "<-- cos_cache_init\n");
@@ -752,321 +744,296 @@

  static int 

  cos_dn_defs_cb (Slapi_Entry* e, void *callback_data)

  {

- 	struct dn_defs_info *info;

- 	cosAttrValue **pSneakyVal = 0;

- 	cosAttrValue *pObjectclass = 0;

- 	cosAttrValue *pCosTargetTree = 0;

- 	cosAttrValue *pCosTemplateDn = 0;

- 	cosAttrValue *pCosSpecifier = 0;

- 	cosAttrValue *pCosAttribute = 0;

- 	cosAttrValue *pCosOverrides = 0;

- 	cosAttrValue *pCosOperational = 0;

- 	cosAttrValue *pCosOpDefault = 0;

- 	cosAttrValue *pCosMerge = 0;

- 	cosAttrValue *pDn = 0;

- 	struct berval **dnVals;

- 	int cosType = 0;

- 	int valIndex = 0;

- 	Slapi_Attr *dnAttr;

- 	char *attrType = 0;

- 	char *norm_dn = NULL;

- 	info=(struct dn_defs_info *)callback_data;

- 	

- 	cos_cache_add_attrval(&pDn, slapi_entry_get_dn(e));

- 	if(slapi_entry_first_attr(e, &dnAttr)) {

- 		goto bail;

- 	}

+     struct dn_defs_info *info;

+     cosAttrValue **pSneakyVal = 0;

+     cosAttrValue *pObjectclass = 0;

+     cosAttrValue *pCosTargetTree = 0;

+     cosAttrValue *pCosTemplateDn = 0;

+     cosAttrValue *pCosSpecifier = 0;

+     cosAttrValue *pCosAttribute = 0;

+     cosAttrValue *pCosOverrides = 0;

+     cosAttrValue *pCosOperational = 0;

+     cosAttrValue *pCosOpDefault = 0;

+     cosAttrValue *pCosMerge = 0;

+     cosAttrValue *pDn = 0;

+     struct berval **dnVals;

+     int cosType = 0;

+     int valIndex = 0;

+     Slapi_Attr *dnAttr;

+     char *attrType = 0;

+     char *norm_dn = NULL;

+     info = (struct dn_defs_info *)callback_data;

+ 

+     cos_cache_add_attrval(&pDn, slapi_entry_get_dn(e));

+     if (slapi_entry_first_attr(e, &dnAttr)) {

+         goto bail;

+     }

  

- 	do {

- 		attrType = 0;		

- 		/* we need to fill in the details of the definition now */

- 		slapi_attr_get_type(dnAttr, &attrType);		

- 		if(!attrType) {

- 			continue;

- 		}

- 		pSneakyVal = 0;

- 		if(!slapi_utf8casecmp((unsigned char*)attrType, (unsigned char*)"objectclass"))

- 			pSneakyVal = &pObjectclass;

- 		else if(!slapi_utf8casecmp((unsigned char*)attrType, (unsigned char*)"cosTargetTree")){

- 			if(pCosTargetTree){

- 				norm_dn = slapi_create_dn_string("%s", pCosTargetTree->val);

- 				if(norm_dn){

- 					slapi_ch_free_string(&pCosTargetTree->val);

- 					pCosTargetTree->val = norm_dn;

- 				}

- 			}

- 			pSneakyVal = &pCosTargetTree;

- 		} else if(!slapi_utf8casecmp((unsigned char*)attrType, (unsigned char*)"cosTemplateDn"))

- 			pSneakyVal = &pCosTemplateDn;

- 		else if(!slapi_utf8casecmp((unsigned char*)attrType, (unsigned char*)"cosSpecifier"))

- 			pSneakyVal = &pCosSpecifier;

- 		else if(!slapi_utf8casecmp((unsigned char*)attrType, (unsigned char*)"cosAttribute"))

- 			pSneakyVal = &pCosAttribute;

- 		else if(!slapi_utf8casecmp((unsigned char*)attrType, (unsigned char*)"cosIndirectSpecifier"))

- 			pSneakyVal = &pCosSpecifier;			

- 		if(!pSneakyVal) {

- 			continue;

- 		}

- 		/* It's a type we're interested in */

- 		if(slapi_attr_get_bervals_copy(dnAttr, &dnVals)) {

- 			continue;

- 		}

- 		valIndex = 0;

- 		if(!dnVals) {

- 			continue;

- 		}

- 		for (valIndex = 0; dnVals[valIndex]; valIndex++)

- 		{

- 			if(!dnVals[valIndex]->bv_val) {

- 				continue;

- 			}

- 			/*

- 			parse any overide or default values

- 			and deal with them

- 			*/

- 			if(pSneakyVal == &pCosAttribute)

- 			{

- 				int qualifier_hit = 0;

- 				int op_qualifier_hit = 0;

- 				int merge_schemes_qualifier_hit = 0;

- 				int override_qualifier_hit =0;

- 				int default_qualifier_hit = 0;

- 				int operational_default_qualifier_hit = 0;

- 				do

- 				{

- 					qualifier_hit = 0;

+     do {

+         attrType = 0;

+         /* we need to fill in the details of the definition now */

+         slapi_attr_get_type(dnAttr, &attrType);

+         if (!attrType) {

+             continue;

+         }

+         pSneakyVal = 0;

+         if (!slapi_utf8casecmp((unsigned char *)attrType, (unsigned char *)"objectclass"))

+             pSneakyVal = &pObjectclass;

+         else if (!slapi_utf8casecmp((unsigned char *)attrType, (unsigned char *)"cosTargetTree")) {

+             if (pCosTargetTree) {

+                 norm_dn = slapi_create_dn_string("%s", pCosTargetTree->val);

+                 if (norm_dn) {

+                     slapi_ch_free_string(&pCosTargetTree->val);

+                     pCosTargetTree->val = norm_dn;

+                 }

+             }

+             pSneakyVal = &pCosTargetTree;

+         } else if (!slapi_utf8casecmp((unsigned char *)attrType, (unsigned char *)"cosTemplateDn"))

+             pSneakyVal = &pCosTemplateDn;

+         else if (!slapi_utf8casecmp((unsigned char *)attrType, (unsigned char *)"cosSpecifier"))

+             pSneakyVal = &pCosSpecifier;

+         else if (!slapi_utf8casecmp((unsigned char *)attrType, (unsigned char *)"cosAttribute"))

+             pSneakyVal = &pCosAttribute;

+         else if (!slapi_utf8casecmp((unsigned char *)attrType, (unsigned char *)"cosIndirectSpecifier"))

+             pSneakyVal = &pCosSpecifier;

+         if (!pSneakyVal) {

+             continue;

+         }

+         /* It's a type we're interested in */

+         if (slapi_attr_get_bervals_copy(dnAttr, &dnVals)) {

+             continue;

+         }

+         valIndex = 0;

+         if (!dnVals) {

+             continue;

+         }

+         for (valIndex = 0; dnVals[valIndex]; valIndex++) {

+             if (!dnVals[valIndex]->bv_val) {

+                 continue;

+             }

+             /*

+             parse any overide or default values

+             and deal with them

+             */

+             if (pSneakyVal == &pCosAttribute) {

+                 int qualifier_hit = 0;

+                 int op_qualifier_hit = 0;

+                 int merge_schemes_qualifier_hit = 0;

+                 int override_qualifier_hit = 0;

+                 int default_qualifier_hit = 0;

+                 int operational_default_qualifier_hit = 0;

+                 do {

+                     qualifier_hit = 0;

+ 

+                     if (cos_cache_backwards_stricmp_and_clip(dnVals[valIndex]->bv_val, " operational")) {

+                         /* matched */

+                         op_qualifier_hit = 1;

+                         qualifier_hit = 1;

+                     }

+ 

+                     if (cos_cache_backwards_stricmp_and_clip(dnVals[valIndex]->bv_val, " merge-schemes")) {

+                         /* matched */

+                         merge_schemes_qualifier_hit = 1;

+                         qualifier_hit = 1;

+                     }

+ 

+                     if (cos_cache_backwards_stricmp_and_clip(dnVals[valIndex]->bv_val, " override")) {

+                         /* matched */

+                         override_qualifier_hit = 1;

+                         qualifier_hit = 1;

+                     }

+ 

+                     if (cos_cache_backwards_stricmp_and_clip(dnVals[valIndex]->bv_val, " default")) {

+                         default_qualifier_hit = 1;

+                         qualifier_hit = 1;

+                     }

+ 

+                     if (cos_cache_backwards_stricmp_and_clip(dnVals[valIndex]->bv_val, " operational-default")) {

+                         operational_default_qualifier_hit = 1;

+                         qualifier_hit = 1;

+                     }

+                 } while (qualifier_hit == 1);

+ 

+                 /*

+                 * At this point, dnVals[valIndex]->bv_val

+                 * is the value of cosAttribute, stripped of

+                 * any qualifiers, so add this pure attribute type to

+                 * the appropriate lists.

+                 */

+ 

+                 if (op_qualifier_hit) {

+                     cos_cache_add_attrval(&pCosOperational,

+                                           dnVals[valIndex]->bv_val);

+                 }

+                 if (merge_schemes_qualifier_hit) {

+                     cos_cache_add_attrval(&pCosMerge, dnVals[valIndex]->bv_val);

+                 }

+                 if (override_qualifier_hit) {

+                     cos_cache_add_attrval(&pCosOverrides,

+                                           dnVals[valIndex]->bv_val);

+                 }

+                 if (default_qualifier_hit) {

+                     /* attr is added below in pSneakyVal, in any case */

+                 }

+ 

+                 if (operational_default_qualifier_hit) {

+                     cos_cache_add_attrval(&pCosOpDefault,

+                                           dnVals[valIndex]->bv_val);

+                 }

+ 

+                 slapi_vattrspi_regattr((vattr_sp_handle *)vattr_handle, dnVals[valIndex]->bv_val, NULL, NULL);

+ 

+             } /* if(attrType is cosAttribute) */

+ 

+             /*

+              * Add the attributetype to the appropriate

+              * list.

+              */

+             cos_cache_add_attrval(pSneakyVal, dnVals[valIndex]->bv_val);

+ 

+         } /* for (valIndex = 0; dnVals[valIndex]; valIndex++) */

+ 

+         ber_bvecfree(dnVals);

+         dnVals = NULL;

+     } while (!slapi_entry_next_attr(e, dnAttr, &dnAttr));

+ 

+     if (pCosAttribute && (!pCosTargetTree || !pCosTemplateDn)) {

+         /* get the parent of the definition */

+         char *orig = slapi_dn_parent(pDn->val);

+         char *parent = NULL;

+         if (orig) {

+             parent = slapi_create_dn_string("%s", orig);

+             if (!parent) {

+                 parent = orig;

+                 slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM,

+                               "cos_dn_defs_cb - "

+                               "Failed to normalize parent dn %s. "

+                               "Adding the pre normalized dn.\n",

+                               parent);

+             }

+             if (!pCosTargetTree) {

+                 cos_cache_add_attrval(&pCosTargetTree, parent);

+             }

+             if (!pCosTemplateDn) {

+                 cos_cache_add_attrval(&pCosTemplateDn, parent);

+             }

+             if (parent != orig) {

+                 slapi_ch_free_string(&parent);

+             }

+             slapi_ch_free_string(&orig);

+         } else {

+             slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM,

+                           "cos_dn_defs_cb - "

+                           "Failed to get parent dn of cos definition %s.\n",

+                           pDn->val);

+             if (!pCosTemplateDn) {

+                 if (!pCosTargetTree) {

+                     slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM, "cos_dn_defs_cb - cosTargetTree and cosTemplateDn are not set.\n");

+                 } else {

+                     slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM, "cos_dn_defs_cb - cosTemplateDn is not set.\n");

+                 }

+             } else if (!pCosTargetTree) {

+                 slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM, "cos_dn_defs_cb - cosTargetTree is not set.\n");

+             }

+         }

+     }

  

- 					if(cos_cache_backwards_stricmp_and_clip(dnVals[valIndex]->bv_val, " operational"))

- 					{

- 						/* matched */

- 						op_qualifier_hit = 1;

- 						qualifier_hit = 1;

- 					}

- 					

- 					if(cos_cache_backwards_stricmp_and_clip(dnVals[valIndex]->bv_val, " merge-schemes"))

- 					{

- 						/* matched */

- 						merge_schemes_qualifier_hit = 1;

- 						qualifier_hit = 1;

- 					}

+     /*

+     determine the type of class of service scheme

+     */

  

- 					if(cos_cache_backwards_stricmp_and_clip(dnVals[valIndex]->bv_val, " override"))

- 					{

- 						/* matched */

- 						override_qualifier_hit = 1;

- 						qualifier_hit = 1;

- 					}

- 					

- 					if(cos_cache_backwards_stricmp_and_clip(dnVals[valIndex]->bv_val, " default")) {

- 						default_qualifier_hit = 1;

- 						qualifier_hit = 1;

- 					}

+     if (pObjectclass) {

+         if (cos_cache_attrval_exists(pObjectclass, "cosDefinition")) {

+             cosType = COSTYPE_CLASSIC;

+         } else if (cos_cache_attrval_exists(pObjectclass, "cosClassicDefinition")) {

+             cosType = COSTYPE_CLASSIC;

  

- 					if(cos_cache_backwards_stricmp_and_clip(dnVals[valIndex]->bv_val, " operational-default")) {

- 						operational_default_qualifier_hit = 1;

- 						qualifier_hit = 1;

- 					}

- 				}

- 				while(qualifier_hit == 1);

+         } else if (cos_cache_attrval_exists(pObjectclass, "cosPointerDefinition")) {

+             cosType = COSTYPE_POINTER;

  

- 				/*

- 				* At this point, dnVals[valIndex]->bv_val

- 				* is the value of cosAttribute, stripped of

- 				* any qualifiers, so add this pure attribute type to

- 				* the appropriate lists.

- 				*/

- 		

- 				if ( op_qualifier_hit ) {

- 					cos_cache_add_attrval(&pCosOperational,

- 					                      dnVals[valIndex]->bv_val);

- 				}

- 				if ( merge_schemes_qualifier_hit ) {

- 					cos_cache_add_attrval(&pCosMerge, dnVals[valIndex]->bv_val);

- 				}

- 				if ( override_qualifier_hit ) {

- 					cos_cache_add_attrval(&pCosOverrides,

- 					                      dnVals[valIndex]->bv_val);

- 				}

- 				if ( default_qualifier_hit ) {

- 					/* attr is added below in pSneakyVal, in any case */

- 				}

+         } else if (cos_cache_attrval_exists(pObjectclass, "cosIndirectDefinition")) {

+             cosType = COSTYPE_INDIRECT;

  

- 				if ( operational_default_qualifier_hit ) {

- 					cos_cache_add_attrval(&pCosOpDefault,

- 					                      dnVals[valIndex]->bv_val);

- 				}

- 

- 				slapi_vattrspi_regattr((vattr_sp_handle *)vattr_handle,

- 				                        dnVals[valIndex]->bv_val, NULL, NULL);

- 			} /* if(attrType is cosAttribute) */

+         } else

+             cosType = COSTYPE_BADTYPE;

+     }

  

- 			/*

- 			 * Add the attributetype to the appropriate

- 			 * list.

- 			 */

- 			cos_cache_add_attrval(pSneakyVal, dnVals[valIndex]->bv_val);

- 			

- 		}/* for (valIndex = 0; dnVals[valIndex]; valIndex++) */

- 		

- 		ber_bvecfree( dnVals );

- 		dnVals = NULL;

- 	} while(!slapi_entry_next_attr(e, dnAttr, &dnAttr));

- 

- 	if (pCosAttribute && (!pCosTargetTree || !pCosTemplateDn)) {

- 		/* get the parent of the definition */

- 		char *orig = slapi_dn_parent(pDn->val);

- 		char *parent = NULL;

- 		if (orig) {

- 			parent = slapi_create_dn_string("%s", orig);

- 			if (!parent) {

- 				parent = orig;

- 				slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM, 

- 				              "cos_dn_defs_cb - "

- 				              "Failed to normalize parent dn %s. "

- 				              "Adding the pre normalized dn.\n", 

- 				              parent);

- 			}

- 			if (!pCosTargetTree) {

- 				cos_cache_add_attrval(&pCosTargetTree, parent);

- 			}

- 			if (!pCosTemplateDn) {

- 				cos_cache_add_attrval(&pCosTemplateDn, parent);

- 			}

- 			if (parent != orig) {

- 				slapi_ch_free_string(&parent);

- 			}

- 			slapi_ch_free_string(&orig);

- 		} else {

- 			slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM,

- 			              "cos_dn_defs_cb - "

- 			              "Failed to get parent dn of cos definition %s.\n",

- 			              pDn->val);

- 			if (!pCosTemplateDn) {

- 				if (!pCosTargetTree) {

- 					slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM, "cos_dn_defs_cb - cosTargetTree and cosTemplateDn are not set.\n");

- 				} else {

- 					slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM, "cos_dn_defs_cb - cosTemplateDn is not set.\n");

- 				}

- 			} else if (!pCosTargetTree) {

- 				slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM, "cos_dn_defs_cb - cosTargetTree is not set.\n");

- 			}

- 		}

- 	}

- 	

- 	/*

- 	determine the type of class of service scheme 

- 	*/

- 	

- 	if(pObjectclass)

- 	{

- 		if(cos_cache_attrval_exists(pObjectclass, "cosDefinition"))

- 		{

- 			cosType = COSTYPE_CLASSIC;

- 		}

- 		else if(cos_cache_attrval_exists(pObjectclass, "cosClassicDefinition"))

- 		{

- 			cosType = COSTYPE_CLASSIC;

- 			

- 		}

- 		else if(cos_cache_attrval_exists(pObjectclass, "cosPointerDefinition"))

- 		{

- 			cosType = COSTYPE_POINTER;

- 			

- 		}

- 		else if(cos_cache_attrval_exists(pObjectclass, "cosIndirectDefinition"))

- 		{

- 			cosType = COSTYPE_INDIRECT;

- 			

- 		}

- 		else

- 			cosType = COSTYPE_BADTYPE;

- 	}

- 	

- 	/*	

- 	we should now have a full definition, 

- 	do some sanity checks because we don't

- 	want bogus entries in the cache 

- 	then ship it

- 	*/

- 	

- 	/* these must exist */

- 	if(pDn && pObjectclass && 

- 		(

- 		(cosType == COSTYPE_CLASSIC &&

- 		pCosTemplateDn && 

- 		pCosSpecifier &&   

- 		pCosAttribute ) 

- 		||

- 		(cosType == COSTYPE_POINTER &&

- 		pCosTemplateDn && 

- 		pCosAttribute ) 

- 		||

- 		(cosType == COSTYPE_INDIRECT &&

- 		pCosSpecifier &&   

- 		pCosAttribute ) 

- 		)

- 		)

- 	{

- 		int rc = 0;

- 	/*

- 	we'll leave the referential integrity stuff

- 	up to the referint plug-in and assume all

- 	is good - if it's not then we just have a

- 	useless definition and we'll nag copiously later.

- 		*/

- 		char *pTmpDn = slapi_ch_strdup(pDn->val); /* because dn gets hosed on error */

- 		

- 		if(!(rc = cos_cache_add_defn(info->pDefs, &pDn, cosType,

- 								&pCosTargetTree, &pCosTemplateDn, 

- 								&pCosSpecifier, &pCosAttribute,

- 								&pCosOverrides, &pCosOperational,

- 								&pCosMerge, &pCosOpDefault))) {

- 			info->ret = 0;  /* we have succeeded to add the defn*/

- 		} else {

- 			/*

- 			 * Failed but we will continue the search for other defs

- 			 * Don't reset info->ret....it keeps track of any success

- 			*/

- 			if ( rc == COS_DEF_ERROR_NO_TEMPLATES) {

- 				slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM, "cos_dn_defs_cb - Skipping CoS Definition %s"

- 					"--no CoS Templates found, which should be added before the CoS Definition.\n",

- 					pTmpDn);

- 			} else {

- 				slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM, "cos_dn_defs_cb - Skipping CoS Definition %s\n"

- 					"--error(%d)\n",

- 					pTmpDn, rc);

- 			}

- 		}

- 		

- 		slapi_ch_free_string(&pTmpDn);

- 	}

- 	else

- 	{

- 	/* 

- 	this definition is brain dead - bail

- 	if we have a dn use it to report, if not then *really* bad

- 	things are going on

- 		*/

- 		if(pDn)

- 		{

- 			slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM, "cos_dn_defs_cb - "

- 				"Incomplete cos definition detected in %s, discarding from cache.\n",pDn->val);

- 		}

- 		else

- 			slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM, "cos_dn_defs_cb - "

- 				"Incomplete cos definition detected, no DN to report, discarding from cache.\n");

- 		

- 		if(pCosTargetTree)

- 			cos_cache_del_attrval_list(&pCosTargetTree);

- 		if(pCosTemplateDn)

- 			cos_cache_del_attrval_list(&pCosTemplateDn);

- 		if(pCosSpecifier)

- 			cos_cache_del_attrval_list(&pCosSpecifier);

- 		if(pCosAttribute)

- 			cos_cache_del_attrval_list(&pCosAttribute);

- 		if(pDn)

- 			cos_cache_del_attrval_list(&pDn);

- 	}

+     /*

+     we should now have a full definition,

+     do some sanity checks because we don't

+     want bogus entries in the cache

+     then ship it

+     */

+ 

+     /* these must exist */

+     if (pDn && pObjectclass &&

+         ((cosType == COSTYPE_CLASSIC &&

+           pCosTemplateDn &&

+           pCosSpecifier &&

+           pCosAttribute) ||

+          (cosType == COSTYPE_POINTER &&

+           pCosTemplateDn &&

+           pCosAttribute) ||

+          (cosType == COSTYPE_INDIRECT &&

+           pCosSpecifier &&

+           pCosAttribute))) {

+         int rc = 0;

+         /*

+     we'll leave the referential integrity stuff

+     up to the referint plug-in and assume all

+     is good - if it's not then we just have a

+     useless definition and we'll nag copiously later.

+         */

+         char *pTmpDn = slapi_ch_strdup(pDn->val); /* because dn gets hosed on error */

+ 

+         if (!(rc = cos_cache_add_defn(info->pDefs, &pDn, cosType,

+                                       &pCosTargetTree, &pCosTemplateDn,

+                                       &pCosSpecifier, &pCosAttribute,

+                                       &pCosOverrides, &pCosOperational,

+                                       &pCosMerge, &pCosOpDefault))) {

+             info->ret = 0; /* we have succeeded to add the defn*/

+         } else {

+             /*

+              * Failed but we will continue the search for other defs

+              * Don't reset info->ret....it keeps track of any success

+             */

+             if (rc == COS_DEF_ERROR_NO_TEMPLATES) {

+                 slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM, "cos_dn_defs_cb - Skipping CoS Definition %s"

+                                                                    "--no CoS Templates found, which should be added before the CoS Definition.\n",

+                               pTmpDn);

+             } else {

+                 slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM, "cos_dn_defs_cb - Skipping CoS Definition %s\n"

+                                                                    "--error(%d)\n",

+                               pTmpDn, rc);

+             }

+         }

+ 

+         slapi_ch_free_string(&pTmpDn);

+     } else {

+         /*

+     this definition is brain dead - bail

+     if we have a dn use it to report, if not then *really* bad

+     things are going on

+         */

+         if (pDn) {

+             slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM, "cos_dn_defs_cb - "

+                                                                "Incomplete cos definition detected in %s, discarding from cache.\n",

+                           pDn->val);

+         } else

+             slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM, "cos_dn_defs_cb - "

+                                                                "Incomplete cos definition detected, no DN to report, discarding from cache.\n");

+ 

+         if (pCosTargetTree)

+             cos_cache_del_attrval_list(&pCosTargetTree);

+         if (pCosTemplateDn)

+             cos_cache_del_attrval_list(&pCosTemplateDn);

+         if (pCosSpecifier)

+             cos_cache_del_attrval_list(&pCosSpecifier);

+         if (pCosAttribute)

+             cos_cache_del_attrval_list(&pCosAttribute);

+         if (pDn)

+             cos_cache_del_attrval_list(&pDn);

+     }

  bail:

  	/* we don't keep the objectclasses, so lets free them */

  	if(pObjectclass) {
@@ -2208,48 +2175,44 @@

                                   vattr_type_list_context *type_context,

                                   int flags __attribute__((unused)))

  {

- 	int ret = 0;

- 	int index = 0;

- 	cosCache *pCache;

- 	char *lastattr = "thisisfakeforcos";

- 	int props = 0;

- 

- 	slapi_log_err(SLAPI_LOG_TRACE, COS_PLUGIN_SUBSYSTEM, "--> cos_cache_vattr_types\n");

- 	

- 	if(cos_cache_getref((cos_cache **)&pCache) < 1)

- 	{

- 		/* problems we are hosed */

- 		slapi_log_err(SLAPI_LOG_PLUGIN, COS_PLUGIN_SUBSYSTEM, "cos_cache_vattr_types - Failed to get class of service reference\n");

- 		goto bail;

- 	}

+     int ret = 0;

+     int index = 0;

+     cosCache *pCache;

+     char *lastattr = "thisisfakeforcos";

  

- 	while(index < pCache->attrCount )

- 	{

- 		if(slapi_utf8casecmp(

- 				(unsigned char *)pCache->ppAttrIndex[index]->pAttrName, 

- 				(unsigned char *)lastattr))

- 		{

- 			lastattr = pCache->ppAttrIndex[index]->pAttrName;

+     slapi_log_err(SLAPI_LOG_TRACE, COS_PLUGIN_SUBSYSTEM, "--> cos_cache_vattr_types\n");

  

- 			if(1 == cos_cache_query_attr(pCache, NULL, e, lastattr, NULL, NULL,

- 											 NULL, &props, NULL))

- 			{

- 				/* entry contains this attr */

- 				vattr_type_thang thang = {0};

- 

- 				thang.type_name = lastattr;

- 				thang.type_flags = props;

+     if (cos_cache_getref((cos_cache **)&pCache) < 1) {

+         /* problems we are hosed */

+         slapi_log_err(SLAPI_LOG_PLUGIN, COS_PLUGIN_SUBSYSTEM, "cos_cache_vattr_types - Failed to get class of service reference\n");

+         goto bail;

+     }

  

- 				slapi_vattrspi_add_type(type_context,&thang,0);

- 			}

- 		}

- 		index++;

- 	}

- 	cos_cache_release(pCache);

+     while (index < pCache->attrCount) {

+         int props = 0;

+         if (slapi_utf8casecmp(

+                 (unsigned char *)pCache->ppAttrIndex[index]->pAttrName,

+                 (unsigned char *)lastattr)) {

+             lastattr = pCache->ppAttrIndex[index]->pAttrName;

+ 

+             if (1 == cos_cache_query_attr(pCache, NULL, e, lastattr, NULL, NULL,

+                                           NULL, &props, NULL)) {

+                 /* entry contains this attr */

+                 vattr_type_thang thang = {0};

+ 

+                 thang.type_name = lastattr;

+                 thang.type_flags = props;

+ 

+                 slapi_vattrspi_add_type(type_context, &thang, 0);

+             }

+         }

+         index++;

+     }

+     cos_cache_release(pCache);

  

  bail:

  

- slapi_log_err(SLAPI_LOG_TRACE, COS_PLUGIN_SUBSYSTEM, "<-- cos_cache_vattr_types\n");

+     slapi_log_err(SLAPI_LOG_TRACE, COS_PLUGIN_SUBSYSTEM, "<-- cos_cache_vattr_types\n");

  	return ret;

  }

  
@@ -2380,7 +2343,7 @@

  

  		if(!cos_cache_schema_check(pCache, attr_index, pObjclasses))

  		{

- 			slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM, "cos_cache_query_attr - cos attribute %s failed schema check on dn: %s\n",type,pDn);

+ 			slapi_log_err(SLAPI_LOG_PLUGIN, COS_PLUGIN_SUBSYSTEM, "cos_cache_query_attr - cos attribute %s failed schema check on dn: %s\n",type,pDn);

  			goto bail;

  		}

  	}

@@ -49,13 +49,10 @@

  static Slapi_DN* _ConfigAreaDN = NULL;

  static Slapi_RWLock *config_rwlock = NULL;

  static Slapi_DN* _pluginDN = NULL;

- static PRMonitor *memberof_operation_lock = 0;

  MemberOfConfig *qsortConfig = 0;

  static int usetxn = 0;

  static int premodfn = 0;

- #define MEMBEROF_HASHTABLE_SIZE 1000

- static PLHashTable *fixup_entry_hashtable = NULL; /* global hash table protected by memberof_lock (memberof_operation_lock) */

- static PLHashTable *group_ancestors_hashtable = NULL; /* global hash table protected by memberof_lock (memberof_operation_lock) */

+ 

  

  typedef struct _memberofstringll

  {
@@ -73,18 +70,7 @@

          PRBool use_cache;

  } memberof_get_groups_data;

  

- /* The key to access the hash table is the normalized DN

-  * The normalized DN is stored in the value because:

-  *  - It is used in slapi_valueset_find

-  *  - It is used to fill the memberof_get_groups_data.group_norm_vals

-  */

- typedef struct _memberof_cached_value

- {

- 	char *key;

- 	char *group_dn_val;

-         char *group_ndn_val;

- 	int valid;

- } memberof_cached_value;

+ 

  struct cache_stat

  {

  	int total_lookup;
@@ -189,14 +175,9 @@

  static int memberof_entry_in_scope(MemberOfConfig *config, Slapi_DN *sdn);

  static int memberof_add_objectclass(char *auto_add_oc, const char *dn);

  static int memberof_add_memberof_attr(LDAPMod **mods, const char *dn, char *add_oc);

- static PLHashTable *hashtable_new();

- static void fixup_hashtable_empty(char *msg);

- static PLHashTable *hashtable_new();

- static void ancestor_hashtable_empty(char *msg);

- static void ancestor_hashtable_entry_free(memberof_cached_value *entry);

- static memberof_cached_value *ancestors_cache_lookup(const char *ndn);

- static PRBool ancestors_cache_remove(const char *ndn);

- static PLHashEntry *ancestors_cache_add(const void *key, void *value);

+ static memberof_cached_value *ancestors_cache_lookup(MemberOfConfig *config, const char *ndn);

+ static PRBool ancestors_cache_remove(MemberOfConfig *config, const char *ndn);

+ static PLHashEntry *ancestors_cache_add(MemberOfConfig *config, const void *key, void *value);

  

  /*** implementation ***/

  
@@ -375,12 +356,6 @@

  	slapi_log_err(SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM,

  		"--> memberof_postop_start\n" );

  

- 	memberof_operation_lock = PR_NewMonitor();

- 	if(0 == memberof_operation_lock)

- 	{

- 		rc = -1;

- 		goto bail;

- 	}

  	if(config_rwlock == NULL){

  		if((config_rwlock = slapi_new_rwlock()) == NULL){

  			rc = -1;
@@ -388,9 +363,6 @@

  		}

  	}

  

- 	fixup_entry_hashtable = hashtable_new();

- 	group_ancestors_hashtable = hashtable_new();

- 

  	/* Set the alternate config area if one is defined. */

  	slapi_pblock_get(pb, SLAPI_PLUGIN_CONFIG_AREA, &config_area);

  	if (config_area)
@@ -482,18 +454,7 @@

  	slapi_sdn_free(&_pluginDN);

  	slapi_destroy_rwlock(config_rwlock);

  	config_rwlock = NULL;

- 	PR_DestroyMonitor(memberof_operation_lock);

- 	memberof_operation_lock = NULL;

  

- 	if (fixup_entry_hashtable) {

- 		fixup_hashtable_empty("memberof_postop_close empty fixup_entry_hastable");

- 		PL_HashTableDestroy(fixup_entry_hashtable);

- 	}

- 

- 	if (group_ancestors_hashtable) {

- 		ancestor_hashtable_empty("memberof_postop_close empty group_ancestors_hashtable");

- 		PL_HashTableDestroy(group_ancestors_hashtable);

- 	}

  	slapi_log_err(SLAPI_LOG_TRACE, MEMBEROF_PLUGIN_SUBSYSTEM,

  		     "<-- memberof_postop_close\n" );

  	return 0;
@@ -554,7 +515,7 @@

  {

  	int ret = SLAPI_PLUGIN_SUCCESS;

  	MemberOfConfig *mainConfig = NULL;

- 	MemberOfConfig configCopy = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

+ 	MemberOfConfig configCopy = {0};

  	Slapi_DN *sdn;

  	void *caller_id = NULL;

  
@@ -583,9 +544,6 @@

  		}

  		memberof_copy_config(&configCopy, memberof_get_config());

  		memberof_unlock_config();

- 

- 		/* get the memberOf operation lock */

- 		memberof_lock();

  		

  		/* remove this DN from the

  		 * membership lists of groups
@@ -594,7 +552,6 @@

  			slapi_log_err(SLAPI_LOG_ERR, MEMBEROF_PLUGIN_SUBSYSTEM,

  			                "memberof_postop_del - Error deleting dn (%s) from group. Error (%d)\n",

  			                slapi_sdn_get_dn(sdn),ret);

- 			memberof_unlock();

  			goto bail;

  		}

  
@@ -618,7 +575,6 @@

  				}

  			}

  		}

- 		memberof_unlock();

  bail:

  		memberof_free_config(&configCopy);

  	}
@@ -813,7 +769,7 @@

  		memberof_cached_value *ht_grp = NULL;

  		const char *ndn = slapi_sdn_get_ndn(sdn);

  		

- 		ht_grp = ancestors_cache_lookup((const void *) ndn);

+ 		ht_grp = ancestors_cache_lookup(config, (const void *) ndn);

  		if (ht_grp) {

  #if MEMBEROF_CACHE_DEBUG

  			slapi_log_err(SLAPI_LOG_PLUGIN, MEMBEROF_PLUGIN_SUBSYSTEM, "memberof_call_foreach_dn: Ancestors of %s already cached (%x)\n", ndn, ht_grp);
@@ -960,7 +916,7 @@

  	if(memberof_oktodo(pb))

  	{

  		MemberOfConfig *mainConfig = 0;

- 		MemberOfConfig configCopy = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

+ 		MemberOfConfig configCopy = {0};

  		struct slapi_entry *pre_e = NULL;

  		struct slapi_entry *post_e = NULL;

  		Slapi_DN *pre_sdn = 0;
@@ -988,8 +944,6 @@

  			goto bail;

  		}

  

- 		memberof_lock();

- 

  		/*  update any downstream members */

  		if(pre_sdn && post_sdn && configCopy.group_filter &&

  		   0 == slapi_filter_test_simple(post_e, configCopy.group_filter))
@@ -1060,7 +1014,6 @@

  				}

  			}

  		}

- 		memberof_unlock();

  bail:

  		memberof_free_config(&configCopy);

  	}
@@ -1220,7 +1173,7 @@

  	{

  		int config_copied = 0;

  		MemberOfConfig *mainConfig = 0;

- 		MemberOfConfig configCopy = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

+ 		MemberOfConfig configCopy = {0};

  

  		/* get the mod set */

  		slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods);
@@ -1267,8 +1220,6 @@

  			{

  				int op = slapi_mod_get_operation(smod);

  

- 				memberof_lock();

- 

  				/* the modify op decides the function */

  				switch(op & ~LDAP_MOD_BVALUES)

  				{
@@ -1280,7 +1231,6 @@

  								"memberof_postop_modify - Failed to add dn (%s) to target.  "

  								"Error (%d)\n", slapi_sdn_get_dn(sdn), ret );

  							slapi_mod_done(next_mod);

- 							memberof_unlock();

  							goto bail;

  						}

  						break;
@@ -1299,7 +1249,6 @@

  									"memberof_postop_modify - Failed to replace list (%s).  "

  									"Error (%d)\n", slapi_sdn_get_dn(sdn), ret );

  								slapi_mod_done(next_mod);

- 								memberof_unlock();

  								goto bail;

  							}

  						}
@@ -1311,7 +1260,6 @@

  									"memberof_postop_modify: failed to remove dn (%s).  "

  									"Error (%d)\n", slapi_sdn_get_dn(sdn), ret );

  								slapi_mod_done(next_mod);

- 								memberof_unlock();

  								goto bail;

  							}

  						}
@@ -1326,7 +1274,6 @@

  								"memberof_postop_modify - Failed to replace values in  dn (%s).  "

  								"Error (%d)\n", slapi_sdn_get_dn(sdn), ret );

  							slapi_mod_done(next_mod);

- 							memberof_unlock();

  							goto bail;

  						}

  						break;
@@ -1342,8 +1289,6 @@

  						break;

  					}

  				}

- 

- 				memberof_unlock();

  			}

  

  			slapi_mod_done(next_mod);
@@ -1398,7 +1343,7 @@

  	if(memberof_oktodo(pb) && (sdn = memberof_getsdn(pb)))

  	{

  		struct slapi_entry *e = NULL;

- 		MemberOfConfig configCopy = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

+ 		MemberOfConfig configCopy = {0};

  		MemberOfConfig *mainConfig;

  		slapi_pblock_get( pb, SLAPI_ENTRY_POST_OP, &e );

  
@@ -1424,8 +1369,6 @@

  			int i = 0;

  			Slapi_Attr *attr = 0;

  

- 			memberof_lock();

- 

  			for (i = 0; configCopy.groupattrs && configCopy.groupattrs[i]; i++)

  			{

  				if(0 == slapi_entry_attr_find(e, configCopy.groupattrs[i], &attr))
@@ -1438,8 +1381,6 @@

  					}

  				}

  			}

- 

- 			memberof_unlock();

  			memberof_free_config(&configCopy);

  		}

  	}
@@ -2201,7 +2142,7 @@

   * the firsts elements of the array has 'valid=1' and the dn/ndn of group it belong to

   */

  static void

- cache_ancestors(Slapi_Value **member_ndn_val, memberof_get_groups_data *groups)

+ cache_ancestors(MemberOfConfig *config, Slapi_Value **member_ndn_val, memberof_get_groups_data *groups)

  {

  	Slapi_ValueSet *groupvals = *((memberof_get_groups_data*)groups)->groupvals;

  	Slapi_Value *sval;
@@ -2298,14 +2239,14 @@

  #if MEMBEROF_CACHE_DEBUG

  	dump_cache_entry(cache_entry, key);

  #endif

- 	if (ancestors_cache_add((const void*) key_copy, (void *) cache_entry) == NULL) {

+ 	if (ancestors_cache_add(config, (const void*) key_copy, (void *) cache_entry) == NULL) {

  		slapi_log_err( SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM, "cache_ancestors: Failed to cache ancestor of %s\n", key);

  		ancestor_hashtable_entry_free(cache_entry);

  		slapi_ch_free ((void**)&cache_entry);

  		return;

  	}

  #if MEMBEROF_CACHE_DEBUG

- 	if (double_check = ancestors_cache_lookup((const void*) key)) {

+ 	if (double_check = ancestors_cache_lookup(config, (const void*) key)) {

  		dump_cache_entry(double_check, "read back");

  	}

  #endif
@@ -2390,9 +2331,9 @@

  		memberof_get_groups_callback, &member_data, &cached, member_data.use_cache);

  

  	merge_ancestors(&member_ndn_val, &member_data, data);

- 	if (!cached && member_data.use_cache)

- 		cache_ancestors(&member_ndn_val, &member_data);

- 

+ 	if (!cached && member_data.use_cache) {

+ 		cache_ancestors(config, &member_ndn_val, &member_data);

+ 	}

  

  	slapi_value_free(&member_ndn_val);

  	slapi_valueset_free(groupvals);
@@ -2957,125 +2898,84 @@

   * We should only use this function when using qsort, and only

   * when the memberOf lock is acquired.

   */

- int memberof_qsort_compare(const void *a, const void *b)

+ int

+ memberof_qsort_compare(const void *a, const void *b)

  {

- 	Slapi_Value *val1 = *((Slapi_Value **)a);

- 	Slapi_Value *val2 = *((Slapi_Value **)b);

- 

- 	/* We only need to provide a Slapi_Attr here for it's syntax.  We

- 	 * already validated all grouping attributes to use the Distinguished

- 	 * Name syntax, so we can safely just use the first attr. */

- 	return slapi_attr_value_cmp_ext(qsortConfig->group_slapiattrs[0],

- 	                                val1, val2);

- }

+     Slapi_Value *val1 = *((Slapi_Value **)a);

+     Slapi_Value *val2 = *((Slapi_Value **)b);

  

- /* betxn: This locking mechanism is necessary to guarantee the memberof

-  * consistency */

- void memberof_lock()

- {

- 	if (usetxn) {

- 		PR_EnterMonitor(memberof_operation_lock);

- 	}

- 	if (fixup_entry_hashtable) {

- 		fixup_hashtable_empty("memberof_lock");

- 	}

- 	if (group_ancestors_hashtable) {

- 		ancestor_hashtable_empty("memberof_lock empty group_ancestors_hashtable");

- 		memset(&cache_stat, 0, sizeof(cache_stat));

- 	}

+     /* We only need to provide a Slapi_Attr here for it's syntax.  We

+      * already validated all grouping attributes to use the Distinguished

+      * Name syntax, so we can safely just use the first attr. */

+     return slapi_attr_value_cmp_ext(qsortConfig->group_slapiattrs[0],

+                                     val1, val2);

  }

  

- void memberof_unlock()

- {

- 	if (group_ancestors_hashtable) {

- 		ancestor_hashtable_empty("memberof_unlock empty group_ancestors_hashtable");

- #if MEMBEROF_CACHE_DEBUG

- 		slapi_log_err(SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM, "cache statistics: total lookup %d (success %d), add %d, remove %d, enum %d\n",

- 			cache_stat.total_lookup, cache_stat.successfull_lookup,

- 			cache_stat.total_add, cache_stat.total_remove, cache_stat.total_enumerate);

- 		slapi_log_err(SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM, "cache statistics duration: lookup %ld, add %ld, remove %ld, enum %ld\n",

- 			cache_stat.cumul_duration_lookup, cache_stat.cumul_duration_add,

- 			cache_stat.cumul_duration_remove, cache_stat.cumul_duration_enumerate);

- #endif

- 	}

- 	if (fixup_entry_hashtable) {

- 		fixup_hashtable_empty("memberof_lock");

- 	}

- 	if (usetxn) {

- 		PR_ExitMonitor(memberof_operation_lock);

- 	}

- }

- 

- void memberof_fixup_task_thread(void *arg)

+ void

+ memberof_fixup_task_thread(void *arg)

  {

- 	MemberOfConfig configCopy = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

- 	Slapi_Task *task = (Slapi_Task *)arg;

- 	task_data *td = NULL;

- 	int rc = 0;

- 	Slapi_PBlock *fixup_pb = NULL;

- 

- 	if (!task) {

- 		return; /* no task */

- 	}

- 	slapi_task_inc_refcount(task);

- 	slapi_log_err(SLAPI_LOG_PLUGIN, MEMBEROF_PLUGIN_SUBSYSTEM,

- 		"memberof_fixup_task_thread - refcount incremented.\n" );

- 	/* Fetch our task data from the task */

- 	td = (task_data *)slapi_task_get_data(task);

- 

- 	/* set bind DN in the thread data */

- 	slapi_td_set_dn(slapi_ch_strdup(td->bind_dn));

- 

- 	slapi_task_begin(task, 1);

- 	slapi_task_log_notice(task, "Memberof task starts (arg: %s) ...\n", 

- 	                      td->filter_str);

- 	slapi_log_err(SLAPI_LOG_INFO, MEMBEROF_PLUGIN_SUBSYSTEM,

- 		"memberof_fixup_task_thread - Memberof task starts (filter: \"%s\") ...\n",

- 		td->filter_str);

- 

- 	/* We need to get the config lock first.  Trying to get the

- 	 * config lock after we already hold the op lock can cause

- 	 * a deadlock. */

- 	memberof_rlock_config();

- 	/* copy config so it doesn't change out from under us */

- 	memberof_copy_config(&configCopy, memberof_get_config());

- 	memberof_unlock_config();

- 

- 	/* Mark this as a task operation */

- 	configCopy.fixup_task = 1;

- 

- 	if (usetxn) {

- 		Slapi_DN *sdn = slapi_sdn_new_dn_byref(td->dn);

- 		Slapi_Backend *be = slapi_be_select_exact(sdn);

- 		slapi_sdn_free(&sdn);

- 		if (be) {

- 			fixup_pb = slapi_pblock_new();

- 			slapi_pblock_set(fixup_pb, SLAPI_BACKEND, be);

- 			rc = slapi_back_transaction_begin(fixup_pb);

- 			if (rc) {

- 				slapi_log_err(SLAPI_LOG_ERR, MEMBEROF_PLUGIN_SUBSYSTEM,

- 					"memberof_fixup_task_thread - Failed to start transaction\n");

- 				goto done;

- 			}

- 		} else {

- 			slapi_log_err(SLAPI_LOG_ERR, MEMBEROF_PLUGIN_SUBSYSTEM,

- 				"memberof_fixup_task_thread - Failed to get be backend from (%s)\n",

- 				td->dn);

- 			slapi_task_log_notice(task, "Memberof task - Failed to get be backend from (%s)\n",

- 				td->dn);

- 			rc = -1;

- 			goto done;

- 		}

- 	}

+     MemberOfConfig configCopy = {0};

+     Slapi_Task *task = (Slapi_Task *)arg;

+     task_data *td = NULL;

+     int rc = 0;

+     Slapi_PBlock *fixup_pb = NULL;

  

- 	/* get the memberOf operation lock */

- 	memberof_lock();

+     if (!task) {

+         return; /* no task */

+     }

+     slapi_task_inc_refcount(task);

+     slapi_log_err(SLAPI_LOG_PLUGIN, MEMBEROF_PLUGIN_SUBSYSTEM,

+                   "memberof_fixup_task_thread - refcount incremented.\n");

+     /* Fetch our task data from the task */

+     td = (task_data *)slapi_task_get_data(task);

+ 

+     /* set bind DN in the thread data */

+     slapi_td_set_dn(slapi_ch_strdup(td->bind_dn));

+ 

+     slapi_task_begin(task, 1);

+     slapi_task_log_notice(task, "Memberof task starts (arg: %s) ...\n",

+                           td->filter_str);

+     slapi_log_err(SLAPI_LOG_INFO, MEMBEROF_PLUGIN_SUBSYSTEM,

+                   "memberof_fixup_task_thread - Memberof task starts (filter: \"%s\") ...\n",

+                   td->filter_str);

+ 

+     /* We need to get the config lock first.  Trying to get the

+      * config lock after we already hold the op lock can cause

+      * a deadlock. */

+     memberof_rlock_config();

+     /* copy config so it doesn't change out from under us */

+     memberof_copy_config(&configCopy, memberof_get_config());

+     memberof_unlock_config();

+ 

+     /* Mark this as a task operation */

+     configCopy.fixup_task = 1;

+ 

+     if (usetxn) {

+         Slapi_DN *sdn = slapi_sdn_new_dn_byref(td->dn);

+         Slapi_Backend *be = slapi_be_select_exact(sdn);

+         slapi_sdn_free(&sdn);

+         if (be) {

+             fixup_pb = slapi_pblock_new();

+             slapi_pblock_set(fixup_pb, SLAPI_BACKEND, be);

+             rc = slapi_back_transaction_begin(fixup_pb);

+             if (rc) {

+                 slapi_log_err(SLAPI_LOG_ERR, MEMBEROF_PLUGIN_SUBSYSTEM,

+                               "memberof_fixup_task_thread - Failed to start transaction\n");

+                 goto done;

+             }

+         } else {

+             slapi_log_err(SLAPI_LOG_ERR, MEMBEROF_PLUGIN_SUBSYSTEM,

+                           "memberof_fixup_task_thread - Failed to get be backend from (%s)\n",

+                           td->dn);

+             slapi_task_log_notice(task, "Memberof task - Failed to get be backend from (%s)\n",

+                                   td->dn);

+             rc = -1;

+             goto done;

+         }

+     }

  

- 	/* do real work */

- 	rc = memberof_fix_memberof(&configCopy, task, td);

-  

- 	/* release the memberOf operation lock */

- 	memberof_unlock();

+     /* do real work */

+     rc = memberof_fix_memberof(&configCopy, task, td);

  

  done:

  	if (usetxn && fixup_pb) {
@@ -3243,7 +3143,7 @@

  }

  

  static memberof_cached_value *

- ancestors_cache_lookup(const char *ndn)

+ ancestors_cache_lookup(MemberOfConfig *config, const char *ndn)

  {

  	memberof_cached_value *e;

  #if defined(DEBUG) && defined(HAVE_CLOCK_GETTIME)
@@ -3261,7 +3161,7 @@

  	}

  #endif

  

- 	e = (memberof_cached_value *) PL_HashTableLookupConst(group_ancestors_hashtable, (const void *) ndn);

+ 	e = (memberof_cached_value *) PL_HashTableLookupConst(config->ancestors_cache, (const void *) ndn);

  

  #if defined(DEBUG) && defined(HAVE_CLOCK_GETTIME)

  	if (start) {
@@ -3277,7 +3177,7 @@

  	

  }

  static PRBool

- ancestors_cache_remove(const char *ndn)

+ ancestors_cache_remove(MemberOfConfig *config, const char *ndn)

  {

  	PRBool rc;

  #if defined(DEBUG) && defined(HAVE_CLOCK_GETTIME)
@@ -3295,7 +3195,7 @@

  	}

  #endif

  

- 	rc = PL_HashTableRemove(group_ancestors_hashtable, (const void *) ndn);

+ 	rc = PL_HashTableRemove(config->ancestors_cache, (const void *) ndn);

  

  #if defined(DEBUG) && defined(HAVE_CLOCK_GETTIME)

  	if (start) {
@@ -3308,7 +3208,7 @@

  }

  

  static PLHashEntry *

- ancestors_cache_add(const void *key, void *value)

+ ancestors_cache_add(MemberOfConfig *config, const void *key, void *value)

  {

  	PLHashEntry *e;

  #if defined(DEBUG) && defined(HAVE_CLOCK_GETTIME)
@@ -3325,7 +3225,7 @@

  	}

  #endif

  

- 	e = PL_HashTableAdd(group_ancestors_hashtable, key, value);

+ 	e = PL_HashTableAdd(config->ancestors_cache, key, value);

  

  #if defined(DEBUG) && defined(HAVE_CLOCK_GETTIME)

  	if (start) {
@@ -3363,10 +3263,11 @@

  		goto bail;

  	}

  

-         /* Check if the entry has not already been fixed */

+     /* Check if the entry has not already been fixed */

  	ndn = slapi_sdn_get_ndn(sdn);

- 	if (ndn && fixup_entry_hashtable && PL_HashTableLookupConst(fixup_entry_hashtable, (void*) ndn)) {

- 		slapi_log_err(SLAPI_LOG_PLUGIN, MEMBEROF_PLUGIN_SUBSYSTEM, "memberof_fix_memberof_callback: Entry %s already fixed up\n", ndn);

+ 	if (ndn && config->fixup_cache && PL_HashTableLookupConst(config->fixup_cache, (void*) ndn)) {

+ 		slapi_log_err(SLAPI_LOG_PLUGIN, MEMBEROF_PLUGIN_SUBSYSTEM,

+             "memberof_fix_memberof_callback: Entry %s already fixed up\n", ndn);

  		goto bail;

  	}

  
@@ -3386,9 +3287,9 @@

  #if MEMBEROF_CACHE_DEBUG

  			slapi_log_err(SLAPI_LOG_PLUGIN, MEMBEROF_PLUGIN_SUBSYSTEM, "memberof_fix_memberof_callback: This is NOT a group %s\n", ndn);

  #endif

- 			ht_grp = ancestors_cache_lookup((const void *) ndn);

+ 			ht_grp = ancestors_cache_lookup(config, (const void *) ndn);

  			if (ht_grp) {

- 				if (ancestors_cache_remove((const void *) ndn)) {

+ 				if (ancestors_cache_remove(config, (const void *) ndn)) {

  					slapi_log_err(SLAPI_LOG_PLUGIN, MEMBEROF_PLUGIN_SUBSYSTEM, "memberof_fix_memberof_callback: free cached values for %s\n", ndn);

  					ancestor_hashtable_entry_free(ht_grp);

  					slapi_ch_free((void **) &ht_grp);
@@ -3399,10 +3300,11 @@

  				/* This is quite unexpected, after a call to memberof_get_groups

  				 * ndn ancestors should be in the cache

  				 */

- 				slapi_log_err(SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM, "memberof_fix_memberof_callback: Weird, %s is not in the cache\n", ndn);

+ 				slapi_log_err(SLAPI_LOG_PLUGIN, MEMBEROF_PLUGIN_SUBSYSTEM, "memberof_fix_memberof_callback: Weird, %s is not in the cache\n", ndn);

  			}

  		}

  	}

+ 

  	/* If we found some groups, replace the existing memberOf attribute

  	 * with the found values.  */

  	if (groups && slapi_valueset_count(groups))
@@ -3442,9 +3344,9 @@

  	slapi_valueset_free(groups);

  

  	/* records that this entry has been fixed up */

- 	if (fixup_entry_hashtable) {

+ 	if (config->fixup_cache) {

  		dn_copy = slapi_ch_strdup(ndn);

- 		if (PL_HashTableAdd(fixup_entry_hashtable, dn_copy, dn_copy) == NULL) {

+ 		if (PL_HashTableAdd(config->fixup_cache, dn_copy, dn_copy) == NULL) {

  			slapi_log_err(SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM, "memberof_fix_memberof_callback: "

  				"failed to add dn (%s) in the fixup hashtable; NSPR error - %d\n",

  				dn_copy, PR_GetError());
@@ -3542,151 +3444,8 @@

  	return rc;

  }

  

- static PRIntn memberof_hash_compare_keys(const void *v1, const void *v2)

- {

- 	PRIntn rc;

- 	if (0 == strcasecmp((const char *) v1, (const char *) v2)) {

- 		rc = 1;

- 	} else {

- 		rc = 0;

- 	}

- 	return rc;

- }

- 

- static PRIntn memberof_hash_compare_values(const void *v1, const void *v2)

- {

- 	PRIntn rc;

- 	if ((char *) v1 == (char *) v2) {

- 		rc = 1;

- 	} else {

- 		rc = 0;

- 	}

- 	return rc;

- }

- 

- /*

-  *  Hashing function using Bernstein's method

-  */

- static PLHashNumber memberof_hash_fn(const void *key)

- {

-     PLHashNumber hash = 5381;

-     unsigned char *x = (unsigned char *)key;

-     int c;

- 

-     while ((c = *x++)){

-         hash = ((hash << 5) + hash) ^ c;

-     }

-     return hash;

- }

- 

- /* allocates the plugin hashtable

-  * This hash table is used by operation and is protected from

-  * concurrent operations with the memberof_lock (if not usetxn, memberof_lock

-  * is not implemented and the hash table will be not used.

-  *

-  * The hash table contains all the DN of the entries for which the memberof

-  * attribute has been computed/updated during the current operation

-  *

-  * hash table should be empty at the beginning and end of the plugin callback

-  */

- static PLHashTable *hashtable_new()

- {

- 	if (!usetxn) {

- 		return NULL;

- 	}

- 

- 	return PL_NewHashTable(MEMBEROF_HASHTABLE_SIZE,

- 		memberof_hash_fn,

- 		memberof_hash_compare_keys,

- 		memberof_hash_compare_values, NULL, NULL);

- }

- /* this function called for each hash node during hash destruction */

- static PRIntn fixup_hashtable_remove(PLHashEntry *he, PRIntn index __attribute__((unused)), void *arg __attribute__((unused)))

- {

- 	char *dn_copy;

- 

- 	if (he == NULL) {

- 		return HT_ENUMERATE_NEXT;

- 	}

- 	dn_copy = (char*) he->value;

- 	slapi_ch_free_string(&dn_copy);

- 

- 	return HT_ENUMERATE_REMOVE;

- }

- 

- static void fixup_hashtable_empty(char *msg)

- {

- 	if (fixup_entry_hashtable) {

- 		PL_HashTableEnumerateEntries(fixup_entry_hashtable, fixup_hashtable_remove, msg);

- 	}

- }

- 

- 

- /* allocates the plugin hashtable

-  * This hash table is used by operation and is protected from

-  * concurrent operations with the memberof_lock (if not usetxn, memberof_lock

-  * is not implemented and the hash table will be not used.

-  *

-  * The hash table contains all the DN of the entries for which the memberof

-  * attribute has been computed/updated during the current operation

-  *

-  * hash table should be empty at the beginning and end of the plugin callback

-  */

- 

- static

- void ancestor_hashtable_entry_free(memberof_cached_value *entry)

- {

- 	int i;

- 	for (i = 0; entry[i].valid; i++) {

- 		slapi_ch_free((void **) &entry[i].group_dn_val);

- 		slapi_ch_free((void **) &entry[i].group_ndn_val);

- 	}

- 	/* Here we are at the ending element containing the key */

- 	slapi_ch_free((void**) &entry[i].key);

- }

- /* this function called for each hash node during hash destruction */

- static PRIntn ancestor_hashtable_remove(PLHashEntry *he, PRIntn index __attribute__((unused)), void *arg __attribute__((unused)))

- {

-     memberof_cached_value *group_ancestor_array;

- 

-     if (he == NULL) {

-         return HT_ENUMERATE_NEXT;

-     }

- 

- 

-     group_ancestor_array = (memberof_cached_value *) he->value;

-     ancestor_hashtable_entry_free(group_ancestor_array);

-     slapi_ch_free((void **)&group_ancestor_array);

- 

-     return HT_ENUMERATE_REMOVE;

- }

- 

- static void ancestor_hashtable_empty(char *msg)

+ int

+ memberof_use_txn()

  {

- #if defined(DEBUG) && defined(HAVE_CLOCK_GETTIME)

- 	long int start;

- 	struct timespec tsnow;

- #endif

- 

- 	if (group_ancestors_hashtable) {

- 		cache_stat.total_enumerate++;

- #if defined(DEBUG) && defined(HAVE_CLOCK_GETTIME)

- 		if (clock_gettime(CLOCK_REALTIME, &tsnow) != 0) {

- 			start = 0;

- 		} else {

- 			start = tsnow.tv_nsec;

- 		}

- #endif

- 		PL_HashTableEnumerateEntries(group_ancestors_hashtable, ancestor_hashtable_remove, msg);

- 

- #if defined(DEBUG) && defined(HAVE_CLOCK_GETTIME)

- 		if (start) {

- 			if (clock_gettime(CLOCK_REALTIME, &tsnow) == 0) {

- 				cache_stat.cumul_duration_enumerate += (tsnow.tv_nsec - start);

- 			}

- 		}

- #endif

- 	}

- 

+     return usetxn;

  }

- 

@@ -62,8 +62,22 @@

  	int skip_nested;

  	int fixup_task;

  	char *auto_add_oc;

+ 	PLHashTable *ancestors_cache;

+ 	PLHashTable *fixup_cache;

  } MemberOfConfig;

  

+ /* The key to access the hash table is the normalized DN

+  * The normalized DN is stored in the value because:

+  *  - It is used in slapi_valueset_find

+  *  - It is used to fill the memberof_get_groups_data.group_norm_vals

+  */

+ typedef struct _memberof_cached_value

+ {

+     char *key;

+     char *group_dn_val;

+     char *group_ndn_val;

+     int valid;

+ } memberof_cached_value;

  

  /*

   * functions
@@ -72,8 +86,6 @@

  void memberof_copy_config(MemberOfConfig *dest, MemberOfConfig *src);

  void memberof_free_config(MemberOfConfig *config);

  MemberOfConfig *memberof_get_config(void);

- void memberof_lock(void);

- void memberof_unlock(void);

  void memberof_rlock_config(void);

  void memberof_wlock_config(void);

  void memberof_unlock_config(void);
@@ -88,5 +100,8 @@

  void *memberof_get_plugin_id(void);

  void memberof_release_config(void);

  PRUint64 get_plugin_started(void);

+ void ancestor_hashtable_entry_free(memberof_cached_value *entry);

+ PLHashTable *hashtable_new();

+ int memberof_use_txn();

  

  #endif	/* _MEMBEROF_H_ */

@@ -14,12 +14,12 @@

   * memberof_config.c - configuration-related code for memberOf plug-in

   *

   */

- 

+ #include "plhash.h"

  #include <plstr.h>

- 

  #include "memberof.h"

  

  #define MEMBEROF_CONFIG_FILTER "(objectclass=*)"

+ #define MEMBEROF_HASHTABLE_SIZE 1000

  

  /*

   * The configuration attributes are contained in the plugin entry e.g.
@@ -33,7 +33,9 @@

  

  /*

   * function prototypes

-  */ 

+  */

+ static void fixup_hashtable_empty( MemberOfConfig *config, char *msg);

+ static void ancestor_hashtable_empty(MemberOfConfig *config, char *msg);

  static int memberof_validate_config (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, 

  										 int *returncode, char *returntext, void *arg);

  static int memberof_search (Slapi_PBlock *pb __attribute__((unused)),
@@ -52,7 +54,7 @@

  /* This is the main configuration which is updated from dse.ldif.  The

   * config will be copied when it is used by the plug-in to prevent it

   * being changed out from under a running memberOf operation. */

- static MemberOfConfig theConfig = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

+ static MemberOfConfig theConfig = {0};

  static Slapi_RWLock *memberof_config_lock = 0;

  static int inited = 0;

  
@@ -712,6 +714,12 @@

  {

  	if (dest && src)

  	{

+         /* Allocate our caches here since we only copy the config at the start of an op */

+         if (memberof_use_txn() == 1){

+             dest->ancestors_cache = hashtable_new();

+             dest->fixup_cache = hashtable_new();

+         }

+ 

  		/* Check if the copy is already up to date */

  		if (src->groupattrs)

  		{
@@ -815,6 +823,14 @@

  		slapi_ch_free_string(&config->memberof_attr);

  		memberof_free_scope(config->entryScopes, &config->entryScopeCount);

  		memberof_free_scope(config->entryScopeExcludeSubtrees, &config->entryExcludeScopeCount);

+ 		if (config->fixup_cache) {

+ 			fixup_hashtable_empty(config, "memberof_free_config empty fixup_entry_hastable");

+ 			PL_HashTableDestroy(config->fixup_cache);

+ 		}

+ 		if (config->ancestors_cache) {

+ 			ancestor_hashtable_empty(config, "memberof_free_config empty group_ancestors_hashtable");

+ 			PL_HashTableDestroy(config->ancestors_cache);

+ 		}

  	}

  }

  
@@ -1017,3 +1033,131 @@

  

  	return ret;

  }

+ 

+ 

+ static PRIntn memberof_hash_compare_keys(const void *v1, const void *v2)

+ {

+ 	PRIntn rc;

+ 	if (0 == strcasecmp((const char *) v1, (const char *) v2)) {

+ 		rc = 1;

+ 	} else {

+ 		rc = 0;

+ 	}

+ 	return rc;

+ }

+ 

+ static PRIntn memberof_hash_compare_values(const void *v1, const void *v2)

+ {

+ 	PRIntn rc;

+ 	if ((char *) v1 == (char *) v2) {

+ 		rc = 1;

+ 	} else {

+ 		rc = 0;

+ 	}

+ 	return rc;

+ }

+ 

+ /*

+  *  Hashing function using Bernstein's method

+  */

+ static PLHashNumber memberof_hash_fn(const void *key)

+ {

+     PLHashNumber hash = 5381;

+     unsigned char *x = (unsigned char *)key;

+     int c;

+ 

+     while ((c = *x++)){

+         hash = ((hash << 5) + hash) ^ c;

+     }

+     return hash;

+ }

+ 

+ /* allocates the plugin hashtable

+  * This hash table is used by operation and is protected from

+  * concurrent operations with the memberof_lock (if not usetxn, memberof_lock

+  * is not implemented and the hash table will be not used.

+  *

+  * The hash table contains all the DN of the entries for which the memberof

+  * attribute has been computed/updated during the current operation

+  *

+  * hash table should be empty at the beginning and end of the plugin callback

+  */

+ PLHashTable *hashtable_new(int usetxn)

+ {

+ 	if (!usetxn) {

+ 		return NULL;

+ 	}

+ 

+ 	return PL_NewHashTable(MEMBEROF_HASHTABLE_SIZE,

+ 		memberof_hash_fn,

+ 		memberof_hash_compare_keys,

+ 		memberof_hash_compare_values, NULL, NULL);

+ }

+ 

+ /* this function called for each hash node during hash destruction */

+ static PRIntn fixup_hashtable_remove(PLHashEntry *he, PRIntn index __attribute__((unused)), void *arg __attribute__((unused)))

+ {

+ 	char *dn_copy;

+ 

+ 	if (he == NULL) {

+ 		return HT_ENUMERATE_NEXT;

+ 	}

+ 	dn_copy = (char*) he->value;

+ 	slapi_ch_free_string(&dn_copy);

+ 

+ 	return HT_ENUMERATE_REMOVE;

+ }

+ 

+ static void fixup_hashtable_empty(MemberOfConfig *config, char *msg)

+ {

+ 	if (config->fixup_cache) {

+ 		PL_HashTableEnumerateEntries(config->fixup_cache, fixup_hashtable_remove, msg);

+ 	}

+ }

+ 

+ 

+ /* allocates the plugin hashtable

+  * This hash table is used by operation and is protected from

+  * concurrent operations with the memberof_lock (if not usetxn, memberof_lock

+  * is not implemented and the hash table will be not used.

+  *

+  * The hash table contains all the DN of the entries for which the memberof

+  * attribute has been computed/updated during the current operation

+  *

+  * hash table should be empty at the beginning and end of the plugin callback

+  */

+ 

+ void ancestor_hashtable_entry_free(memberof_cached_value *entry)

+ {

+ 	int i;

+ 

+ 	for (i = 0; entry[i].valid; i++) {

+ 		slapi_ch_free((void **) &entry[i].group_dn_val);

+ 		slapi_ch_free((void **) &entry[i].group_ndn_val);

+ 	}

+ 	/* Here we are at the ending element containing the key */

+ 	slapi_ch_free((void**) &entry[i].key);

+ }

+ 

+ /* this function called for each hash node during hash destruction */

+ static PRIntn ancestor_hashtable_remove(PLHashEntry *he, PRIntn index __attribute__((unused)), void *arg __attribute__((unused)))

+ {

+     memberof_cached_value *group_ancestor_array;

+ 

+     if (he == NULL) {

+         return HT_ENUMERATE_NEXT;

+     }

+     group_ancestor_array = (memberof_cached_value *) he->value;

+     ancestor_hashtable_entry_free(group_ancestor_array);

+     slapi_ch_free((void **)&group_ancestor_array);

+ 

+     return HT_ENUMERATE_REMOVE;

+ }

+ 

+ static void ancestor_hashtable_empty(MemberOfConfig *config, char *msg)

+ {

+ 	if (config->ancestors_cache) {

+ 		PL_HashTableEnumerateEntries(config->ancestors_cache, ancestor_hashtable_remove, msg);

+ 	}

+ 

+ }

@@ -92,7 +92,7 @@

  

  /* For testing pbkdf2 only */

  uint64_t pbkdf2_sha256_benchmark_iterations();

- PRUint32 pbkdf2_sha256_calculate_iterations();

+ PRUint32 pbkdf2_sha256_calculate_iterations(uint64_t time_nsec);

  

  /* Utility functions */

  PRUint32 pwdstorage_base64_decode_len(const char *encval, PRUint32 enclen);

@@ -304,6 +304,8 @@

  static void _cl5WriteBerval (struct berval *bv, char** buff);

  static int _cl5ReadBervals (struct berval ***bv, char** buff, unsigned int size);

  static int _cl5WriteBervals (struct berval **bv, char** buff, u_int32_t *size);

+ static int32_t _cl5CheckMaxRUV(CL5DBFile *file, RUV *maxruv);

+ static int32_t _cl5CheckCSNinCL(const ruv_enum_data *element, void *arg);

  

  /* replay iteration */

  #ifdef FOR_DEBUGGING
@@ -2885,6 +2887,36 @@

      return CL5_SUCCESS;

  }

  

+ static int32_t

+ _cl5CheckCSNinCL(const ruv_enum_data *element, void *arg)

+ {

+     CL5DBFile *file = (CL5DBFile *)arg;

+     int rc = 0;

+ 

+     DBT key = {0}, data = {0};

+     char csnStr[CSN_STRSIZE];

+ 

+     /* construct the key */

+     key.data = csn_as_string(element->csn, PR_FALSE, csnStr);

+     key.size = CSN_STRSIZE;

+ 

+     data.flags = DB_DBT_MALLOC;

+ 

+     rc = file->db->get(file->db, NULL /*txn*/, &key, &data, 0);

+ 

+     slapi_ch_free(&(data.data));

+     return rc;

+ }

+ 

+ static int32_t

+ _cl5CheckMaxRUV(CL5DBFile *file, RUV *maxruv)

+ {

+     int rc = 0;

+ 

+     rc = ruv_enumerate_elements(maxruv, _cl5CheckCSNinCL, (void *)file);

+ 

+     return rc;

+ }

  /* upgrade from db33 to db41

   * 1. Run recovery on the database environment using the DB_ENV->open method

   * 2. Remove any Berkeley DB environment using the DB_ENV->remove method 
@@ -4248,6 +4280,13 @@

  		rc = ruv_to_bervals(file->maxRUV, &vals);

  	}

  

+ 	if (!purge && _cl5CheckMaxRUV(file, file->maxRUV)) {

+ 		slapi_log_err(SLAPI_LOG_ERR, repl_plugin_name_cl,

+ 				"_cl5WriteRUV - changelog maxRUV not found in changelog for file %s\n",

+ 				file->name);

+ 		return CL5_DB_ERROR;

+ 	}

+ 

  	key.size = CSN_STRSIZE;

      

  	rc = _cl5WriteBervals (vals, &buff, &data.size);

@@ -14,7 +14,6 @@

  

  #include "csnpl.h"

  #include "llist.h"

- #include "repl_shared.h"

  

  struct csnpl 

  {
@@ -22,13 +21,17 @@

  	Slapi_RWLock*	csnLock;	/* lock to serialize access to PL */

  };	

  

+ 

  typedef struct _csnpldata

  {

  	PRBool	committed;  /* True if CSN committed */

  	CSN	*csn;       /* The actual CSN */

+ 	Replica * prim_replica; /* The replica where the prom csn was generated */

  	const CSN *prim_csn;  /* The primary CSN of an operation consising of multiple sub ops*/

  } csnpldata;

  

+ static PRBool csn_primary_or_nested(csnpldata *csn_data,  const CSNPL_CTX *csn_ctx);

+ 

  /* forward declarations */

  #ifdef DEBUG

  static void _csnplDumpContentNoLock(CSNPL *csnpl, const char *caller);
@@ -104,7 +107,7 @@

   *          1 if the csn has already been seen

   *         -1 for any other kind of errors

   */

- int csnplInsert (CSNPL *csnpl, const CSN *csn, const CSN *prim_csn)

+ int csnplInsert (CSNPL *csnpl, const CSN *csn, const CSNPL_CTX *prim_csn)

  {

  	int rc;

  	csnpldata *csnplnode;
@@ -129,10 +132,13 @@

          return 1;

      }

  

- 	csnplnode = (csnpldata *)slapi_ch_malloc(sizeof(csnpldata));

+ 	csnplnode = (csnpldata *)slapi_ch_calloc(1, sizeof(csnpldata));

  	csnplnode->committed = PR_FALSE;

  	csnplnode->csn = csn_dup(csn);

- 	csnplnode->prim_csn = prim_csn;

+ 	if (prim_csn) {

+ 		csnplnode->prim_csn = prim_csn->prim_csn;

+ 		csnplnode->prim_replica =  prim_csn->prim_repl;

+ 	}

  	csn_as_string(csn, PR_FALSE, csn_str);

  	rc = llistInsertTail (csnpl->csnList, csn_str, csnplnode);

  
@@ -187,8 +193,58 @@

  

  	return 0;

  }

+ PRBool csn_primary(Replica *replica, const CSN *csn,  const CSNPL_CTX *csn_ctx)

+ {

+     if (csn_ctx == NULL)

+         return PR_FALSE;

+     

+     if (replica != csn_ctx->prim_repl) {

+         /* The CSNs are not from the same replication topology

+          * so even if the csn values are equal they are not related

+          * to the same operation

+          */

+         return PR_FALSE;

+     }

+     

+     /* Here the two CSNs belong to the same replication topology */

+     

+     /* check if the CSN identifies the primary update */

+     if (csn_is_equal(csn, csn_ctx->prim_csn)) {

+         return PR_TRUE;

+     }

+     

+     return PR_FALSE;

+ }

+ 

+ static PRBool csn_primary_or_nested(csnpldata *csn_data,  const CSNPL_CTX *csn_ctx)

+ {

+     if ((csn_data == NULL) || (csn_ctx == NULL))

+         return PR_FALSE;

+     

+     if (csn_data->prim_replica != csn_ctx->prim_repl) {

+         /* The CSNs are not from the same replication topology

+          * so even if the csn values are equal they are not related

+          * to the same operation

+          */

+         return PR_FALSE;

+     }

+     

+     /* Here the two CSNs belong to the same replication topology */

+     

+     /* First check if the CSN identifies the primary update */

+     if (csn_is_equal(csn_data->csn, csn_ctx->prim_csn)) {

+         return PR_TRUE;

+     }

+     

+     /* Second check if the CSN identifies a nested update */

+     if (csn_is_equal(csn_data->prim_csn, csn_ctx->prim_csn)) {

+         return PR_TRUE;

+     }

+     

+     return PR_FALSE;

+ }

  

- int csnplRemoveAll (CSNPL *csnpl, const CSN *csn)

+ int csnplRemoveAll (CSNPL *csnpl, const CSNPL_CTX *csn_ctx)

  {

  	csnpldata *data;

  	void *iterator;
@@ -197,8 +253,7 @@

  	data = (csnpldata *)llistGetFirst(csnpl->csnList, &iterator);

  	while (NULL != data)

  	{

- 		if (csn_is_equal(data->csn, csn) ||

- 		    csn_is_equal(data->prim_csn, csn)) {

+ 		if (csn_primary_or_nested(data, csn_ctx)) {

  			csnpldata_free(&data);

  			data = (csnpldata *)llistRemoveCurrentAndGetNext(csnpl->csnList, &iterator);

  		} else {
@@ -213,13 +268,13 @@

  }

  

  

- int csnplCommitAll (CSNPL *csnpl, const CSN *csn)

+ int csnplCommitAll (CSNPL *csnpl, const CSNPL_CTX *csn_ctx)

  {

  	csnpldata *data;

  	void *iterator;

  	char csn_str[CSN_STRSIZE];

  

- 	csn_as_string(csn, PR_FALSE, csn_str);

+ 	csn_as_string(csn_ctx->prim_csn, PR_FALSE, csn_str);

  	slapi_log_err(SLAPI_LOG_REPL, repl_plugin_name,

  		            "csnplCommitALL: committing all csns for csn %s\n", csn_str);

  	slapi_rwlock_wrlock (csnpl->csnLock);
@@ -229,8 +284,7 @@

  		csn_as_string(data->csn, PR_FALSE, csn_str);

  		slapi_log_err(SLAPI_LOG_REPL, repl_plugin_name,

  				"csnplCommitALL: processing data csn %s\n", csn_str);

- 		if (csn_is_equal(data->csn, csn) ||

- 		    csn_is_equal(data->prim_csn, csn)) {

+                 if (csn_primary_or_nested(data, csn_ctx)) {

  			data->committed = PR_TRUE;

  		}

  		data = (csnpldata *)llistGetNext (csnpl->csnList, &iterator);
@@ -395,7 +449,12 @@

  

  /* wrapper around csn_free, to satisfy NSPR thread context API */

  void

- csnplFreeCSN (void *arg)

+ csnplFreeCSNPL_CTX (void *arg)

  {

- 	csn_free((CSN **)&arg);

+ 	CSNPL_CTX *csnpl_ctx = (CSNPL_CTX *)arg;

+ 	csn_free(&csnpl_ctx->prim_csn);

+ 	if (csnpl_ctx->sec_repl) {

+ 		slapi_ch_free((void **)&csnpl_ctx->sec_repl);

+ 	}

+ 	slapi_ch_free((void **)&csnpl_ctx);

  }

@@ -17,15 +17,17 @@

  #define CSNPL_H

  

  #include "slapi-private.h"

+ #include "repl5.h"

  

  typedef struct csnpl CSNPL;

  

  CSNPL* csnplNew(void);

  void csnplFree (CSNPL **csnpl);

- int csnplInsert (CSNPL *csnpl, const CSN *csn, const CSN *prim_csn);

+ int csnplInsert (CSNPL *csnpl, const CSN *csn, const CSNPL_CTX *prim_csn);

  int csnplRemove (CSNPL *csnpl, const CSN *csn);

- int csnplRemoveAll (CSNPL *csnpl, const CSN *csn);

- int csnplCommitAll (CSNPL *csnpl, const CSN *csn);

+ int csnplRemoveAll (CSNPL *csnpl, const CSNPL_CTX *csn_ctx);

+ int csnplCommitAll (CSNPL *csnpl, const CSNPL_CTX *csn_ctx);

+ PRBool csn_primary(Replica *replica, const CSN *csn,  const CSNPL_CTX *csn_ctx);

  CSN* csnplGetMinCSN (CSNPL *csnpl, PRBool *committed);

  int csnplCommit (CSNPL *csnpl, const CSN *csn);

  CSN *csnplRollUp(CSNPL *csnpl, CSN ** first);

@@ -228,12 +228,27 @@

  int multimaster_be_betxnpostop_add (Slapi_PBlock *pb);

  int multimaster_be_betxnpostop_modify (Slapi_PBlock *pb);

  

+ /* In repl5_replica.c */

+ typedef struct replica Replica;

+ 

+ /* csn pending lists */

+ #define CSNPL_CTX_REPLCNT 4

+ typedef struct CSNPL_CTX

+ {

+ 	CSN *prim_csn;

+ 	size_t repl_alloc; /* max number of replicas  */

+ 	size_t repl_cnt; /* number of replicas affected by operation */

+ 	Replica *prim_repl; /* pirmary replica */

+ 	Replica **sec_repl; /* additional replicas affected */

+ } CSNPL_CTX;

+ 

  /* In repl5_init.c */

  extern int repl5_is_betxn;

  char* get_thread_private_agmtname(void);

  void  set_thread_private_agmtname (const char *agmtname);

- void  set_thread_primary_csn (const CSN *prim_csn);

- CSN*  get_thread_primary_csn(void);

+ void  set_thread_primary_csn (const CSN *prim_csn, Replica *repl);

+ void  add_replica_to_primcsn(CSNPL_CTX *prim_csn, Replica *repl);

+ CSNPL_CTX*  get_thread_primary_csn(void);

  void* get_thread_private_cache(void);

  void  set_thread_private_cache (void *buf);

  char* get_repl_session_id (Slapi_PBlock *pb, char *id, CSN **opcsn);
@@ -302,7 +317,6 @@

  

  /* In repl5_agmt.c */

  typedef struct repl5agmt Repl_Agmt;

- typedef struct replica Replica;

  

  #define TRANSPORT_FLAG_SSL 1

  #define TRANSPORT_FLAG_TLS 2
@@ -549,6 +563,7 @@

  PRBool replica_get_tombstone_reap_active(const Replica *r);

  const Slapi_DN *replica_get_root(const Replica *r);

  const char *replica_get_name(const Replica *r);

+ uint64_t replica_get_locking_conn(const Replica *r);

  ReplicaId replica_get_rid (const Replica *r);

  void replica_set_rid (Replica *r, ReplicaId rid);

  PRBool replica_is_initialized (const Replica *r);
@@ -605,7 +620,6 @@

  void replica_set_purge_delay (Replica *r, PRUint32 purge_delay);

  void replica_set_tombstone_reap_interval (Replica *r, long interval);

  void replica_update_ruv_consumer (Replica *r, RUV *supplier_ruv);

- void replica_set_ruv_dirty (Replica *r);

  Slapi_Entry *get_in_memory_ruv(Slapi_DN *suffix_sdn);

  int replica_write_ruv (Replica *r);

  char *replica_get_dn(Replica *r);
@@ -628,6 +642,8 @@

  void replica_set_precise_purging(Replica *r, PRUint64 on_off);

  PRBool ignore_error_and_keep_going(int error);

  void replica_check_release_timeout(Replica *r, Slapi_PBlock *pb);

+ void replica_lock_replica(Replica *r);

+ void replica_unlock_replica(Replica *r);

  

  /* The functions below handles the state flag */

  /* Current internal state flags */
@@ -664,12 +680,37 @@

  

  void multimaster_be_state_change (void *handle, char *be_name, int old_be_state, int new_be_state);

  

+ #define CLEANRIDSIZ 64 /* maximum number for concurrent CLEANALLRUV tasks */

+ 

+ typedef struct _cleanruv_data

+ {

+     Object *repl_obj;

+     Replica *replica;

+     ReplicaId rid;

+     Slapi_Task *task;

+     struct berval *payload;

+     CSN *maxcsn;

+     char *repl_root;

+     Slapi_DN *sdn;

+     char *certify;

+     char *force;

+     PRBool original_task;

+ } cleanruv_data;

+ 

+ typedef struct _cleanruv_purge_data

+ {

+     int cleaned_rid;

+     const Slapi_DN *suffix_sdn;

+     char *replName;

+     char *replGen;

+ } cleanruv_purge_data;

+ 

  /* In repl5_replica_config.c */

  int replica_config_init(void);

  void replica_config_destroy(void);

  int get_replica_type(Replica *r);

  int replica_execute_cleanruv_task_ext(Object *r, ReplicaId rid);

- void add_cleaned_rid(ReplicaId rid, Replica *r, char *maxcsn, char *forcing);

+ void add_cleaned_rid(cleanruv_data *data, char *maxcsn);

  int is_cleaned_rid(ReplicaId rid);

  int replica_cleanall_ruv_abort(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *eAfter,

                                 int *returncode, char *returntext, void *arg);
@@ -690,29 +731,6 @@

  void cleanruv_log(Slapi_Task *task, int rid, char *task_type, int sev_level, char *fmt, ...);

  char * replica_cleanallruv_get_local_maxcsn(ReplicaId rid, char *base_dn);

  

- #define CLEANRIDSIZ 64 /* maximum number for concurrent CLEANALLRUV tasks */

- 

- typedef struct _cleanruv_data

- {

- 	Object *repl_obj;

- 	Replica *replica;

- 	ReplicaId rid;

- 	Slapi_Task *task;

- 	struct berval *payload;

- 	CSN *maxcsn;

- 	char *repl_root;

- 	Slapi_DN *sdn;

- 	char *certify;

- 	char *force;

- } cleanruv_data;

- 

- typedef struct _cleanruv_purge_data

- {

- 	int cleaned_rid;

- 	const Slapi_DN *suffix_sdn;

- 	char *replName;

- 	char *replGen;

- } cleanruv_purge_data;

  

  /* replutil.c */

  LDAPControl* create_managedsait_control(void);

@@ -154,26 +154,62 @@

  		PR_SetThreadPrivate(thread_private_agmtname, (void *)agmtname);

  }

  

- CSN*

+ CSNPL_CTX*

  get_thread_primary_csn(void)

  {

- 	CSN *prim_csn = NULL;

+ 	CSNPL_CTX *prim_csn = NULL;

  	if (thread_primary_csn)

- 		prim_csn = (CSN *)PR_GetThreadPrivate(thread_primary_csn);

+ 		prim_csn = (CSNPL_CTX *)PR_GetThreadPrivate(thread_primary_csn);

+ 

  	return prim_csn;

  }

  void

- set_thread_primary_csn(const CSN *prim_csn)

+ set_thread_primary_csn (const CSN *prim_csn, Replica *repl)

  {

  	if (thread_primary_csn) {

  		if (prim_csn) {

- 			PR_SetThreadPrivate(thread_primary_csn, (void *)csn_dup(prim_csn));

+ 			CSNPL_CTX *csnpl_ctx = (CSNPL_CTX *)slapi_ch_calloc(1,sizeof(CSNPL_CTX));

+ 			csnpl_ctx->prim_csn = csn_dup(prim_csn);

+ 			/* repl_alloc, repl_cnt and sec_repl are 0 by calloc */

+ 			csnpl_ctx->prim_repl = repl;

+ 			PR_SetThreadPrivate(thread_primary_csn, (void *)csnpl_ctx);

  		} else {

  			PR_SetThreadPrivate(thread_primary_csn, NULL);

  		}

  	}

  }

  

+ void

+ add_replica_to_primcsn(CSNPL_CTX *csnpl_ctx, Replica *repl)

+ {

+ 	size_t found = 0;

+ 	size_t it = 0;

+ 

+ 	if (repl == csnpl_ctx->prim_repl) return;

+ 

+ 	while (it < csnpl_ctx->repl_cnt) {

+ 		if (csnpl_ctx->sec_repl[it] == repl) {

+ 			found = 1;

+ 			break;

+ 		}

+ 		it++;

+ 	}

+ 	if (found) return;

+ 

+ 	if (csnpl_ctx->repl_cnt < csnpl_ctx->repl_alloc) {

+ 		csnpl_ctx->sec_repl[csnpl_ctx->repl_cnt++] = repl;

+ 		return;

+ 	}

+ 	csnpl_ctx->repl_alloc += CSNPL_CTX_REPLCNT;

+ 	if (csnpl_ctx->repl_cnt == 0) {

+ 		csnpl_ctx->sec_repl = (Replica **)slapi_ch_calloc(csnpl_ctx->repl_alloc, sizeof(Replica *));

+ 	} else {

+ 		csnpl_ctx->sec_repl = (Replica **)slapi_ch_realloc((char *)csnpl_ctx->sec_repl, csnpl_ctx->repl_alloc * sizeof(Replica *));

+ 	}

+ 	csnpl_ctx->sec_repl[csnpl_ctx->repl_cnt++] = repl;

+ 	return;

+ }

+ 

  void*

  get_thread_private_cache ()

  {
@@ -740,7 +776,7 @@

  		/* Initialize thread private data for logging. Ignore if fails */

  		PR_NewThreadPrivateIndex (&thread_private_agmtname, NULL);

  		PR_NewThreadPrivateIndex (&thread_private_cache, NULL);

- 		PR_NewThreadPrivateIndex (&thread_primary_csn, csnplFreeCSN);

+ 		PR_NewThreadPrivateIndex (&thread_primary_csn, csnplFreeCSNPL_CTX);

  

  		/* Decode the command line args to see if we're dumping to LDIF */

  		is_ldif_dump = check_for_ldif_dump(pb);

@@ -45,6 +45,7 @@

  #include "repl.h"

  #include "cl5_api.h"

  #include "urp.h"

+ #include "csnpl.h"

  

  static char *local_purl = NULL;

  static char *purl_attrs[] = {"nsslapd-localhost", "nsslapd-port", "nsslapd-secureport", NULL};
@@ -1034,7 +1035,7 @@

  {

  	Slapi_Operation *op = NULL;

  	CSN *opcsn;

- 	CSN *prim_csn;

+ 	CSNPL_CTX *prim_csn;

  	int rc;

  	slapi_operation_parameters *op_params = NULL;

  	Object *repl_obj = NULL;
@@ -1070,14 +1071,15 @@

  	if (repl_obj == NULL)

  		return return_value;

  

+ 	r = (Replica*)object_get_data (repl_obj);

+ 	PR_ASSERT (r);

+ 

  	slapi_pblock_get(pb, SLAPI_RESULT_CODE, &rc);

  	if (rc) { /* op failed - just return */

  		cancel_opcsn(pb);

  		goto common_return;

  	}

  

- 	r = (Replica*)object_get_data (repl_obj);

- 	PR_ASSERT (r);

  

  	replica_check_release_timeout(r, pb);

  
@@ -1143,6 +1145,16 @@

  			goto common_return;

  		}

  

+                 /* Skip internal operations with no op csn if this is a read-only replica */

+                 if (op_params->csn == NULL &&

+                     operation_is_flag_set(op, OP_FLAG_INTERNAL) &&

+                     replica_get_type(r) == REPLICA_TYPE_READONLY)

+                 {

+                     slapi_log_err(SLAPI_LOG_REPL, "write_changelog_and_ruv",

+                                   "Skipping internal operation on read-only replica\n");

+                     goto common_return;

+                 }

+ 

  		/* we might have stripped all the mods - in that case we do not

  		   log the operation */

  		if (op_params->operation_type != SLAPI_OPERATION_MODIFY ||
@@ -1223,8 +1235,13 @@

  common_return:

  	opcsn = operation_get_csn(op);

  	prim_csn = get_thread_primary_csn();

- 	if (csn_is_equal(opcsn, prim_csn)) {

- 		set_thread_primary_csn(NULL);

+ 	if (csn_primary(r, opcsn, prim_csn)) {

+ 		if (return_value == 0) {

+ 			/* the primary csn was succesfully committed

+ 			 * unset it in the thread local data

+ 			 */

+ 			set_thread_primary_csn(NULL, NULL);

+ 		}

  	}

  	if (repl_obj) {

  		object_release (repl_obj);
@@ -1425,7 +1442,7 @@

  

              ruv_obj = replica_get_ruv (r);

              PR_ASSERT (ruv_obj);

-             ruv_cancel_csn_inprogress ((RUV*)object_get_data (ruv_obj), opcsn, replica_get_rid(r));

+             ruv_cancel_csn_inprogress (r, (RUV*)object_get_data (ruv_obj), opcsn, replica_get_rid(r));

              object_release (ruv_obj);

          }

  
@@ -1486,7 +1503,7 @@

      ruv = (RUV*)object_get_data (ruv_obj);

      PR_ASSERT (ruv);

   

-     rc = ruv_add_csn_inprogress (ruv, csn);

+     rc = ruv_add_csn_inprogress (r, ruv, csn);

  

      object_release (ruv_obj);

      object_release (r_obj);

@@ -46,7 +46,6 @@

  	char*   legacy_purl;            /* partial url of the legacy supplier   */

  	ReplicaId repl_rid;				/* replicaID							*/

  	Object	*repl_ruv;				/* replica update vector				*/

- 	PRBool repl_ruv_dirty;          /* Dirty flag for ruv                   */

  	CSNPL *min_csn_pl;              /* Pending list for minimal CSN         */

  	void *csn_pl_reg_id;            /* registration assignment for csn callbacks */

  	unsigned long repl_state_flags;	/* state flags							*/
@@ -64,6 +63,7 @@

  	PRBool state_update_inprogress; /* replica state is being updated */

  	PRLock *agmt_lock;          	/* protects agreement creation, start and stop */

  	char *locking_purl;				/* supplier who has exclusive access */

+ 	uint64_t locking_conn;         	/* The supplier's connection id */

  	Slapi_Counter *protocol_timeout;/* protocol shutdown timeout */

  	Slapi_Counter *backoff_min;		/* backoff retry minimum */

  	Slapi_Counter *backoff_max;		/* backoff retry maximum */
@@ -602,19 +602,32 @@

  				slapi_sdn_get_dn(r->repl_root),

  				r->locking_purl ? r->locking_purl : "unknown");

  		rval = PR_FALSE;

+ 		if (!(r->repl_state_flags & REPLICA_TOTAL_IN_PROGRESS)) {

+ 			/* inc update */

+ 			if (r->locking_purl && r->locking_conn == connid) {

+ 				/* This is the same supplier connection, reset the replica

+ 				 * purl, and return success */

+ 				slapi_log_err(SLAPI_LOG_REPL, repl_plugin_name,

+ 					"replica_get_exclusive_access - "

+ 					"This is a second acquire attempt from the same replica connection "

+ 					" - return success instead of busy\n");

+ 				slapi_ch_free_string(&r->locking_purl);

+ 				r->locking_purl = slapi_ch_strdup(locking_purl);

+ 				rval = PR_TRUE;

+ 				goto done;

+ 			}

+ 			if (replica_get_release_timeout(r)) {

+ 				/*

+ 				 * Abort the current session so other replicas can acquire

+ 				 * this server.

+ 				 */

+ 				r->abort_session = ABORT_SESSION;

+ 			}

+ 		}

  		if (current_purl)

  		{

  			*current_purl = slapi_ch_strdup(r->locking_purl);

  		}

- 		if (!(r->repl_state_flags & REPLICA_TOTAL_IN_PROGRESS) &&

- 		    replica_get_release_timeout(r))

- 		{

- 			/*

- 			 * We are not doing a total update, so abort the current session

- 			 * so other replicas can acquire this server.

- 			 */

- 			r->abort_session = ABORT_SESSION;

- 		}

  	}

  	else

  	{
@@ -642,7 +655,9 @@

  		}

  		slapi_ch_free_string(&r->locking_purl);

  		r->locking_purl = slapi_ch_strdup(locking_purl);

+ 		r->locking_conn = connid;

  	}

+ done:

  	replica_unlock(r->repl_lock);

  	return rval;

  }
@@ -720,6 +735,18 @@

      return(r->repl_name);

  }

  

+ /*

+  * Returns locking_conn of this replica

+  */

+ uint64_t

+ replica_get_locking_conn(const Replica *r)

+ {

+ 	uint64_t connid;

+ 	replica_lock(r->repl_lock);

+ 	connid = r->locking_conn;

+ 	replica_unlock(r->repl_lock);

+ 	return connid;

+ }

  /* 

   * Returns replicaid of this replica 

   */
@@ -827,7 +854,6 @@

      }

  

  	r->repl_ruv = object_new((void*)ruv, (FNFree)ruv_destroy);

- 	r->repl_ruv_dirty = PR_TRUE;

  

  	replica_unlock(r->repl_lock);

  }
@@ -895,7 +921,7 @@

  					}

  				}

  				/* Update max csn for local and remote replicas */

- 				rc = ruv_update_ruv (ruv, updated_csn, replica_purl, r->repl_rid);

+ 				rc = ruv_update_ruv (ruv, updated_csn, replica_purl, r, r->repl_rid);

  				if (RUV_COVERS_CSN == rc)

  				{

  					slapi_log_err(SLAPI_LOG_REPL,
@@ -913,11 +939,6 @@

  						slapi_sdn_get_dn(r->repl_root),

  						csn_as_string(updated_csn, PR_FALSE, csn_str));

  				}

- 				else

- 				{

- 					/* RUV updated - mark as dirty */

- 					r->repl_ruv_dirty = PR_TRUE;

- 				}

  			}

  			else

  			{
@@ -1498,8 +1519,6 @@

      slapi_log_err(SLAPI_LOG_REPL, repl_plugin_name, "\tupdate dn: %s\n",

              updatedn_list? updatedn_list : "not configured");

      slapi_ch_free_string(&updatedn_list);

-     slapi_log_err(SLAPI_LOG_REPL, repl_plugin_name, "\truv: %s configured and is %sdirty\n",

-                     r->repl_ruv ? "" : "not", r->repl_ruv_dirty ? "" : "not ");

      slapi_log_err(SLAPI_LOG_REPL, repl_plugin_name, "\tCSN generator: %s configured\n",

                      r->repl_csngen ? "" : "not");

  	/* JCMREPL - Dump Referrals */
@@ -1849,7 +1868,6 @@

  

                      ruv_force_csn_update_from_ruv(upper_bound_ruv, r_ruv, 

                              "Force update of database RUV (from CL RUV) -> ", SLAPI_LOG_NOTICE);

-                     replica_set_ruv_dirty(r);

                  }

                  

              } else {
@@ -2251,6 +2269,9 @@

      }

  

      r->tombstone_reap_stop = r->tombstone_reap_active = PR_FALSE;

+     

+     /* No supplier holding the replica */

+     r->locking_conn = ULONG_MAX;

  

      return (_replica_check_validity (r));

  }
@@ -2277,6 +2298,7 @@

          char csnstr[CSN_STRSIZE];

          char *token = NULL;

          char *forcing;

+         PRBool original_task;

          char *csnpart;

          char *ridstr;

          char *iter = NULL;
@@ -2306,8 +2328,15 @@

              csn_init_by_string(maxcsn, csnpart);

              csn_as_string(maxcsn, PR_FALSE, csnstr);

              forcing = ldap_utf8strtok_r(iter, ":", &iter);

-             if(forcing == NULL){

+             original_task = PR_TRUE;

+             if (forcing == NULL){

                  forcing = "no";

+             } else if (!strcasecmp(forcing, "yes") || !strcasecmp(forcing, "no")) {

+                 /* forcing was correctly set, lets try to read the original task flag */

+                 token = ldap_utf8strtok_r(iter, ":", &iter);

+                 if (token && !atoi(token)) {

+                     original_task = PR_FALSE;

+                 }

              }

  

              slapi_log_err(SLAPI_LOG_NOTICE, repl_plugin_name, "CleanAllRUV Task - cleanAllRUV task found, "
@@ -2344,6 +2373,13 @@

                  data->force = slapi_ch_strdup(forcing);

                  data->repl_root = NULL;

  

+                 /* This is a corner case, a cleanAllRuv task was interrupted by a shutdown or a crash

+                  * We retrieved from type_replicaCleanRUV if the cleanAllRuv request

+                  * was received from a direct task ADD or if was received via

+                  * the cleanAllRuv extop.

+                  */

+                 data->original_task = original_task;

+ 

                  thread = PR_CreateThread(PR_USER_THREAD, replica_cleanallruv_thread_ext,

                          (void *)data, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,

                          PR_UNJOINABLE_THREAD, SLAPD_DEFAULT_THREAD_STACKSIZE);
@@ -2434,6 +2470,12 @@

                      data->sdn = slapi_sdn_dup(r->repl_root);

                      data->certify = slapi_ch_strdup(certify);

  

+                     /* This is a corner case, a cleanAllRuv task was interrupted by a shutdown or a crash

+                      * Let's assum this replica was the original receiver of the task.

+                      * This flag has no impact on Abort cleanAllRuv

+                      */

+                     data->original_task = PR_TRUE;

+ 

                      thread = PR_CreateThread(PR_USER_THREAD, replica_abort_task_thread,

                              (void *)data, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,

                              PR_UNJOINABLE_THREAD, SLAPD_DEFAULT_THREAD_STACKSIZE);
@@ -2963,12 +3005,6 @@

  

  	replica_lock(r->repl_lock);

  

- 	if (!r->repl_ruv_dirty)

- 	{

- 		replica_unlock(r->repl_lock);

- 		return rc;

- 	}

- 

  	PR_ASSERT (r->repl_ruv);

  	

  	ruv_to_smod ((RUV*)object_get_data(r->repl_ruv), &smod);
@@ -3003,19 +3039,13 @@

      /* ruv does not exist - create one */

      replica_lock(r->repl_lock);

  

-     if (rc == LDAP_SUCCESS)

-     {

-         r->repl_ruv_dirty = PR_FALSE;

-     }

-     else if (rc == LDAP_NO_SUCH_OBJECT)

+     if (rc == LDAP_NO_SUCH_OBJECT)

      {

          /* this includes an internal operation - but since this only happens

             during server startup - its ok that we have lock around it */

          rc = _replica_configure_ruv  (r, PR_TRUE);

-         if (rc == 0)

-             r->repl_ruv_dirty = PR_FALSE;

      }

- 	else /* error */

+ 	else if (rc != LDAP_SUCCESS) /* error */

  	{

  		slapi_log_err(SLAPI_LOG_REPL, repl_plugin_name, 

  			"replica_write_ruv - Failed to update RUV tombstone for %s; "
@@ -3539,7 +3569,6 @@

  

              if (ruv_init_new(csnstr, r->repl_rid, purl, &ruv) == RUV_SUCCESS){

                  r->repl_ruv = object_new((void*)ruv, (FNFree)ruv_destroy);

-                 r->repl_ruv_dirty = PR_TRUE;

                  return_value = LDAP_SUCCESS;

              } else {

                  slapi_log_err(SLAPI_LOG_ERR, repl_plugin_name, "replica_create_ruv_tombstone - "
@@ -3579,8 +3608,6 @@

      slapi_add_internal_pb(pb);

      e = NULL; /* add consumes e, upon success or failure */

      slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &return_value);

-     if (return_value == LDAP_SUCCESS)

-         r->repl_ruv_dirty = PR_FALSE;

  		

  done:

      slapi_entry_free (e);
@@ -3632,7 +3659,7 @@

          }

      }

  

-     ruv_add_csn_inprogress (ruv, csn);

+     ruv_add_csn_inprogress (r, ruv, csn);

  

      replica_unlock(r->repl_lock);

  
@@ -3661,13 +3688,13 @@

      {

          int rc = csnplRemove(r->min_csn_pl, csn);

          if (rc) {

-             slapi_log_err(SLAPI_LOG_ERR, repl_plugin_name, "abort_csn_callback - csnplRemove failed");

+             slapi_log_err(SLAPI_LOG_ERR, repl_plugin_name, "abort_csn_callback - csnplRemove failed\n");

              replica_unlock(r->repl_lock);

              return;

          }

      }

  

-     ruv_cancel_csn_inprogress (ruv, csn, replica_get_rid(r));

+     ruv_cancel_csn_inprogress (r, ruv, csn, replica_get_rid(r));

      replica_unlock(r->repl_lock);

  

      object_release (ruv_obj);
@@ -3899,7 +3926,6 @@

      ruv_get_cleaned_rids(ruv, rid);

      while(rid[i] != 0){

          ruv_delete_replica(ruv, rid[i]);

-         replica_set_ruv_dirty(r);

          if (replica_write_ruv(r)) {

              slapi_log_err(SLAPI_LOG_REPL, repl_plugin_name,

                      "replica_strip_cleaned_rids - Failed to write RUV\n");
@@ -4021,15 +4047,6 @@

  	}

  }

  

- void 

- replica_set_ruv_dirty(Replica *r)

- {

- 	PR_ASSERT(r);

- 	replica_lock(r->repl_lock);

- 	r->repl_ruv_dirty = PR_TRUE;

- 	replica_unlock(r->repl_lock);

- }

- 

  PRBool

  replica_is_state_flag_set(Replica *r, PRInt32 flag)

  {
@@ -4458,3 +4475,13 @@

  	}

  	replica_unlock(r->repl_lock);

  }

+ void

+ replica_lock_replica(Replica *r)

+ {

+ 	replica_lock(r->repl_lock);

+ }

+ void

+ replica_unlock_replica(Replica *r)

+ {

+ 	replica_unlock(r->repl_lock);

+ }

@@ -1055,7 +1055,6 @@

                      replica_reset_csn_pl(r);

                  }

                  ruv_delete_replica(ruv, oldrid);

-                 replica_set_ruv_dirty(r);

                  cl5CleanRUV(oldrid);

                  replica_set_csn_assigned(r);

              }
@@ -1475,7 +1474,6 @@

  		return LDAP_UNWILLING_TO_PERFORM;

  	}

  	rc = ruv_delete_replica(local_ruv, rid);

- 	replica_set_ruv_dirty(replica);

  	if (replica_write_ruv(replica)) {

  		slapi_log_err(SLAPI_LOG_REPL, repl_plugin_name, "cleanAllRUV_task - Could not write RUV\n");

  	}
@@ -1727,6 +1725,11 @@

      data->repl_root = slapi_ch_strdup(basedn);

      data->force = slapi_ch_strdup(force_cleaning);

  

+     /* It is either a consequence of a direct ADD cleanAllRuv task

+      * or modify of the replica to add nsds5task: cleanAllRuv

+      */

+     data->original_task = PR_TRUE;

+ 

      thread = PR_CreateThread(PR_USER_THREAD, replica_cleanallruv_thread,

          (void *)data, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,

          PR_UNJOINABLE_THREAD, SLAPD_DEFAULT_THREAD_STACKSIZE);
@@ -1850,7 +1853,7 @@

      /*

       *  Add the cleanallruv task to the repl config - so we can handle restarts

       */

-     add_cleaned_rid(data->rid, data->replica, csnstr, data->force); /* marks config that we started cleaning a rid */

+     add_cleaned_rid(data, csnstr); /* marks config that we started cleaning a rid */

      cleanruv_log(data->task, data->rid, CLEANALLRUV_ID, SLAPI_LOG_INFO, "Cleaning rid (%d)...", data->rid);

      /*

       *  First, wait for the maxcsn to be covered
@@ -1936,12 +1939,13 @@

          /*

           *  need to sleep between passes

           */

-         cleanruv_log(data->task, data->rid, CLEANALLRUV_ID, LOG_NOTICE, "Not all replicas have received the "

-             "cleanallruv extended op, retrying in %d seconds",interval);

-         if(!slapi_is_shutting_down()){

-             PR_Lock( notify_lock );

-             PR_WaitCondVar( notify_cvar, PR_SecondsToInterval(interval) );

-             PR_Unlock( notify_lock );

+         cleanruv_log(data->task, data->rid, CLEANALLRUV_ID, SLAPI_LOG_NOTICE, "Not all replicas have received the "

+                      "cleanallruv extended op, retrying in %d seconds",

+                      interval);

+         if (!slapi_is_shutting_down()) {

+             PR_Lock(notify_lock);

+             PR_WaitCondVar(notify_cvar, PR_SecondsToInterval(interval));

+             PR_Unlock(notify_lock);

          }

          if(interval < 14400){ /* 4 hour max */

              interval = interval * 2;
@@ -1977,8 +1981,8 @@

                  found_dirty_rid = 0;

              } else {

                  found_dirty_rid = 1;

-                 cleanruv_log(data->task, data->rid, CLEANALLRUV_ID, LOG_NOTICE, "Replica is not cleaned yet (%s)",

-                         agmt_get_long_name(agmt));

+                 cleanruv_log(data->task, data->rid, CLEANALLRUV_ID, SLAPI_LOG_NOTICE, "Replica is not cleaned yet (%s)",

+                              agmt_get_long_name(agmt));

                  break;

              }

              agmt_obj = agmtlist_get_next_agreement_for_replica (data->replica, agmt_obj);
@@ -1994,12 +1998,13 @@

          /*

           * Need to sleep between passes unless we are shutting down

           */

-         if (!slapi_is_shutting_down()){

-             cleanruv_log(data->task, data->rid, CLEANALLRUV_ID, LOG_NOTICE, "Replicas have not been cleaned yet, "

-                 "retrying in %d seconds", interval);

-             PR_Lock( notify_lock );

-             PR_WaitCondVar( notify_cvar, PR_SecondsToInterval(interval) );

-             PR_Unlock( notify_lock );

+         if (!slapi_is_shutting_down()) {

+             cleanruv_log(data->task, data->rid, CLEANALLRUV_ID, SLAPI_LOG_NOTICE, "Replicas have not been cleaned yet, "

+                                                                             "retrying in %d seconds",

+                          interval);

+             PR_Lock(notify_lock);

+             PR_WaitCondVar(notify_cvar, PR_SecondsToInterval(interval));

+             PR_Unlock(notify_lock);

          }

  

          if(interval < 14400){ /* 4 hour max */
@@ -2025,7 +2030,13 @@

           */

          delete_cleaned_rid_config(data);

          check_replicas_are_done_cleaning(data);

-         remove_keep_alive_entry(data->task, data->rid, data->repl_root);

+         if (data->original_task) {

+             cleanruv_log(data->task, data->rid, CLEANALLRUV_ID, SLAPI_LOG_INFO, "Original task deletes Keep alive entry (%d).", data->rid);

+             remove_keep_alive_entry(data->task, data->rid, data->repl_root);

+         } else {

+             cleanruv_log(data->task, data->rid, CLEANALLRUV_ID, SLAPI_LOG_INFO, "Propagated task does not delete Keep alive entry (%d).", data->rid);

+         }

+ 

          clean_agmts(data);

          remove_cleaned_rid(data->rid);

          cleanruv_log(data->task, data->rid, CLEANALLRUV_ID, SLAPI_LOG_INFO, "Successfully cleaned rid(%d).", data->rid);
@@ -2033,11 +2044,11 @@

          /*

           *  Shutdown or abort

           */

-         if(!is_task_aborted(data->rid) || slapi_is_shutting_down()){

-             cleanruv_log(data->task, data->rid, CLEANALLRUV_ID, LOG_NOTICE,

-                     "Server shutting down.  Process will resume at server startup");

+         if (!is_task_aborted(data->rid) || slapi_is_shutting_down()) {

+             cleanruv_log(data->task, data->rid, CLEANALLRUV_ID, SLAPI_LOG_NOTICE,

+                          "Server shutting down.  Process will resume at server startup");

          } else {

-             cleanruv_log(data->task, data->rid, CLEANALLRUV_ID, LOG_NOTICE, "Task aborted for rid(%d).",data->rid);

+             cleanruv_log(data->task, data->rid, CLEANALLRUV_ID, SLAPI_LOG_NOTICE, "Task aborted for rid(%d).", data->rid);

              delete_cleaned_rid_config(data);

              remove_cleaned_rid(data->rid);

          }
@@ -2176,7 +2187,7 @@

          "Waiting for all the replicas to finish cleaning...");

  

      csn_as_string(data->maxcsn, PR_FALSE, csnstr);

-     filter = PR_smprintf("(%s=%d:%s:%s)", type_replicaCleanRUV,(int)data->rid, csnstr, data->force);

+     filter = PR_smprintf("(%s=%d:%s:%s:%d)", type_replicaCleanRUV, (int)data->rid, csnstr, data->force, data->original_task ? 1 : 0);

      while(not_all_cleaned && !is_task_aborted(data->rid) && !slapi_is_shutting_down()){

          agmt_obj = agmtlist_get_first_agreement_for_replica (data->replica);

          if(agmt_obj == NULL){
@@ -2205,7 +2216,7 @@

              break;

          }

  

-         cleanruv_log(data->task, data->rid, CLEANALLRUV_ID, LOG_NOTICE,

+         cleanruv_log(data->task, data->rid, CLEANALLRUV_ID, SLAPI_LOG_NOTICE,

                       "Not all replicas finished cleaning, retrying in %d seconds",

                       interval);

          if(!slapi_is_shutting_down()){
@@ -2315,12 +2326,12 @@

          if(not_all_aborted == 0){

              break;

          }

-         cleanruv_log(data->task, data->rid, ABORT_CLEANALLRUV_ID, LOG_NOTICE,

-                 "Not all replicas finished aborting, retrying in %d seconds",interval);

-         if(!slapi_is_shutting_down()){

-             PR_Lock( notify_lock );

-             PR_WaitCondVar( notify_cvar, PR_SecondsToInterval(interval) );

-             PR_Unlock( notify_lock );

+         cleanruv_log(data->task, data->rid, ABORT_CLEANALLRUV_ID, SLAPI_LOG_NOTICE,

+                      "Not all replicas finished aborting, retrying in %d seconds", interval);

+         if (!slapi_is_shutting_down()) {

+             PR_Lock(notify_lock);

+             PR_WaitCondVar(notify_cvar, PR_SecondsToInterval(interval));

+             PR_Unlock(notify_lock);

          }

          if(interval < 14400){ /* 4 hour max */

              interval = interval * 2;
@@ -2362,8 +2373,8 @@

                  not_all_caughtup = 0;

              } else {

                  not_all_caughtup = 1;

-                 cleanruv_log(data->task, data->rid, CLEANALLRUV_ID, LOG_NOTICE,

-                         "Replica not caught up (%s)",agmt_get_long_name(agmt));

+                 cleanruv_log(data->task, data->rid, CLEANALLRUV_ID, SLAPI_LOG_NOTICE,

+                              "Replica not caught up (%s)", agmt_get_long_name(agmt));

                  break;

              }

              agmt_obj = agmtlist_get_next_agreement_for_replica (data->replica, agmt_obj);
@@ -2372,12 +2383,12 @@

          if(not_all_caughtup == 0 || is_task_aborted(data->rid) ){

              break;

          }

-         cleanruv_log(data->task, data->rid, CLEANALLRUV_ID, LOG_NOTICE,

-                 "Not all replicas caught up, retrying in %d seconds",interval);

-         if(!slapi_is_shutting_down()){

-             PR_Lock( notify_lock );

-             PR_WaitCondVar( notify_cvar, PR_SecondsToInterval(interval) );

-             PR_Unlock( notify_lock );

+         cleanruv_log(data->task, data->rid, CLEANALLRUV_ID, SLAPI_LOG_NOTICE,

+                      "Not all replicas caught up, retrying in %d seconds", interval);

+         if (!slapi_is_shutting_down()) {

+             PR_Lock(notify_lock);

+             PR_WaitCondVar(notify_cvar, PR_SecondsToInterval(interval));

+             PR_Unlock(notify_lock);

          }

          if(interval < 14400){ /* 4 hour max */

              interval = interval * 2;
@@ -2422,8 +2433,8 @@

                  not_all_alive = 0;

              } else {

                  not_all_alive = 1;

-                 cleanruv_log(task, rid, CLEANALLRUV_ID, LOG_NOTICE, "Replica not online (%s)",

-                         agmt_get_long_name(agmt));

+                 cleanruv_log(task, rid, CLEANALLRUV_ID, SLAPI_LOG_NOTICE, "Replica not online (%s)",

+                              agmt_get_long_name(agmt));

                  break;

              }

              agmt_obj = agmtlist_get_next_agreement_for_replica (replica, agmt_obj);
@@ -2432,8 +2443,8 @@

          if(not_all_alive == 0 || is_task_aborted(rid)){

              break;

          }

-         cleanruv_log(task, rid, CLEANALLRUV_ID, LOG_NOTICE, "Not all replicas online, retrying in %d seconds...",

-                 interval);

+         cleanruv_log(task, rid, CLEANALLRUV_ID, SLAPI_LOG_NOTICE, "Not all replicas online, retrying in %d seconds...",

+                      interval);

  

          if(!slapi_is_shutting_down()){

              PR_Lock( notify_lock );
@@ -2650,7 +2661,7 @@

   *  Add the rid and maxcsn to the repl config (so we can resume after a server restart)

   */

  void

- add_cleaned_rid(ReplicaId rid, Replica *r, char *maxcsn, char *forcing)

+ add_cleaned_rid(cleanruv_data *cleanruv_data, char *maxcsn)

  {

      Slapi_PBlock *pb;

      struct berval *vals[2];
@@ -2660,6 +2671,16 @@

      char data[CSN_STRSIZE + 10];

      char *dn;

      int rc;

+     ReplicaId rid;

+     Replica *r;

+     char *forcing;

+ 

+     if (data == NULL) {

+         return;

+     }

+     rid = cleanruv_data->rid;

+     r = cleanruv_data->replica;

+     forcing = cleanruv_data->force;

  

      if(r == NULL || maxcsn == NULL){

          return;
@@ -2667,7 +2688,7 @@

      /*

       *  Write the rid & maxcsn to the config entry

       */

-     val.bv_len = PR_snprintf(data, sizeof(data),"%d:%s:%s", rid, maxcsn, forcing);

+     val.bv_len = PR_snprintf(data, sizeof(data), "%d:%s:%s:%d", rid, maxcsn, forcing, cleanruv_data->original_task ? 1 : 0);

      dn = replica_get_dn(r);

      pb = slapi_pblock_new();

      mod.mod_op  = LDAP_MOD_ADD|LDAP_MOD_BVALUES;
@@ -3099,6 +3120,7 @@

      data->repl_root = slapi_ch_strdup(base_dn);

      data->sdn = NULL;

      data->certify = slapi_ch_strdup(certify_all);

+     data->original_task = PR_TRUE;

  

      thread = PR_CreateThread(PR_USER_THREAD, replica_abort_task_thread,

                  (void *)data, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
@@ -3204,11 +3226,11 @@

          /*

           *  Need to sleep between passes. unless we are shutting down

           */

-         if (!slapi_is_shutting_down()){

-             cleanruv_log(data->task, data->rid, ABORT_CLEANALLRUV_ID, LOG_NOTICE, "Retrying in %d seconds",interval);

-             PR_Lock( notify_lock );

-             PR_WaitCondVar( notify_cvar, PR_SecondsToInterval(interval) );

-             PR_Unlock( notify_lock );

+         if (!slapi_is_shutting_down()) {

+             cleanruv_log(data->task, data->rid, ABORT_CLEANALLRUV_ID, SLAPI_LOG_NOTICE, "Retrying in %d seconds", interval);

+             PR_Lock(notify_lock);

+             PR_WaitCondVar(notify_cvar, PR_SecondsToInterval(interval));

+             PR_Unlock(notify_lock);

          }

  

          if(interval < 14400){ /* 4 hour max */
@@ -3326,9 +3348,10 @@

                      /* extop was accepted */

                      rc = 0;

                  } else {

-                     cleanruv_log(clean_data->task, clean_data->rid, CLEANALLRUV_ID, LOG_NOTICE, 

-                             "Replica %s does not support the CLEANALLRUV task.  "

-                             "Sending replica CLEANRUV task...", slapi_sdn_get_dn(agmt_get_dn_byref(ra)));

+                     cleanruv_log(clean_data->task, clean_data->rid, CLEANALLRUV_ID, SLAPI_LOG_NOTICE,

+                                  "Replica %s does not support the CLEANALLRUV task.  "

+                                  "Sending replica CLEANRUV task...",

+                                  slapi_sdn_get_dn(agmt_get_dn_byref(ra)));

                      /*

                       *  Ok, this replica doesn't know about CLEANALLRUV, so just manually

                       *  add the CLEANRUV task to the replica.
@@ -3493,9 +3516,9 @@

                      csn_init_by_string(repl_max, remote_maxcsn);

                      if(csn_compare (repl_max, max) < 0){

                          /* we are not caught up yet, free, and return */

-                         cleanruv_log(task, atoi(rid_text), CLEANALLRUV_ID, LOG_NOTICE,

-                             "Replica maxcsn (%s) is not caught up with deleted replica's maxcsn(%s)",

-                             remote_maxcsn, maxcsn);

+                         cleanruv_log(task, atoi(rid_text), CLEANALLRUV_ID, SLAPI_LOG_NOTICE,

+                                      "Replica maxcsn (%s) is not caught up with deleted replica's maxcsn(%s)",

+                                      remote_maxcsn, maxcsn);

                          rc = -1;

                      } else {

                          /* ok this replica is caught up */
@@ -3666,7 +3689,6 @@

  void

  cleanruv_log(Slapi_Task *task, int rid, char *task_type, int sev_level, char *fmt, ...)

  {

- #ifdef LDAP_DEBUG

      va_list ap1;

      va_list ap2;

      va_list ap3;
@@ -3691,7 +3713,6 @@

      va_end(ap2);

      va_end(ap3);

      va_end(ap4);

- #endif

  }

  

  char *

@@ -77,7 +77,7 @@

  static const char * const prefix_replicageneration = "{replicageneration}";

  static const char * const prefix_ruvcsn = "{replica "; /* intentionally missing '}' */

  

- static int ruv_update_ruv_element (RUV *ruv, RUVElement *replica, const CSN *csn, const char *replica_purl, PRBool isLocal);

+ static int ruv_update_ruv_element (RUV *ruv, RUVElement *replica, const CSNPL_CTX *prim_csn, const char *replica_purl, PRBool isLocal);

  

  /* API implementation */

  
@@ -1502,7 +1502,17 @@

   * Extract all the referral URL's from the RUV (but self URL),

   * returning them in an array of strings, that

   * the caller must free.

+  * We also check and remove duplicates (caused by unclean RUVs)

   */

+ static int

+ ruv_referral_exists(unsigned char *purl, char **refs, int count)

+ {

+     for (size_t j=0; j<count; j++) {

+         if (0 == slapi_utf8casecmp(purl, (unsigned char *)refs[j]))

+             return 1;

+     }

+     return 0;

+ }

  char **

  ruv_get_referrals(const RUV *ruv)

  {
@@ -1525,7 +1535,8 @@

  			/* Add URL into referrals if doesn't match self URL */

  			if((replica->replica_purl!=NULL) &&

  			   (slapi_utf8casecmp((unsigned char *)replica->replica_purl,

- 								  (unsigned char *)mypurl) != 0))

+                                    (unsigned char *)mypurl) != 0) &&

+ 			    !ruv_referral_exists((unsigned char *)replica->replica_purl, r, i))

  			{

  		 		r[i]= slapi_ch_strdup(replica->replica_purl);

  				i++;
@@ -1599,13 +1610,13 @@

  

  /* this function notifies the ruv that there are operations in progress so that

     they can be added to the pending list for the appropriate client. */

- int ruv_add_csn_inprogress (RUV *ruv, const CSN *csn)

+ int ruv_add_csn_inprogress (void *repl, RUV *ruv, const CSN *csn)

  {

      RUVElement* replica;

      char csn_str[CSN_STRSIZE];

      int rc = RUV_SUCCESS;

      int rid = csn_get_replicaid (csn);

-     CSN *prim_csn;

+     CSNPL_CTX *prim_csn;

  

      PR_ASSERT (ruv && csn);

  
@@ -1645,8 +1656,13 @@

      }

      prim_csn = get_thread_primary_csn();

      if (prim_csn == NULL) {

-         set_thread_primary_csn(csn);

+         set_thread_primary_csn(csn, (Replica *)repl);

          prim_csn = get_thread_primary_csn();

+     } else {

+ 	/* the prim csn data already exist, need to check if

+ 	 * current replica is already present

+ 	 */

+ 	add_replica_to_primcsn(prim_csn, (Replica *)repl);

      }

      rc = csnplInsert (replica->csnpl, csn, prim_csn);

      if (rc == 1)    /* we already seen this csn */
@@ -1656,7 +1672,7 @@

                              "The csn %s has already be seen - ignoring\n",

                              csn_as_string (csn, PR_FALSE, csn_str));

          }

-         set_thread_primary_csn(NULL);

+         set_thread_primary_csn(NULL, NULL);

          rc = RUV_COVERS_CSN;    

      }

      else if(rc != 0)
@@ -1681,11 +1697,13 @@

      return rc;

  }

  

- int ruv_cancel_csn_inprogress (RUV *ruv, const CSN *csn, ReplicaId local_rid)

+ int ruv_cancel_csn_inprogress (void *repl, RUV *ruv, const CSN *csn, ReplicaId local_rid)

  {

-     RUVElement* replica;

+     RUVElement* repl_ruv;

      int rc = RUV_SUCCESS;

-     CSN *prim_csn = NULL;

+     CSNPL_CTX *prim_csn = NULL;

+     Replica *repl_it;

+     size_t it;

  

  

      PR_ASSERT (ruv && csn);
@@ -1693,29 +1711,53 @@

      prim_csn = get_thread_primary_csn();

      /* locate ruvElement */

      slapi_rwlock_wrlock (ruv->lock);

-     replica = ruvGetReplica (ruv, csn_get_replicaid (csn));

-     if (replica == NULL) {

+     repl_ruv = ruvGetReplica (ruv, csn_get_replicaid (csn));

+     if (repl_ruv == NULL) {

          /* ONREPL - log error */

  	rc = RUV_NOTFOUND;

  	goto done;

      }

-     if (csn_is_equal(csn, prim_csn)) {

- 	/* the prim csn is cancelled, lets remove all dependent csns */

- 	ReplicaId prim_rid = csn_get_replicaid (csn);

- 	replica = ruvGetReplica (ruv, prim_rid);

- 	rc = csnplRemoveAll (replica->csnpl, prim_csn);

- 	if (prim_rid != local_rid) {

- 		if( local_rid != READ_ONLY_REPLICA_ID) {

- 			replica = ruvGetReplica (ruv, local_rid);

- 			if (replica) {

- 				rc = csnplRemoveAll (replica->csnpl, prim_csn);

- 			} else {

- 				rc = RUV_NOTFOUND;

- 			}

- 		}

- 	}

+     if (csn_primary(repl, csn, prim_csn)) {

+         /* the prim csn is cancelled, lets remove all dependent csns */

+         /* for the primary replica we can have modifications for two RIDS:

+          * - the local RID for direct or internal operations

+          * - a remote RID if the primary csn is for a replciated op.

+          */

+         ReplicaId prim_rid = csn_get_replicaid(csn);

+         repl_ruv = ruvGetReplica(ruv, prim_rid);

+         if (!repl_ruv) {

+             rc = RUV_NOTFOUND;

+             goto done;

+         }

+         rc = csnplRemoveAll(repl_ruv->csnpl, prim_csn);

+ 

+         if (prim_rid != local_rid && local_rid != READ_ONLY_REPLICA_ID) {

+             repl_ruv = ruvGetReplica(ruv, local_rid);

+             if (!repl_ruv) {

+                 rc = RUV_NOTFOUND;

+                 goto done;

+             }

+             rc = csnplRemoveAll(repl_ruv->csnpl, prim_csn);

+         }

+ 

+         for (it = 0; it < prim_csn->repl_cnt; it++) {

+             repl_it = prim_csn->sec_repl[it];

+             replica_lock_replica(repl_it);

+             local_rid = replica_get_rid(repl_it);

+             if (local_rid != READ_ONLY_REPLICA_ID) {

+                 Object *ruv_obj = replica_get_ruv(repl_it);

+                 RUV *ruv_it = object_get_data(ruv_obj);

+                 repl_ruv = ruvGetReplica(ruv_it, local_rid);

+                 if (repl_ruv) {

+                     rc = csnplRemoveAll(repl_ruv->csnpl, prim_csn);

+                 } else {

+                     rc = RUV_NOTFOUND;

+                 }

+             }

+             replica_unlock_replica(repl_it);

+         }

      } else {

- 	rc = csnplRemove (replica->csnpl, csn);

+ 	rc = csnplRemove (repl_ruv->csnpl, csn);

      }

      if (rc != 0)

          rc = RUV_NOTFOUND;
@@ -1727,86 +1769,100 @@

      return rc;

  }

  

- int ruv_update_ruv (RUV *ruv, const CSN *csn, const char *replica_purl, ReplicaId local_rid)

+ int ruv_update_ruv (RUV *ruv, const CSN *csn, const char *replica_purl, void *replica, ReplicaId local_rid)

  {

      int rc=RUV_SUCCESS;

-     RUVElement *replica;

+     RUVElement *repl_ruv;

      ReplicaId prim_rid;

+     Replica *repl_it = NULL;

+     size_t it = 0;

  

-     CSN *prim_csn = get_thread_primary_csn();

+     CSNPL_CTX *prim_csn = get_thread_primary_csn();

  

-     if (! csn_is_equal(csn, prim_csn)) {

+     if (! csn_primary(replica, csn, prim_csn)) {

  	/* not a primary csn, nothing to do */

  	return rc;

      }

-     slapi_rwlock_wrlock (ruv->lock);

+ 

+     /* first handle primary replica 

+      * there can be two ruv elements affected

+      */

      prim_rid = csn_get_replicaid (csn);

-     replica = ruvGetReplica (ruv, local_rid);

-     rc = ruv_update_ruv_element(ruv, replica, csn, replica_purl, PR_TRUE);

-     if ( rc || local_rid == prim_rid) goto done;

-     replica = ruvGetReplica (ruv, prim_rid);

-     rc = ruv_update_ruv_element(ruv, replica, csn, replica_purl, PR_FALSE);

- done:

+     slapi_rwlock_wrlock (ruv->lock);

+     if ( local_rid != prim_rid) {

+ 	repl_ruv = ruvGetReplica (ruv, prim_rid);

+ 	rc = ruv_update_ruv_element(ruv, repl_ruv, prim_csn, replica_purl, PR_FALSE);

+     }

+     repl_ruv = ruvGetReplica (ruv, local_rid);

+     rc = ruv_update_ruv_element(ruv, repl_ruv, prim_csn, replica_purl, PR_TRUE);

      slapi_rwlock_unlock (ruv->lock);

+     if (rc) return rc;

+ 

+     /* now handle secondary replicas */

+     for (it=0; it<prim_csn->repl_cnt; it++) {

+ 	repl_it = prim_csn->sec_repl[it];

+ 	replica_lock_replica(repl_it);

+ 	Object *ruv_obj = replica_get_ruv (repl_it);

+ 	RUV *ruv_it = object_get_data (ruv_obj);

+ 	slapi_rwlock_wrlock (ruv_it->lock);

+ 	repl_ruv = ruvGetReplica (ruv_it, replica_get_rid(repl_it));

+ 	rc = ruv_update_ruv_element(ruv_it, repl_ruv, prim_csn, replica_purl, PR_TRUE);

+ 	slapi_rwlock_unlock (ruv_it->lock);

+ 	replica_unlock_replica(repl_it);

+ 	if (rc) break;

+     }

      return rc;

  }

+ 

  static int

- ruv_update_ruv_element (RUV *ruv, RUVElement *replica, const CSN *csn, const char *replica_purl, PRBool isLocal)

+ ruv_update_ruv_element (RUV *ruv, RUVElement *replica, const CSNPL_CTX *prim_csn, const char *replica_purl, PRBool isLocal)

  {

      int rc=RUV_SUCCESS;

      char csn_str[CSN_STRSIZE];

      CSN *max_csn;

      CSN *first_csn = NULL;

      

-     if (replica == NULL)

-     {

+     if (replica == NULL) {

          /* we should have a ruv element at this point because it would have

             been added by ruv_add_inprogress function */

          slapi_log_err(SLAPI_LOG_REPL, repl_plugin_name, "ruv_update_ruv - "

- 			            "Can't locate RUV element for replica %d\n", csn_get_replicaid (csn)); 

+                         "Can't locate RUV element for replica %d\n", csn_get_replicaid (prim_csn->prim_csn));

          goto done;

      } 

  

- 	if (csnplCommitAll(replica->csnpl, csn) != 0)

- 	{

- 		slapi_log_err(SLAPI_LOG_ERR, repl_plugin_name, "ruv_update_ruv - Cannot commit csn %s\n",

- 			            csn_as_string(csn, PR_FALSE, csn_str));

+     if (csnplCommitAll(replica->csnpl, prim_csn) != 0) {

+         slapi_log_err(SLAPI_LOG_ERR, repl_plugin_name, "ruv_update_ruv - Cannot commit csn %s\n",

+                         csn_as_string(prim_csn->prim_csn, PR_FALSE, csn_str));

          rc = RUV_CSNPL_ERROR;

          goto done;

- 	}

-     else

-     {

+     } else {

          if (slapi_is_loglevel_set(SLAPI_LOG_REPL)) {

              slapi_log_err(SLAPI_LOG_REPL, repl_plugin_name, "ruv_update_ruv - "

-                             "Successfully committed csn %s\n", csn_as_string(csn, PR_FALSE, csn_str));

+                             "Successfully committed csn %s\n", csn_as_string(prim_csn->prim_csn, PR_FALSE, csn_str));

          }

      }

  

- 	if ((max_csn = csnplRollUp(replica->csnpl, &first_csn)) != NULL)

- 	{

- #ifdef DEBUG

- 		slapi_log_err(SLAPI_LOG_REPL, repl_plugin_name, "ruv_update_ruv - Rolled up to csn %s\n",

- 			            csn_as_string(max_csn, PR_FALSE, csn_str)); /* XXXggood remove debugging */

- #endif

+     if ((max_csn = csnplRollUp(replica->csnpl, &first_csn)) != NULL) {

+         slapi_log_err(SLAPI_LOG_REPL, repl_plugin_name, "ruv_update_ruv - Rolled up to csn %s\n",

+                         csn_as_string(max_csn, PR_FALSE, csn_str)); /* XXXggood remove debugging */

          /* replica object sets min csn for local replica */

- 		if (!isLocal && replica->min_csn == NULL) {

- 		  /* bug 559223 - it seems that, under huge stress, a server might pass

- 		   * through this code when more than 1 change has already been sent and commited into

- 		   * the pending lists... Therefore, as we are trying to set the min_csn ever 

- 		   * generated by this replica, we need to set the first_csn as the min csn in the

- 		   * ruv */

- 		  set_min_csn_nolock(ruv, first_csn, replica_purl);

- 		}

- 		/* only update the max_csn in the RUV if it is greater than the existing one */

- 		rc = set_max_csn_nolock_ext(ruv, max_csn, replica_purl, PR_TRUE /* must be greater */);

- 		/* It is possible that first_csn points to max_csn.

- 		   We need to free it once */

- 		if (max_csn != first_csn) {

- 			csn_free(&first_csn); 

- 		}

- 		csn_free(&max_csn);

- 	}

- 

+         if (!isLocal && replica->min_csn == NULL) {

+             /* bug 559223 - it seems that, under huge stress, a server might pass

+              * through this code when more than 1 change has already been sent and commited into

+              * the pending lists... Therefore, as we are trying to set the min_csn ever

+              * generated by this replica, we need to set the first_csn as the min csn in the

+              * ruv */

+         set_min_csn_nolock(ruv, first_csn, replica_purl);

+         }

+         /* only update the max_csn in the RUV if it is greater than the existing one */

+         rc = set_max_csn_nolock_ext(ruv, max_csn, replica_purl, PR_TRUE /* must be greater */);

+         /* It is possible that first_csn points to max_csn.

+            We need to free it once */

+         if (max_csn != first_csn) {

+             csn_free(&first_csn);

+         }

+         csn_free(&max_csn);

+     }

  done:

  

      return rc;

@@ -108,9 +108,9 @@

  PRInt32 ruv_replica_count (const RUV *ruv);

  char **ruv_get_referrals(const RUV *ruv);

  void ruv_dump(const RUV *ruv, char *ruv_name, PRFileDesc *prFile);

- int ruv_add_csn_inprogress (RUV *ruv, const CSN *csn);

- int ruv_cancel_csn_inprogress (RUV *ruv, const CSN *csn, ReplicaId rid);

- int ruv_update_ruv (RUV *ruv, const CSN *csn, const char *replica_purl, ReplicaId local_rid);

+ int ruv_add_csn_inprogress (void *repl, RUV *ruv, const CSN *csn);

+ int ruv_cancel_csn_inprogress (void *repl, RUV *ruv, const CSN *csn, ReplicaId rid);

+ int ruv_update_ruv (RUV *ruv, const CSN *csn, const char *replica_purl, void *replica, ReplicaId local_rid);

  int ruv_move_local_supplier_to_first(RUV *ruv, ReplicaId rid);

  int ruv_get_first_id_and_purl(RUV *ruv, ReplicaId *rid, char **replica_purl );

  int ruv_local_contains_supplier(RUV *ruv, ReplicaId rid);

@@ -130,6 +130,10 @@

  {

  	int i;

  

+ 	if (s == NULL) {

+ 		return;

+ 	}

+ 

      /* unschedule update window event if exists */

      unschedule_window_state_change_event (s);

  
@@ -177,11 +181,15 @@

  int

  schedule_set(Schedule *sch, Slapi_Attr *attr)

  {

- 	int return_value;

+ 	int return_value = -1;

  	schedule_item *si = NULL;

  	schedule_item *new_schedule_list = NULL;

  	int valid = 1;

  	

+ 	if (sch == NULL) {

+ 		return return_value;

+ 	}

+ 

  	if (NULL != attr)

  	{

  		int ind;

@@ -1138,9 +1138,46 @@

  		 */

  		if (NULL != connext && NULL != connext->replica_acquired)

  		{

-             Object *r_obj = (Object*)connext->replica_acquired;

- 			replica_relinquish_exclusive_access((Replica*)object_get_data (r_obj),

- 												connid, opid);

+ 			Replica *r = (Replica*)object_get_data ((Object*)connext->replica_acquired);

+ 			uint64_t r_locking_conn;

+ 			

+ 			/* At this point the supplier runs a Replica Agreement for 

+ 			 * the specific replica connext->replica_acquired.

+ 			 * The RA does not know it holds the replica (because it is

+ 			 * sending this request).

+ 			 * The situation is confused

+ 			 */

+ 			slapi_log_err(SLAPI_LOG_REPL, repl_plugin_name, "multimaster_extop_StartNSDS50ReplicationRequest - "

+ 				"already acquired replica: replica not ready (%d) (replica=%s)\n", response, replica_get_name(r) ? replica_get_name(r) : "no name");

+ 			

+ 			/*

+ 			 * On consumer side, we release the exclusive access at the

+ 			 * condition this is this RA that holds the replica

+ 			 */

+ 			if (r) {

+ 				

+ 				r_locking_conn = replica_get_locking_conn(r);

+ 				slapi_log_err(SLAPI_LOG_REPL, repl_plugin_name, "multimaster_extop_StartNSDS50ReplicationRequest - "

+ 				"already acquired replica: locking_conn=%d, current connid=%d\n", (int) r_locking_conn, (int) connid);

+ 				

+ 				if ((r_locking_conn != ULONG_MAX) && (r_locking_conn == connid)) {

+ 					replica_relinquish_exclusive_access(r, connid, opid);

+ 					object_release((Object*) connext->replica_acquired);

+ 					connext->replica_acquired = NULL;

+ 				}

+ 			}

+ 			/*

+ 			 * On consumer side we should not keep a incoming connection

+ 			 * with replica_acquired set although the supplier is not aware of

+ 			 * 

+ 			 * On the supplier, we need to close the connection so

+ 			 * that the RA will restart a new session in a clear state 

+ 			 */

+ 			slapi_log_err(SLAPI_LOG_REPL, repl_plugin_name, 

+ 			              "multimaster_extop_StartNSDS50ReplicationRequest - "

+ 			              "already acquired replica: disconnect conn=%" PRIu64 "\n", connid);

+ 			slapi_disconnect_server(conn);

+             

  		}

  		/* Remove any flags that would indicate repl session in progress */

  		if (NULL != connext)
@@ -1524,6 +1561,7 @@

  	data->rid = rid;

  	data->repl_root = slapi_ch_strdup(repl_root);

  	data->certify = slapi_ch_strdup(certify_all);

+ 	data->original_task = PR_FALSE;

  	/*

  	 *  Set the aborted rid and stop the cleaning

  	 */
@@ -1665,6 +1703,7 @@

  		data->payload = slapi_ch_bvdup(extop_payload);

  		data->force = slapi_ch_strdup(force);

  		data->repl_root = slapi_ch_strdup(repl_root);

+ 		data->original_task = PR_FALSE;

  

  		thread = PR_CreateThread(PR_USER_THREAD, replica_cleanallruv_thread_ext,

  				(void *)data, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,

@@ -433,7 +433,14 @@

  			/* The target entry is a loser */

  

  			char *newrdn_with_uniqueid;

- 			newrdn_with_uniqueid= get_rdn_plus_uniqueid (sessionid, newrdn, op_uniqueid);

+ 			char *newdn = NULL;

+                         if (new_parent_entry) {

+                             newdn = slapi_ch_smprintf("%s,%s", newrdn, slapi_entry_get_dn(new_parent_entry));

+                         } else {

+                             newdn = slapi_ch_smprintf("%s,%s", newrdn, slapi_entry_get_dn(parent_entry));

+                         }

+ 			newrdn_with_uniqueid= get_rdn_plus_uniqueid (sessionid, newdn, op_uniqueid);

+ 			slapi_ch_free_string(&newdn);

  			if(newrdn_with_uniqueid==NULL)

  			{

  				op_result= LDAP_OPERATIONS_ERROR;

@@ -58,7 +58,7 @@

  #else

  #define RETROCL_DLL_DEFAULT_THREAD_STACKSIZE 131072L

  #endif

- #define RETROCL_BE_CACHEMEMSIZE  "2097152"

+ #define RETROCL_BE_CACHEMEMSIZE  "209715200"

  #define RETROCL_BE_CACHESIZE "-1"

  #define RETROCL_PLUGIN_NAME "DSRetroclPlugin"

  

@@ -48,9 +48,6 @@

  /* views scoping */

  static void **views_api;

  

- /* Service provider handler */

- static vattr_sp_handle *vattr_handle = NULL;

- 

  /* List of nested roles */

  typedef struct _role_object_nested {

  	Slapi_DN *dn;	/* value of attribute nsroledn in a nested role definition */
@@ -224,13 +221,16 @@

  

  	/* Register a callback on backends creation|modification|deletion, 

        so that we update the corresponding cache */

- 	slapi_register_backend_state_change(NULL, roles_cache_trigger_update_suffix);

-    

- 	if ( slapi_vattrspi_register((vattr_sp_handle **)&vattr_handle, 

- 									roles_sp_get_value, 

- 									roles_sp_compare_value, 

- 									roles_sp_list_types) )

- 	{

+     slapi_register_backend_state_change(NULL, roles_cache_trigger_update_suffix);

+ 

+     /* Service provider handler - only used once! and freed by vattr! */

+     vattr_sp_handle *vattr_handle = NULL;

+ 

+ 

+     if (slapi_vattrspi_register((vattr_sp_handle **)&vattr_handle,

+                                 roles_sp_get_value,

+                                 roles_sp_compare_value,

+                                 roles_sp_list_types)) {

          slapi_log_err(SLAPI_LOG_ERR, ROLES_PLUGIN_SUBSYSTEM,

                 "roles_cache_init - slapi_vattrspi_register failed\n");

  
@@ -649,22 +649,20 @@

  

      slapi_log_err(SLAPI_LOG_PLUGIN, ROLES_PLUGIN_SUBSYSTEM, "--> roles_cache_stop\n");

  

- 	/* Go through all the roles list and trigger the associated structure */

- 	slapi_rwlock_wrlock(global_lock);

- 	current_role = roles_list;

- 	while ( current_role )

- 	{

- 		slapi_lock_mutex(current_role->change_lock);

- 		current_role->keeprunning = 0;	

- 		next_role = current_role->next;

- 		slapi_notify_condvar(current_role->something_changed, 1 );

- 		slapi_unlock_mutex(current_role->change_lock);

- 

- 		current_role = next_role;

- 	}

- 	slapi_rwlock_unlock(global_lock);

- 	slapi_ch_free((void **)&vattr_handle);

- 	roles_list = NULL;

+     /* Go through all the roles list and trigger the associated structure */

+     slapi_rwlock_wrlock(global_lock);

+     current_role = roles_list;

+     while (current_role) {

+         slapi_lock_mutex(current_role->change_lock);

+         current_role->keeprunning = 0;

+         next_role = current_role->next;

+         slapi_notify_condvar(current_role->something_changed, 1);

+         slapi_unlock_mutex(current_role->change_lock);

+ 

+         current_role = next_role;

+     }

+     slapi_rwlock_unlock(global_lock);

+     roles_list = NULL;

  

      slapi_log_err(SLAPI_LOG_PLUGIN, ROLES_PLUGIN_SUBSYSTEM, "<-- roles_cache_stop\n");

  }
@@ -1074,20 +1072,38 @@

  }

  

  /*

-  * Check that we are not using nsrole in the filter

+  * Check that we are not using nsrole in the filter, recurse over all the

+  * nested filters.

   */

  static int roles_check_filter(Slapi_Filter *filter_list)

  {

  	Slapi_Filter  *f;

  	char *type = NULL;

  

- 	for ( f = slapi_filter_list_first( filter_list );

- 	          f != NULL;

- 	          f = slapi_filter_list_next( filter_list, f ) )

- 	{

- 		slapi_filter_get_attribute_type(f, &type);

- 		if (strcasecmp(type, NSROLEATTR) == 0){

- 			return -1;

+ 	f = slapi_filter_list_first( filter_list );

+ 	if (f == NULL){

+ 		/* Single filter */

+ 		if (slapi_filter_get_attribute_type(filter_list, &type) == 0){

+ 			if (strcasecmp(type, NSROLEATTR) == 0){

+ 				return -1;

+ 			}

+ 		}

+ 	}

+ 	for ( ; f != NULL; f = slapi_filter_list_next(filter_list, f) ){

+ 		/* Complex filter */

+ 		if (slapi_filter_list_first(f)) {

+ 			/* Another filter list - recurse */

+ 			if (roles_check_filter(f) == -1){

+ 				/* Done, break out */

+ 				return -1;

+ 			}

+ 		} else {

+ 			/* Not a filter list, so check the type */

+ 			if (slapi_filter_get_attribute_type(f, &type) == 0){

+ 				if (strcasecmp(type, NSROLEATTR) == 0){

+ 					return -1;

+ 				}

+ 			}

  		}

  	}

  

file modified
+3 -3
@@ -597,11 +597,11 @@

  

  #if defined(THISISTEST)

  		{

- 			/* test code to retrieve an unhashed pw from the entry extention &

+ 			/* test code to retrieve an unhashed pw from the entry extension &

  			 * PSEUDO_ATTR_UNHASHEDUSERPASSWORD attribute */

  			char *test_str = slapi_get_first_clear_text_pw(e);

  			if (test_str) {

- 				slapi_log_err(SLAPI_LOG_ERR,

+ 				slapi_log_err(SLAPI_LOG_ERR, "op_shared_add",

  				              "Value from extension: %s\n", test_str);

  				slapi_ch_free_string(&test_str);

  			}
@@ -609,7 +609,7 @@

  			test_str = slapi_entry_attr_get_charptr(e,

  			                                  PSEUDO_ATTR_UNHASHEDUSERPASSWORD);

  			if (test_str) {

- 				slapi_log_err(SLAPI_LOG_ERR,

+ 				slapi_log_err(SLAPI_LOG_ERR, "op_shared_add",

  				              "Value from attr: %s\n", test_str);

  				slapi_ch_free_string(&test_str);

  			}

@@ -303,8 +303,10 @@

  #define ENTRY_STATE_DELETED     0x1 /* entry is marked as deleted */

  #define ENTRY_STATE_CREATING    0x2 /* entry is being created; don't touch it */

  #define ENTRY_STATE_NOTINCACHE  0x4 /* cache_add failed; not in the cache */

+ #define ENTRY_STATE_INVALID     0x8 /* cache entry is invalid and needs to be removed */

      int               ep_refcnt;    /* entry reference cnt */

      size_t            ep_size;      /* for cache tracking */

+     struct timespec ep_create_time; /* the time the entry was added to the cache */

  };

  

  /* From ep_type through ep_size MUST be identical to backcommon */
@@ -316,6 +318,7 @@

      char              ep_state;     /* state in the cache */

      int               ep_refcnt;    /* entry reference cnt */

      size_t            ep_size;      /* for cache tracking */

+     struct timespec ep_create_time; /* the time the entry was added to the cache */

      Slapi_Entry       *ep_entry;    /* real entry */

      Slapi_Entry       *ep_vlventry;

      void *            ep_dn_link;   /* linkage for the 3 hash */
@@ -333,6 +336,7 @@

      char              ep_state;    /* state in the cache; share ENTRY_STATE_* */

      int               ep_refcnt;   /* entry reference cnt */

      size_t            ep_size;      /* for cache tracking */

+     struct timespec ep_create_time; /* the time the entry was added to the cache */

      Slapi_DN          *dn_sdn;

      void              *dn_id_link; /* for hash table */

  };

@@ -23,7 +23,7 @@

  		return;

  	}

  	ep = *bep;

- 	PR_ASSERT(ep->ep_state & (ENTRY_STATE_DELETED|ENTRY_STATE_NOTINCACHE));

+ 	PR_ASSERT(ep->ep_state & (ENTRY_STATE_DELETED | ENTRY_STATE_NOTINCACHE | ENTRY_STATE_INVALID));

  	if ( ep->ep_entry != NULL ) {

  		slapi_entry_free( ep->ep_entry );

  	}

@@ -55,6 +55,11 @@

  #define LOG(...)

  #endif

  

+ typedef enum {

+     ENTRY_CACHE,

+     DN_CACHE,

+ } CacheType;

+ 

  #define LRU_DETACH(cache, e) lru_detach((cache), (void *)(e))

  

  #define CACHE_LRU_HEAD(cache, type) ((type)((cache)->c_lruhead))
@@ -180,6 +185,7 @@

  int add_hash(Hashtable *ht, void *key, size_t keylen, void *entry,

                    void **alt)

  {

+     struct backcommon *back_entry = (struct backcommon *)entry;

      u_long val, slot;

      void *e;

  
@@ -197,6 +203,7 @@

         e = HASH_NEXT(ht, e);

      }

      /* ok, it's not already there, so add it */

+     back_entry->ep_create_time = slapi_current_rel_time_hr();

      HASH_NEXT(ht, entry) = ht->slot[slot];

      ht->slot[slot] = entry;

      return 1;
@@ -487,6 +494,126 @@

      }

  }

  

+ /*

+  * Helper function for flush_hash() to calculate if the entry should be

+  * removed from the cache.

+  */

+ static int32_t

+ flush_remove_entry(struct timespec *entry_time, struct timespec *start_time)

+ {

+     struct timespec diff;

+ 

+     slapi_timespec_diff(entry_time, start_time, &diff);

+     if (diff.tv_sec >= 0) {

+         return 1;

+     } else {

+         return 0;

+     }

+ }

+ 

+ /*

+  * Flush all the cache entries that were added after the "start time"

+  * This is called when a backend transaction plugin fails, and we need

+  * to remove all the possible invalid entries in the cache.

+  *

+  * If the ref count is 0, we can straight up remove it from the cache, but

+  * if the ref count is greater than 1, then the entry is currently in use.

+  * In the later case we set the entry state to ENTRY_STATE_INVALID, and

+  * when the owning thread cache_returns() the cache entry is automatically

+  * removed so another thread can not use/lock the invalid cache entry.

+  */

+ static void

+ flush_hash(struct cache *cache, struct timespec *start_time, int32_t type)

+ {

+     Hashtable *ht = cache->c_idtable; /* start with the ID table as it's in both ENTRY and DN caches */

+     void *e, *laste = NULL;

+ 

+     cache_lock(cache);

+ 

+     for (size_t i = 0; i < ht->size; i++) {

+         e = ht->slot[i];

+         while (e) {

+             struct backcommon *entry = (struct backcommon *)e;

+             uint64_t remove_it = 0;

+             if (flush_remove_entry(&entry->ep_create_time, start_time)) {

+                 /* Mark the entry to be removed */

+                 slapi_log_err(SLAPI_LOG_CACHE, "flush_hash", "[%s] Removing entry id (%d)\n",

+                         type ? "DN CACHE" : "ENTRY CACHE", entry->ep_id);

+                 remove_it = 1;

+             }

+             laste = e;

+             e = HASH_NEXT(ht, e);

+ 

+             if (remove_it) {

+                 /* since we have the cache lock we know we can trust refcnt */

+                 entry->ep_state |= ENTRY_STATE_INVALID;

+                 if (entry->ep_refcnt == 0) {

+                     entry->ep_refcnt++;

+                     lru_delete(cache, laste);

+                     if (type == ENTRY_CACHE) {

+                         entrycache_remove_int(cache, laste);

+                         entrycache_return(cache, (struct backentry **)&laste);

+                     } else {

+                         dncache_remove_int(cache, laste);

+                         dncache_return(cache, (struct backdn **)&laste);

+                     }

+                 } else {

+                     /* Entry flagged for removal */

+                     slapi_log_err(SLAPI_LOG_CACHE, "flush_hash",

+                             "[%s] Flagging entry to be removed later: id (%d) refcnt: %d\n",

+                             type ? "DN CACHE" : "ENTRY CACHE", entry->ep_id, entry->ep_refcnt);

+                 }

+             }

+         }

+     }

+ 

+     if (type == ENTRY_CACHE) {

+         /* Also check the DN hashtable */

+         ht = cache->c_dntable;

+ 

+         for (size_t i = 0; i < ht->size; i++) {

+             e = ht->slot[i];

+             while (e) {

+                 struct backcommon *entry = (struct backcommon *)e;

+                 uint64_t remove_it = 0;

+                 if (flush_remove_entry(&entry->ep_create_time, start_time)) {

+                     /* Mark the entry to be removed */

+                     slapi_log_err(SLAPI_LOG_CACHE, "flush_hash", "[ENTRY CACHE] Removing entry id (%d)\n",

+                             entry->ep_id);

+                     remove_it = 1;

+                 }

+                 laste = e;

+                 e = HASH_NEXT(ht, e);

+ 

+                 if (remove_it) {

+                     /* since we have the cache lock we know we can trust refcnt */

+                     entry->ep_state |= ENTRY_STATE_INVALID;

+                     if (entry->ep_refcnt == 0) {

+                         entry->ep_refcnt++;

+                         lru_delete(cache, laste);

+                         entrycache_remove_int(cache, laste);

+                         entrycache_return(cache, (struct backentry **)&laste);

+                     } else {

+                         /* Entry flagged for removal */

+                         slapi_log_err(SLAPI_LOG_CACHE, "flush_hash",

+                                 "[ENTRY CACHE] Flagging entry to be removed later: id (%d) refcnt: %d\n",

+                                 entry->ep_id, entry->ep_refcnt);

+                     }

+                 }

+             }

+         }

+     }

+ 

+     cache_unlock(cache);

+ }

+ 

+ void

+ revert_cache(ldbm_instance *inst, struct timespec *start_time)

+ {

+     flush_hash(&inst->inst_cache, start_time, ENTRY_CACHE);

+     flush_hash(&inst->inst_dncache, start_time, DN_CACHE);

+ }

+ 

  /* initialize the cache */

  int cache_init(struct cache *cache, size_t maxsize, long maxentries, int type)

  {
@@ -1139,7 +1266,7 @@

      {

          ASSERT(e->ep_refcnt > 0);

          if (! --e->ep_refcnt) {

-             if (e->ep_state & ENTRY_STATE_DELETED) {

+             if (e->ep_state & (ENTRY_STATE_DELETED | ENTRY_STATE_INVALID)) {

                  const char* ndn = slapi_sdn_get_ndn(backentry_get_sdn(e));

                  if (ndn){

                      /*
@@ -1151,6 +1278,13 @@

                          LOG("entrycache_return -Failed to remove %s from dn table\n", ndn);

                      }

                  }

+                 if (e->ep_state & ENTRY_STATE_INVALID) {

+                     /* Remove it from the hash table before we free the back entry */

+                     slapi_log_err(SLAPI_LOG_CACHE, "entrycache_return",

+                             "Finally flushing invalid entry: %d (%s)\n",

+                             e->ep_id, backentry_get_ndn(e));

+                     entrycache_remove_int(cache, e);

+                 }

                  backentry_free(bep);

              } else {

                  lru_add(cache, e);
@@ -1540,7 +1674,7 @@

  

      /* make sure entry hasn't been deleted now */

      cache_lock(cache);

-     if (e->ep_state & (ENTRY_STATE_DELETED|ENTRY_STATE_NOTINCACHE)) {

+     if (e->ep_state & (ENTRY_STATE_DELETED | ENTRY_STATE_NOTINCACHE | ENTRY_STATE_INVALID)) {

         cache_unlock(cache);

         PR_ExitMonitor(e->ep_mutexp);

         LOG("<= cache_lock_entry (DELETED)\n");
@@ -1708,7 +1842,14 @@

      {

          ASSERT((*bdn)->ep_refcnt > 0);

          if (! --(*bdn)->ep_refcnt) {

-             if ((*bdn)->ep_state & ENTRY_STATE_DELETED) {

+             if ((*bdn)->ep_state & (ENTRY_STATE_DELETED | ENTRY_STATE_INVALID)) {

+                 if ((*bdn)->ep_state & ENTRY_STATE_INVALID) {

+                     /* Remove it from the hash table before we free the back dn */

+                     slapi_log_err(SLAPI_LOG_CACHE, "dncache_return",

+                             "Finally flushing invalid entry: %d (%s)\n",

+                             (*bdn)->ep_id, slapi_sdn_get_dn((*bdn)->dn_sdn));

+                     dncache_remove_int(cache, (*bdn));

+                 }

                  backdn_free(bdn);

              } else {

                  lru_add(cache, (void *)*bdn);

@@ -6143,7 +6143,7 @@

              return_value = dblayer_copy_directory(li, task, changelogdir,

                                                    changelog_destdir,

                                                    0 /* backup */,

-                                                   &cnt, 1, 0, 0);

+                                                   &cnt, 0, 0, 1);

              if (return_value) {

                  slapi_log_err(SLAPI_LOG_ERR,

                            "dblayer_backup", "Error in copying directory "
@@ -6823,7 +6823,7 @@

                      *cldirname = '\0';

                      return_value = dblayer_copy_directory(li, task, filename1,

                                                            changelogdir, 1 /* restore */,

-                                                           &cnt, 1, 0 ,0);

+                                                           &cnt, 0, 0 ,1);

                      *cldirname = '/';

                      if (return_value) {

                          slapi_log_err(SLAPI_LOG_ERR,

@@ -101,8 +101,7 @@

          len = strlen(buf);

          if ( slapi_write_buffer( prfd, buf, len ) != (PRInt32)len )

          {

-             slapi_log_err(SLAPI_LOG_ERR, "dbversion_write - "

-                     "Could not write to file \"%s\"\n", filename, 0, 0 );

+             slapi_log_err(SLAPI_LOG_ERR, "dbversion_write", "Could not write to file \"%s\"\n", filename );

              rc= -1;

          }

          if(rc==0 && dataversion!=NULL)
@@ -111,8 +110,7 @@

              len = strlen( buf );

              if ( slapi_write_buffer( prfd, buf, len ) != (PRInt32)len )

              {

-                 slapi_log_err(SLAPI_LOG_ERR, "dbversion_write - "

-                         "Could not write to file \"%s\"\n", filename, 0, 0 );

+                 slapi_log_err(SLAPI_LOG_ERR, "dbversion_write", "Could not write to file \"%s\"\n", filename );

                  rc= -1;

              }

          }
@@ -160,7 +158,7 @@

          /* File missing... we are probably creating a new database. */

          return EACCES;

      } else {

-         char buf[LDBM_VERSION_MAXBUF];

+         char buf[LDBM_VERSION_MAXBUF] = {0};

          PRInt32 nr = slapi_read_buffer(prfd, buf, (PRInt32)LDBM_VERSION_MAXBUF-1);

          if ( nr > 0 && nr != (PRInt32)LDBM_VERSION_MAXBUF-1 )

          {
@@ -178,6 +176,19 @@

              }

          }

          (void)PR_Close( prfd );

+ 

+         if (*dataversion == NULL ) {

+             slapi_log_err(SLAPI_LOG_DEBUG, "dbversion_read", "dataversion not present in \"%s\"\n", filename);

+         }

+         if (*ldbmversion == NULL ) {

+             /* DBVERSIOn is corrupt, COMPLAIN! */

+             /* This is IDRM           Identifier removed (POSIX.1)

+              * which seems appropriate for the error here :)

+              */

+             slapi_log_err(SLAPI_LOG_CRIT, "dbversion_read", "Could not parse file \"%s\". It may be corrupted.\n", filename);

+             slapi_log_err(SLAPI_LOG_CRIT, "dbversion_read", "It may be possible to recover by replacing with a valid DBVERSION file from another DB instance\n");

+             return EIDRM;

+         }

          return 0;

      }

  }

@@ -234,7 +234,7 @@

          return;

      }

  

-     while(is_instance_busy(job->inst)){

+     while (task->task_state == SLAPI_TASK_RUNNING) {

          /* wait for the job to finish before freeing it */

          DS_Sleep(PR_SecondsToInterval(1));

      }

@@ -808,7 +808,10 @@

  		    bufSpace -= (s - first);

  		}

  		do {

- 		    *bufNext++ = '\\'; --bufSpace;

+ 		    if (bufSpace) {

+ 		        *bufNext++ = '\\';

+ 		        --bufSpace;

+ 		    }

  		    if (bufSpace < 2) {

  			memcpy (bufNext, "..", 2);

  			bufNext += 2;
@@ -903,8 +906,10 @@

  		slapi_log_err(SLAPI_LOG_ERR, "index_read_ext_allids", "NULL prefix\n");

  		return NULL;

  	}

- 	slapi_log_err(SLAPI_LOG_TRACE, "index_read_ext_allids", "=> ( \"%s\" %s \"%s\" )\n",

- 		   type, prefix, encode (val, buf));

+ 	if (slapi_is_loglevel_set(LDAP_DEBUG_TRACE)) {

+ 	    slapi_log_err(SLAPI_LOG_TRACE, "index_read_ext_allids", "=> ( \"%s\" %s \"%s\" )\n",

+ 	                  type, prefix, encode (val, buf));

+ 	}

  

  	basetype = typebuf;

  	if ( (basetmp = slapi_attr_basetype( type, typebuf, sizeof(typebuf) ))
@@ -1737,16 +1742,13 @@

                   */

  		key.flags = DB_DBT_USERMEM;

                  key.ulen = tmpbuflen;

- #ifdef LDAP_ERROR_LOGGING

- 		/* XXX if ( slapd_ldap_debug & LDAP_DEBUG_TRACE )  XXX */

- 		{

+         if (slapi_is_loglevel_set(LDAP_DEBUG_TRACE)) {

  			char encbuf[BUFSIZ];

  

  			slapi_log_err(SLAPI_LOG_TRACE, "addordel_values", "%s_value(\"%s\")\n",

  				   (flags & BE_INDEX_ADD) ? "add" : "del",

  				   encoded (&key, encbuf));

  		}

- #endif

  

  		if (NULL != txn) {

  			db_txn = txn->back_txn_txn;
@@ -1907,16 +1909,13 @@

           */

          key.flags = DB_DBT_USERMEM;

          key.ulen = tmpbuflen;

- #ifdef LDAP_ERROR_LOGGING

-         /* XXX if ( slapd_ldap_debug & LDAP_DEBUG_TRACE )  XXX */

-         {

+         if (slapi_is_loglevel_set(LDAP_DEBUG_TRACE)) {

              char encbuf[BUFSIZ];

  

              slapi_log_err(SLAPI_LOG_TRACE, "addordel_values_sv", "%s_value(\"%s\")\n",

                         (flags & BE_INDEX_ADD) ? "add" : "del",

                         encoded (&key, encbuf));

          }

- #endif

  

          if (NULL != txn) {

              db_txn = txn->back_txn_txn;

@@ -302,12 +302,13 @@

          inst = (ldbm_instance *) object_get_data(inst_obj);

          ldbm_instance_set_flags(inst);

          rc1 = ldbm_instance_start(inst->inst_be);

-     if (rc1 != 0) {

-         rc = rc1;

-     } else {

-         vlv_init(inst);

-         slapi_mtn_be_started(inst->inst_be);

-     }

+         if (rc1 != 0) {

+             rc = rc1;

+         } else {

+             ldbm_instance_register_modify_callback(inst);

+             vlv_init(inst);

+             slapi_mtn_be_started(inst->inst_be);

+         }

          inst_obj = objset_next_obj(li->li_instance_set, inst_obj);

      }

  

@@ -94,6 +94,9 @@

  	PRUint64 conn_id;

  	int op_id;

  	int result_sent = 0;

+ 	int32_t parent_op = 0;

+ 	struct timespec parent_time;

+ 

  	if (slapi_pblock_get(pb, SLAPI_CONN_ID, &conn_id) < 0) {

  		conn_id = 0; /* connection is NULL */

  	}
@@ -131,6 +134,13 @@

  	slapi_entry_delete_values( e, numsubordinates, NULL );

  

  	dblayer_txn_init(li,&txn);

+ 

+     if (txn.back_txn_txn == NULL) {

+         /* This is the parent operation, get the time */

+         parent_op = 1;

+         parent_time = slapi_current_rel_time_hr();

+ 	}

+ 

  	/* the calls to perform searches require the parent txn if any

  	   so set txn to the parent_txn until we begin the child transaction */

  	if (parent_txn) {
@@ -1194,6 +1204,10 @@

  	goto common_return;

  

  error_return:

+     /* Revert the caches if this is the parent operation */

+     if (parent_op) {

+         revert_cache(inst, &parent_time);

+     }

  	if ( addingentry_id_assigned )

  	{

  		next_id_return( be, addingentry->ep_id );

@@ -420,7 +420,7 @@

          /* Stop the user configuring a stupidly small cache */

          /* min: 8KB (page size) * def thrd cnts (threadnumber==20). */

  #define DBDEFMINSIZ     500000

-         /* We allow a value of 0, because the autotuting in start.c will

+         /* We allow a value of 0, because the autotuning in start.c will

           * register that, and trigger the recalculation of the dbcachesize as

           * needed on the next start up.

           */
@@ -443,7 +443,18 @@

                  return LDAP_UNWILLING_TO_PERFORM;

              }

          }

+ 

          if (CONFIG_PHASE_RUNNING == phase) {

+             if (val > 0 && li->li_cache_autosize) {

+                 /* We are auto-tuning the cache, so this change would be overwritten - return an error */

+                 slapi_create_errormsg(errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,

+                     "Error: \"nsslapd-dbcachesize\" can not be updated while \"nsslapd-cache-autosize\" is set "

+                     "in \"cn=config,cn=ldbm database,cn=plugins,cn=config\".");

+                 slapi_log_err(SLAPI_LOG_ERR, "ldbm_config_dbcachesize_set",

+                     "\"nsslapd-dbcachesize\" can not be set while \"nsslapd-cache-autosize\" is set "

+                     "in \"cn=config,cn=ldbm database,cn=plugins,cn=config\".\n");

+                 return LDAP_UNWILLING_TO_PERFORM;

+             }

              li->li_new_dbcachesize = val;

              if (val == 0) {

                  slapi_log_err(SLAPI_LOG_NOTICE, "ldbm_config_dbcachesize_set", "cache size reset to 0, will be autosized on next startup.\n");
@@ -1289,14 +1300,25 @@

  

  static int ldbm_config_cache_autosize_set(void *arg,

                                            void *value,

-                                           char *errorbuf __attribute__((unused)),

+                                           char *errorbuf,

                                            int phase __attribute__((unused)),

                                            int apply)

  {

      struct ldbminfo *li = (struct ldbminfo *)arg;

  

-     if (apply)

-     li->li_cache_autosize = (int)((uintptr_t)value);

+     if (apply) {

+         int val = (int)((uintptr_t)value);

+         if (val < 0 || val > 100) {

+             slapi_create_errormsg(errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,

+                 "Error: Invalid value for %s (%d). The value must be between \"0\" and \"100\"\n",

+                 CONFIG_CACHE_AUTOSIZE, val);

+             slapi_log_err(SLAPI_LOG_ERR, "ldbm_config_cache_autosize_set",

+                 "Invalid value for %s (%d). The value must be between \"0\" and \"100\"\n",

+                 CONFIG_CACHE_AUTOSIZE, val);

+             return LDAP_UNWILLING_TO_PERFORM;

+         }

+         li->li_cache_autosize = val;

+     }

      return LDAP_SUCCESS;

  }

  
@@ -1309,14 +1331,25 @@

  

  static int ldbm_config_cache_autosize_split_set(void *arg,

                                                  void *value,

-                                                 char *errorbuf __attribute__((unused)),

+                                                 char *errorbuf,

                                                  int phase __attribute__((unused)),

                                                  int apply)

  {

      struct ldbminfo *li = (struct ldbminfo *)arg;

  

-     if (apply)

-     li->li_cache_autosize_split = (int)((uintptr_t)value);

+     if (apply) {

+         int val = (int)((uintptr_t)value);

+         if (val < 0 || val > 100) {

+             slapi_create_errormsg(errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,

+                 "Error: Invalid value for %s (%d). The value must be between \"0\" and \"100\"\n",

+                 CONFIG_CACHE_AUTOSIZE_SPLIT, val);

+             slapi_log_err(SLAPI_LOG_ERR, "ldbm_config_cache_autosize_split_set",

+                 "Invalid value for %s (%d). The value must be between \"0\" and \"100\"\n",

+                 CONFIG_CACHE_AUTOSIZE_SPLIT, val);

+             return LDAP_UNWILLING_TO_PERFORM;

+         }

+         li->li_cache_autosize_split = val;

+     }

      return LDAP_SUCCESS;

  }

  
@@ -1720,8 +1753,8 @@

      {CONFIG_DB_DEBUG_CHECKPOINTING, CONFIG_TYPE_ONOFF, "off", &ldbm_config_db_debug_checkpointing_get, &ldbm_config_db_debug_checkpointing_set, 0},

      {CONFIG_DB_HOME_DIRECTORY, CONFIG_TYPE_STRING, "", &ldbm_config_db_home_directory_get, &ldbm_config_db_home_directory_set, 0},

      {CONFIG_IMPORT_CACHE_AUTOSIZE, CONFIG_TYPE_INT, "-1", &ldbm_config_import_cache_autosize_get, &ldbm_config_import_cache_autosize_set, CONFIG_FLAG_ALWAYS_SHOW|CONFIG_FLAG_ALLOW_RUNNING_CHANGE},

-     {CONFIG_CACHE_AUTOSIZE, CONFIG_TYPE_INT, "10", &ldbm_config_cache_autosize_get, &ldbm_config_cache_autosize_set, 0},

-     {CONFIG_CACHE_AUTOSIZE_SPLIT, CONFIG_TYPE_INT, "40", &ldbm_config_cache_autosize_split_get, &ldbm_config_cache_autosize_split_set, 0},

+     {CONFIG_CACHE_AUTOSIZE, CONFIG_TYPE_INT, "10", &ldbm_config_cache_autosize_get, &ldbm_config_cache_autosize_set, CONFIG_FLAG_ALWAYS_SHOW|CONFIG_FLAG_ALLOW_RUNNING_CHANGE},

+     {CONFIG_CACHE_AUTOSIZE_SPLIT, CONFIG_TYPE_INT, "40", &ldbm_config_cache_autosize_split_get, &ldbm_config_cache_autosize_split_set, CONFIG_FLAG_ALWAYS_SHOW|CONFIG_FLAG_ALLOW_RUNNING_CHANGE},

      {CONFIG_IMPORT_CACHESIZE, CONFIG_TYPE_SIZE_T, "16777216", &ldbm_config_import_cachesize_get, &ldbm_config_import_cachesize_set, CONFIG_FLAG_ALWAYS_SHOW|CONFIG_FLAG_ALLOW_RUNNING_CHANGE},

      {CONFIG_IDL_SWITCH, CONFIG_TYPE_STRING, "new", &ldbm_config_idl_get_idl_new, &ldbm_config_idl_set_tune, CONFIG_FLAG_ALWAYS_SHOW},

      {CONFIG_IDL_UPDATE, CONFIG_TYPE_ONOFF, "on", &ldbm_config_idl_get_update, &ldbm_config_idl_set_update, 0},

@@ -157,6 +157,6 @@

  ldbm_instance_index_config_enable_index(ldbm_instance *inst, Slapi_Entry* e);

  int ldbm_instance_create_default_user_indexes(ldbm_instance *inst);

  void ldbm_config_destroy(struct ldbminfo *li);

- 

+ void ldbm_instance_register_modify_callback(ldbm_instance *inst);

  

  #endif /* _LDBM_CONFIG_H_ */

@@ -79,6 +79,8 @@

  	ID tomb_ep_id = 0;

  	int result_sent = 0;

  	Connection *pb_conn;

+     int32_t parent_op = 0;

+     struct timespec parent_time;

  

  	if (slapi_pblock_get(pb, SLAPI_CONN_ID, &conn_id) < 0) {

  		conn_id = 0; /* connection is NULL */
@@ -98,6 +100,13 @@

  

  	/* dblayer_txn_init needs to be called before "goto error_return" */

  	dblayer_txn_init(li,&txn);

+ 

+     if (txn.back_txn_txn == NULL) {

+         /* This is the parent operation, get the time */

+         parent_op = 1;

+         parent_time = slapi_current_rel_time_hr();

+     }

+ 

  	/* the calls to perform searches require the parent txn if any

  	   so set txn to the parent_txn until we begin the child transaction */

  	if (parent_txn) {
@@ -208,8 +217,13 @@

  						ldap_result_code= LDAP_OPERATIONS_ERROR;

  						goto error_return;

  					}

- 					if (cache_is_in_cache(&inst->inst_cache, tombstone)) {

- 						CACHE_REMOVE(&inst->inst_cache, tombstone);

+ 					CACHE_REMOVE(&inst->inst_cache, tombstone); /* TEST FIX: Always remove tombstone from cache */

+ 					if (cache_is_in_cache(&inst->inst_cache, tombstone) == 0) {

+ 					    /* This really should not happen - means there is cache corruption */

+                         slapi_log_err(SLAPI_LOG_ALERT, "ldbm_back_delete",

+ 						        "DEBUG [retry: %d] - tombstone NOT in cache (%s) orig dn (%s) state (%d), "

+                                 "but it should be in the cache (avoided crash, but cache was corrupted)\n",

+ 						        retry_count, backentry_get_ndn(tombstone), dn, tombstone->ep_state);

  					}

  					CACHE_RETURN(&inst->inst_cache, &tombstone);

  					if (tombstone) {
@@ -1312,6 +1326,10 @@

  	goto common_return;

  

  error_return:

+     /* Revert the caches if this is the parent operation */

+     if (parent_op) {

+         revert_cache(inst, &parent_time);

+     }

  	if (tombstone) {

  		if (cache_is_in_cache(&inst->inst_cache, tombstone)) {

  			tomb_ep_id = tombstone->ep_id; /* Otherwise, tombstone might have been freed. */

@@ -66,7 +66,7 @@

  ldbm_instance_config_cachesize_set(void *arg,

                                     void *value,

                                     char *errorbuf __attribute__((unused)),

-                                    int phase __attribute__((unused)),

+                                    int phase,

                                     int apply)

  {

      ldbm_instance *inst = (ldbm_instance *) arg;
@@ -76,6 +76,18 @@

      /* Do whatever we can to make sure the data is ok. */

  

      if (apply) {

+         if (CONFIG_PHASE_RUNNING == phase) {

+             if (val > 0 && inst->inst_li->li_cache_autosize) {

+                 /* We are auto-tuning the cache, so this change would be overwritten - return an error */

+                 slapi_create_errormsg(errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,

+                     "Error: \"nsslapd-cachesize\" can not be updated while \"nsslapd-cache-autosize\" is set "

+                     "in \"cn=config,cn=ldbm database,cn=plugins,cn=config\".");

+                 slapi_log_err(SLAPI_LOG_ERR, "ldbm_instance_config_cachesize_set",

+                     "\"nsslapd-cachesize\" can not be set while \"nsslapd-cache-autosize\" is set "

+                     "in \"cn=config,cn=ldbm database,cn=plugins,cn=config\".\n");

+                 return LDAP_UNWILLING_TO_PERFORM;

+             }

+         }

          cache_set_max_entries(&(inst->inst_cache), val);

      }

  
@@ -94,7 +106,7 @@

  ldbm_instance_config_cachememsize_set(void *arg,

                                        void *value,

                                        char *errorbuf,

-                                       int phase __attribute__((unused)),

+                                       int phase,

                                        int apply)

  {

      ldbm_instance *inst = (ldbm_instance *) arg;
@@ -115,6 +127,18 @@

       */

  

      if (apply) {

+         if (CONFIG_PHASE_RUNNING == phase) {

+             if (val > 0 && inst->inst_li->li_cache_autosize) {

+                 /* We are auto-tuning the cache, so this change would be overwritten - return an error */

+                 slapi_create_errormsg(errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,

+                     "Error: \"nsslapd-cachememsize\" can not be updated while \"nsslapd-cache-autosize\" is set "

+                     "in \"cn=config,cn=ldbm database,cn=plugins,cn=config\".");

+                 slapi_log_err(SLAPI_LOG_ERR, "ldbm_instance_config_cachememsize_set",

+                     "\"nsslapd-cachememsize\" can not be set while \"nsslapd-cache-autosize\" is set "

+                     "in \"cn=config,cn=ldbm database,cn=plugins,cn=config\".\n");

+                 return LDAP_UNWILLING_TO_PERFORM;

+             }

+         }

          if (val > inst->inst_cache.c_maxsize) {

              delta = val - inst->inst_cache.c_maxsize;

              delta_original = delta;
@@ -126,10 +150,12 @@

  

              if (sane == UTIL_CACHESIZE_ERROR){

                  slapi_create_errormsg(errorbuf, SLAPI_DSE_RETURNTEXT_SIZE, "Error: unable to determine system memory limits.");

-                 slapi_log_err(SLAPI_LOG_ERR, "ldbm_instance_config_cachememsize_set", "Enable to determine system memory limits.\n");

+                 slapi_log_err(SLAPI_LOG_ERR, "ldbm_instance_config_cachememsize_set",

+                         "Enable to determine system memory limits.\n");

                  return LDAP_UNWILLING_TO_PERFORM;

              } else if (sane == UTIL_CACHESIZE_REDUCED) {

-                 slapi_log_err(SLAPI_LOG_WARNING, "ldbm_instance_config_cachememsize_set", "delta +%"PRIu64" of request %"PRIu64" reduced to %"PRIu64"\n", delta_original, val, delta);

+                 slapi_log_err(SLAPI_LOG_WARNING, "ldbm_instance_config_cachememsize_set",

+                         "delta +%"PRIu64" of request %"PRIu64" reduced to %"PRIu64"\n", delta_original, val, delta);

                  /*

                   * This works as: value = 100

                   * delta_original to inst, 20;
@@ -141,7 +167,8 @@

              }

          }

          if (inst->inst_cache.c_maxsize < MINCACHESIZE || val < MINCACHESIZE) {

-             slapi_log_err(SLAPI_LOG_ERR, "ldbm_instance_config_cachememsize_set", "force a minimal value %"PRIu64"\n", MINCACHESIZE);

+             slapi_log_err(SLAPI_LOG_INFO, "ldbm_instance_config_cachememsize_set",

+                     "force a minimal value %"PRIu64"\n", MINCACHESIZE);

              /* This value will trigger an autotune next start up, but it should increase only */

              val = MINCACHESIZE;

          }
@@ -551,6 +578,19 @@

      return SLAPI_DSE_CALLBACK_ERROR;

  }

  

+ void

+ ldbm_instance_register_modify_callback(ldbm_instance *inst)

+ {

+     struct ldbminfo *li = inst->inst_li;

+     char *dn = NULL;

+ 

+     dn = slapi_create_dn_string("cn=%s,cn=%s,cn=plugins,cn=config",

+                                 inst->inst_name, li->li_plugin->plg_name);

+     slapi_config_register_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, dn,

+         LDAP_SCOPE_BASE, "(objectclass=*)",

+         ldbm_instance_modify_config_entry_callback, (void *) inst);

+     slapi_ch_free_string(&dn);

+ }

  /* Reads in any config information held in the dse for the given

   * entry.  Creates dse entries used to configure the given instance

   * if they don't already exist.  Registers dse callback functions to
@@ -860,7 +900,7 @@

                  continue;

              }

  

-         /* This assumes there is only one bval for this mod. */

+             /* This assumes there is only one bval for this mod. */

              if (mods[i]->mod_bvalues == NULL) {

                  /* This avoids the null pointer deref.

                   * In ldbm_config.c ldbm_config_set, it checks for the NULL.
@@ -1187,7 +1227,7 @@

          return SLAPI_DSE_CALLBACK_ERROR;

      }

  

-     slapi_log_err(SLAPI_LOG_ERR, "ldbm_instance_post_delete_instance_entry_callback",

+     slapi_log_err(SLAPI_LOG_INFO, "ldbm_instance_post_delete_instance_entry_callback",

          "Removing '%s'.\n", instance_name);

  

      cache_destroy_please(&inst->inst_cache, CACHE_TYPE_ENTRY);
@@ -1224,9 +1264,9 @@

                          dbp = PR_smprintf("%s/%s", inst_dirp, direntry->name);

                          if (NULL == dbp) {

                              slapi_log_err(SLAPI_LOG_ERR,

-                             "ldbm_instance_post_delete_instance_entry_callback",

-                             "Failed to generate db path: %s/%s\n",

-                             inst_dirp, direntry->name);

+                                 "ldbm_instance_post_delete_instance_entry_callback",

+                                 "Failed to generate db path: %s/%s\n",

+                                 inst_dirp, direntry->name);

                              break;

                          }

  

@@ -393,6 +393,8 @@

  	int fixup_tombstone = 0;

  	int ec_locked = 0;

  	int result_sent = 0;

+     int32_t parent_op = 0;

+     struct timespec parent_time;

  

  	slapi_pblock_get( pb, SLAPI_BACKEND, &be);

  	slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &li );
@@ -405,6 +407,12 @@

  	fixup_tombstone = operation_is_flag_set(operation, OP_FLAG_TOMBSTONE_FIXUP);

  

  	dblayer_txn_init(li,&txn); /* must do this before first goto error_return */

+ 

+     if (txn.back_txn_txn == NULL) {

+         /* This is the parent operation, get the time */

+         parent_op = 1;

+         parent_time = slapi_current_rel_time_hr();

+     }

  	/* the calls to perform searches require the parent txn if any

  	   so set txn to the parent_txn until we begin the child transaction */

  	if (parent_txn) {
@@ -863,6 +871,10 @@

  	goto common_return;

  

  error_return:

+     /* Revert the caches if this is the parent operation */

+     if (parent_op) {

+         revert_cache(inst, &parent_time);

+     }

  	if ( postentry != NULL ) 

  	{

  		slapi_entry_free( postentry );

@@ -97,6 +97,8 @@

      int op_id;

      int result_sent = 0;

      Connection *pb_conn = NULL;

+     int32_t parent_op = 0;

+     struct timespec parent_time;

  

      if (slapi_pblock_get(pb, SLAPI_CONN_ID, &conn_id) < 0) {

          conn_id = 0; /* connection is NULL */
@@ -134,6 +136,13 @@

      

      /* dblayer_txn_init needs to be called before "goto error_return" */

      dblayer_txn_init(li,&txn);

+ 

+     if (txn.back_txn_txn == NULL) {

+         /* This is the parent operation, get the time */

+         parent_op = 1;

+         parent_time = slapi_current_rel_time_hr();

+     }

+ 

      /* the calls to perform searches require the parent txn if any

         so set txn to the parent_txn until we begin the child transaction */

      if (parent_txn) {
@@ -1326,6 +1335,10 @@

      goto common_return;

  

  error_return:

+     /* Revert the caches if this is the parent operation */

+     if (parent_op) {

+        revert_cache(inst, &parent_time);

+     }

      /* result already sent above - just free stuff */

      if (postentry) {

          slapi_entry_free( postentry );
@@ -1410,6 +1423,10 @@

                      slapi_pblock_set( pb, SLAPI_PLUGIN_OPRETURN, ldap_result_code ? &ldap_result_code : &retval );

                  }

                  slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &ldap_result_message);

+                 /* Revert the caches if this is the parent operation */

+                 if (parent_op) {

+                     revert_cache(inst, &parent_time);

+                 }

              }

  

              /* Release SERIAL LOCK */
@@ -1456,13 +1473,21 @@

                  }

              }

          }

-         /* remove the new entry from the cache if the op failed -

-            otherwise, leave it in */

+ 

+         if (ec && retval) {

+             /* if the operation failed, the destination entry does not exist

+              * but it has been added in dncache during cache_add_tentative

+              * we need to remove it. Else a retrieval from ep_id can give the wrong DN

+              */

+             struct backdn *bdn = dncache_find_id(&inst->inst_dncache, ec->ep_id);

+             slapi_log_err(SLAPI_LOG_CACHE, "ldbm_back_modrdn",

+                                       "operation failed, the target entry is cleared from dncache (%s)\n", slapi_entry_get_dn(ec->ep_entry));

+             CACHE_REMOVE(&inst->inst_dncache, bdn);

+             CACHE_RETURN(&inst->inst_dncache, &bdn);

+         }

+ 

          if (ec && inst) {

-             if (retval && cache_is_in_cache(&inst->inst_cache, ec)) {

-                 CACHE_REMOVE( &inst->inst_cache, ec );

-             }

-             CACHE_RETURN( &inst->inst_cache, &ec );

+             CACHE_RETURN(&inst->inst_cache, &ec);

          }

          ec = NULL;

      }

@@ -3225,7 +3225,7 @@

      run_from_cmdline = (task_flags & SLAPI_TASK_RUNNING_FROM_COMMANDLINE);

  

      be = inst->inst_be;

-     slapi_log_err(SLAPI_LOG_ERR, "upgradedb_core",

+     slapi_log_err(SLAPI_LOG_INFO, "upgradedb_core",

                      "%s: Start upgradedb.\n", inst->inst_name);

  

      if (!run_from_cmdline)
@@ -3644,6 +3644,7 @@

      char *ldbmversion = NULL;

      char *dataversion = NULL;

      int ud_flags = 0;

+     int result = 0;

                                                                           

      slapi_pblock_get(pb, SLAPI_TASK_FLAGS, &task_flags);

      slapi_pblock_get(pb, SLAPI_BACKEND_TASK, &task);
@@ -3723,8 +3724,8 @@

  

      workdbdir = rel2abspath(rawworkdbdir);

  

-     dbversion_read(li, workdbdir, &ldbmversion, &dataversion);

-     if (ldbmversion) {

+     result = dbversion_read(li, workdbdir, &ldbmversion, &dataversion);

+     if (result == 0 && ldbmversion) {

          char *ptr = PL_strstr(ldbmversion, BDB_DNFORMAT);

          if (ptr) {

              /* DN format is RFC 4514 compliant */

@@ -47,6 +47,9 @@

      PRUint64 hits, tries;

      long nentries, maxentries, count;

      size_t size, maxsize;

+     size_t thread_size;

+     size_t evicts;

+     size_t slots;

  /* NPCTE fix for bugid 544365, esc 0. <P.R> <04-Jul-2001> */

      struct stat astat;

  /* end of NPCTE fix for bugid 544365 */
@@ -122,7 +125,7 @@

      }

      /* normalized dn cache stats */

      if(ndn_cache_started()){

-         ndn_cache_get_stats(&hits, &tries, &size, &maxsize, &count);

+         ndn_cache_get_stats(&hits, &tries, &size, &maxsize, &thread_size, &evicts, &slots, &count);

          sprintf(buf, "%" PRIu64, tries);

          MSET("normalizedDnCacheTries");

          sprintf(buf, "%" PRIu64, hits);
@@ -131,6 +134,8 @@

          MSET("normalizedDnCacheMisses");

          sprintf(buf, "%lu", (unsigned long)(100.0*(double)hits / (double)(tries > 0 ? tries : 1)));

          MSET("normalizedDnCacheHitRatio");

+         sprintf(buf, "%"PRIu64, evicts);

+         MSET("NormalizedDnCacheEvictions");

          sprintf(buf, "%lu", (long unsigned int)size);

          MSET("currentNormalizedDnCacheSize");

          if(maxsize == 0){
@@ -139,6 +144,10 @@

          	sprintf(buf, "%lu", (long unsigned int)maxsize);

          }

          MSET("maxNormalizedDnCacheSize");

+         sprintf(buf, "%"PRIu64, thread_size);

+         MSET("NormalizedDnCacheThreadSize");

+         sprintf(buf, "%"PRIu64, slots);

+         MSET("NormalizedDnCacheThreadSlots");

          sprintf(buf, "%ld", count);

          MSET("currentNormalizedDnCacheCount");

      }
@@ -146,7 +155,7 @@

  #ifdef DEBUG

      {

          /* debugging for hash statistics */

-         char *x;

+         char *x = NULL;

          cache_debug_hash(&(inst->inst_cache), &x);

          val.bv_val = x;

          val.bv_len = strlen(x);

@@ -165,7 +165,6 @@

  		if (0 == ret) {

  #define ONEG  1073741824

  			perf->cache_size_bytes = mpstat->st_gbytes * ONEG + mpstat->st_bytes;

- 			perf->page_access_rate = mpstat->st_cache_hit + mpstat->st_cache_miss;			

  			perf->cache_hit = mpstat->st_cache_hit;			

  			perf->cache_try = mpstat->st_cache_hit + mpstat->st_cache_miss;			

  			perf->page_create_rate = mpstat->st_page_create;			
@@ -257,8 +256,6 @@

  			offsetof( performance_counters, log_write_rate ) },

  	{ SLAPI_LDBM_PERFCTR_AT_PREFIX "longest-chain-length",

  			offsetof( performance_counters, longest_chain_length ) },

- 	{ SLAPI_LDBM_PERFCTR_AT_PREFIX "objects-locked",

- 			offsetof( performance_counters, page_access_rate ) },

  	{ SLAPI_LDBM_PERFCTR_AT_PREFIX "page-create-rate",

  			offsetof( performance_counters, page_create_rate ) },

  	{ SLAPI_LDBM_PERFCTR_AT_PREFIX "page-read-rate",

@@ -32,7 +32,6 @@

  	uint64_t    log_write_rate;

  	uint64_t    log_bytes_since_checkpoint;

  	uint64_t    cache_size_bytes;

- 	uint64_t    page_access_rate;

  	uint64_t    cache_hit;

  	uint64_t    cache_try;

  	uint64_t    page_create_rate;

@@ -61,6 +61,7 @@

  int cache_replace(struct cache *cache, void *oldptr, void *newptr);

  int cache_has_otherref(struct cache *cache, void *bep);

  int cache_is_in_cache(struct cache *cache, void *ptr);

+ void revert_cache(ldbm_instance *inst, struct timespec *start_time);

  

  #ifdef CACHE_DEBUG

  void check_entry_cache(struct cache *cache, struct backentry *e);

@@ -101,7 +101,11 @@

      /* This doesn't control the availability of the feature, so we can take the

       * default from ldbm_config.c

       */

-     autosize_db_percentage_split = li->li_cache_autosize_split;

+     if (li->li_cache_autosize_split == 0) {

+         autosize_db_percentage_split = 40;

+     } else {

+         autosize_db_percentage_split = li->li_cache_autosize_split;

+     }

  

  

      /* Check the values are sane. */
@@ -131,10 +135,18 @@

      db_size = (autosize_db_percentage_split * zone_size) / 100;

  

      /* Cap the DB size at 512MB, as this doesn't help perf much more (lkrispen's advice) */

+     /* NOTE: Do we need a minimum DB size? */

      if (db_size > (512 * MEGABYTE)) {

          db_size = (512 * MEGABYTE);

      }

  

+     /* NOTE: Because of how we workout entry_size, even if

+      * have autosize split to say ... 90% for dbcache, because

+      * we cap db_size, we use zone_size - db_size, meaning that entry

+      * cache still gets the remaining memory *even* though we didn't use it all.

+      * If we didn't do this, entry_cache would only get 10% of of the avail, even

+      * if db_size was caped at say 5% down from 90.

+      */

      if (backend_count > 0 ) {

          /* Number of entry cache pages per backend. */

          entry_size = (zone_size - db_size) / backend_count;
@@ -157,7 +169,7 @@

      }

  

      slapi_log_err(SLAPI_LOG_NOTICE, "ldbm_back_start", "found %luk physical memory\n", mi->system_total_bytes / 1024);

-     slapi_log_err(SLAPI_LOG_NOTICE, "ldbm_back_start", "found %luk avaliable\n", mi->system_available_bytes / 1024);

+     slapi_log_err(SLAPI_LOG_NOTICE, "ldbm_back_start", "found %luk available\n", mi->system_available_bytes / 1024);

  

      /* We've now calculated the autotuning values. Do we need to apply it?

       * we use the logic of "if size is 0, or autosize is > 0. This way three

@@ -133,12 +133,15 @@

  check_db_version( struct ldbminfo *li, int *action )

  {

      int value = 0;

+     int result = 0;

      char *ldbmversion = NULL;

      char *dataversion = NULL;

  

      *action = 0;

-     dbversion_read(li, li->li_directory, &ldbmversion, &dataversion);

-     if (NULL == ldbmversion || '\0' == *ldbmversion) {

+     result = dbversion_read(li, li->li_directory, &ldbmversion, &dataversion);

+     if (result != 0) {

+         return 0;

+     } else if (NULL == ldbmversion || '\0' == *ldbmversion) {

          slapi_ch_free_string(&ldbmversion);

          slapi_ch_free_string(&dataversion);

          return 0;
@@ -215,14 +218,17 @@

      char *ldbmversion = NULL;

      char *dataversion = NULL;

      int rval = 0;

+     int result = 0;

      char inst_dir[MAXPATHLEN*2];

      char *inst_dirp = NULL;

  

      inst_dirp =

          dblayer_get_full_inst_dir(inst->inst_li, inst, inst_dir, MAXPATHLEN*2);

  

-     dbversion_read(inst->inst_li, inst_dirp, &ldbmversion, &dataversion);

-     if (NULL == ldbmversion || '\0' == *ldbmversion) {

+     result = dbversion_read(inst->inst_li, inst_dirp, &ldbmversion, &dataversion);

+     if (result != 0) {

+         return rval;

+     } else if (NULL == ldbmversion || '\0' == *ldbmversion) {

          slapi_ch_free_string(&ldbmversion);

          slapi_ch_free_string(&dataversion);

          return rval;

file modified
+24 -23
@@ -662,12 +662,14 @@

          /* We could be serving multiple database backends.  Select the appropriate one */

          /* pw_verify_be_dn will select the backend we need for us. */

  

-         if (auto_bind) {

-             /* We have no password material. We should just check who we are binding as. */

-             rc = pw_validate_be_dn(pb, &referral);

-         } else {

-             rc = pw_verify_be_dn(pb, &referral);

-         }

+         /*

+          * WARNING: We have to validate *all* other conditions *first* before

+          * we attempt the bind!

+          *

+          * this is because ldbm_bind.c will SEND THE FAILURE.

+          */

+ 

+         rc = pw_validate_be_dn(pb, &referral);

  

          if (rc == SLAPI_BIND_NO_BACKEND) {

              send_nobackend_ldap_result( pb );
@@ -677,8 +679,7 @@

              slapi_entry_free(referral);

              goto free_and_return;

          } else if (auto_bind || rc == SLAPI_BIND_SUCCESS || rc == SLAPI_BIND_ANONYMOUS) {

-             long t;

-             char* authtype = NULL;

+             char *authtype = NULL;

              /* rc is SLAPI_BIND_SUCCESS or SLAPI_BIND_ANONYMOUS */

              if(auto_bind) {

                  rc = SLAPI_BIND_SUCCESS;
@@ -736,8 +737,18 @@

                      myrc = 0;

                  }

                  if (!auto_bind) {

-                     /* 

-                      * There could be a race that bind_target_entry was not added 

+                     /*

+                      * Okay, we've made it here. FINALLY check if the entry really

+                      * can bind or not. THIS IS THE PASSWORD CHECK.

+                      */

+                     rc = pw_verify_be_dn(pb, &referral);

+                     if (rc != SLAPI_BIND_SUCCESS) {

+                         /* Invalid pass - lets bail ... */

+                         goto bind_failed;

+                     }

+ 

+                     /*

+                      * There could be a race that bind_target_entry was not added

                       * when bind_target_entry was retrieved before be_bind, but it

                       * was in be_bind.  Since be_bind returned SLAPI_BIND_SUCCESS,

                       * the entry is in the DS.  So, we need to retrieve it once more.
@@ -755,19 +766,8 @@

                                           slapi_ch_strdup(slapi_sdn_get_ndn(sdn)),

                                           NULL, NULL, NULL, bind_target_entry);

                      if (!slapi_be_is_flag_set(be, SLAPI_BE_FLAG_REMOTE_DATA)) {

-                         /* check if need new password before sending 

-                            the bind success result */

-                         myrc = need_new_pw(pb, &t, bind_target_entry, pw_response_requested);

-                         switch (myrc) {

-                         case 1:

-                             (void)slapi_add_pwd_control(pb, LDAP_CONTROL_PWEXPIRED, 0);

-                             break;

-                         case 2:

-                             (void)slapi_add_pwd_control(pb, LDAP_CONTROL_PWEXPIRING, t);

-                             break;

-                         default:

-                             break;

-                         }

+                         /* check if need new password before sending the bind success result */

+                         myrc = need_new_pw(pb, bind_target_entry, pw_response_requested);

                      }

                  }

                  if (auth_response_requested) {
@@ -786,6 +786,7 @@

                  }

              }

          } else { /* if auto_bind || rc == slapi_bind_success | slapi_bind_anonymous */

+         bind_failed:

              if (rc == LDAP_OPERATIONS_ERROR) {

                  send_ldap_result( pb, LDAP_UNWILLING_TO_PERFORM, NULL, "Function not implemented", 0, NULL );

                  goto free_and_return;

file modified
+57 -5
@@ -272,6 +272,20 @@

      return( 0 );

  }

  

+ /*

+  * Assert that some str s is in the charray, or add it.

+  */

+ void

+ charray_assert_present(char ***a, char *s)

+ {

+     int result = charray_utf8_inlist(*a, s);

+     /* Not in the list */

+     if (result == 0) {

+         char *sdup = slapi_ch_strdup(s);

+         slapi_ch_array_add_ext(a, sdup);

+     }

+ }

+ 

  int slapi_ch_array_utf8_inlist(char **a, char *s)

  {

  	return charray_utf8_inlist(a,s);
@@ -348,8 +362,9 @@

              }

          }

  

-         if ( !dup_found )

+         if ( !dup_found ) {

              res[i++] = slapi_ch_strdup( s );

+         }

      }

      res[i] = NULL;

  
@@ -413,10 +428,11 @@

      char **bp, **cp, **tmp;

      char **p;

  

-     if (c)

+     if (c) {

          tmp = *c = cool_charray_dup(a);

-     else

+     } else {

          tmp = a;

+     }

  

      for (cp = tmp; cp && *cp; cp++) {

          for (bp = b; bp && *bp; bp++) {
@@ -433,14 +449,50 @@

              for (p = cp+1; *p && *p == (char *)SUBTRACT_DEL; p++)

                  ;

              *cp = *p;    

-             if (*p == NULL)

+             if (*p == NULL) {

                  break;

-             else

+             } else {

                  *p = SUBTRACT_DEL;

+             }

          }

      }

  }

  

+ /*

+  * Provides the intersection of two arrays.

+  * IE if you have:

+  * (A, B, C)

+  * (B, D, E)

+  * result is (B,)

+  * a and b are NOT consumed in the process.

+  */

+ char **

+ charray_intersection(char **a, char **b) {

+     char **result;

+     size_t rp = 0;

+ 

+     if (a == NULL || b == NULL) {

+         return NULL;

+     }

+ 

+     size_t a_len = 0;

+     /* Find how long A is. */

+     for (; a[a_len] != NULL; a_len++);

+ 

+     /* Allocate our result, it can't be bigger than A */

+     result = (char **)slapi_ch_calloc(1, sizeof(char *) * (a_len + 1));

+ 

+     /* For each in A, see if it's in b */

+     for (size_t i = 0; a[i] != NULL; i++) {

+         if (charray_get_index(b, a[i]) != -1) {

+             result[rp] = slapi_ch_strdup(a[i]);

+             rp++;

+         }

+     }

+ 

+     return result;

+ }

+ 

  int

  charray_get_index(char **array, char *s)

  {

file modified
+172 -211
@@ -116,220 +116,181 @@

  int

  slapd_bootstrap_config(const char *configdir)

  {

- 	char configfile[MAXPATHLEN+1];

- 	PRFileInfo64 prfinfo;

- 	int rc = 0; /* Fail */

- 	int done = 0;

- 	PRInt32 nr = 0;

- 	PRFileDesc *prfd = 0;

- 	char returntext[SLAPI_DSE_RETURNTEXT_SIZE] = {0};

- 	char *buf = 0;

- 	char *lastp = 0;

- 	char *entrystr = 0;

- 	char tmpfile[MAXPATHLEN+1];

- 

- 	if (NULL == configdir) {

- 		slapi_log_err(SLAPI_LOG_ERR, "slapd_bootstrap_config",

- 			"Passed null config directory\n");

- 		return rc; /* Fail */

- 	}

- 	PR_snprintf(configfile, sizeof(configfile), "%s/%s", configdir,

- 				CONFIG_FILENAME);

- 	PR_snprintf(tmpfile, sizeof(tmpfile), "%s/%s.tmp", configdir,

- 					CONFIG_FILENAME);

- 	if ( (rc = dse_check_file(configfile, tmpfile)) == 0 ) {

- 		PR_snprintf(tmpfile, sizeof(tmpfile), "%s/%s.bak", configdir,

- 					CONFIG_FILENAME);

- 		rc = dse_check_file(configfile, tmpfile);

- 	}

- 

- 	if ( (rc = PR_GetFileInfo64( configfile, &prfinfo )) != PR_SUCCESS )

- 	{

- 		PRErrorCode prerr = PR_GetError();

- 		slapi_log_err(SLAPI_LOG_ERR, "slapd_bootstrap_config",

- 			"The given config file %s could not be accessed, " SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",

- 			configfile, prerr, slapd_pr_strerror(prerr));

- 		return rc;

- 	}

- 	else if (( prfd = PR_Open( configfile, PR_RDONLY,

- 							   SLAPD_DEFAULT_FILE_MODE )) == NULL )

- 	{

- 		PRErrorCode prerr = PR_GetError();

- 		slapi_log_err(SLAPI_LOG_ERR, "slapd_bootstrap_config",

- 			"The given config file %s could not be opened for reading, " SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",

- 			configfile, prerr, slapd_pr_strerror(prerr));

- 		return rc; /* Fail */

- 	}

- 	else

- 	{

- 		/* read the entire file into core */

- 		buf = slapi_ch_malloc( prfinfo.size + 1 );

- 		if (( nr = slapi_read_buffer( prfd, buf, prfinfo.size )) < 0 )

- 		{

- 			slapi_log_err(SLAPI_LOG_ERR, "slapd_bootstrap_config",

- 				"Could only read %d of %ld bytes from config file %s\n",

- 				nr, (long int)prfinfo.size, configfile);

- 			rc = 0; /* Fail */

- 			done= 1;

- 		}

-                           

- 		(void)PR_Close(prfd);

- 		buf[ nr ] = '\0';

- 

- 		if(!done)

- 		{

- 			char workpath[MAXPATHLEN+1];

- 			char loglevel[BUFSIZ];

- 			char maxdescriptors[BUFSIZ];

- 			char val[BUFSIZ];

- 			char _localuser[BUFSIZ];

- 			char logenabled[BUFSIZ];

- 			char schemacheck[BUFSIZ];

- 			char syntaxcheck[BUFSIZ];

- 			char syntaxlogging[BUFSIZ];

- 			char plugintracking[BUFSIZ];

- 			char dn_validate_strict[BUFSIZ];

- 			char moddn_aci[BUFSIZ];

- 			Slapi_DN plug_dn;

- 

- 			workpath[0] = loglevel[0] = maxdescriptors[0] = '\0';

- 			val[0] = logenabled[0] = schemacheck[0] = syntaxcheck[0] = '\0';

- 			syntaxlogging[0] = _localuser[0] = '\0';

- 			plugintracking [0] = dn_validate_strict[0] = moddn_aci[0] ='\0';

- 

- 			/* Convert LDIF to entry structures */

- 			slapi_sdn_init_ndn_byref(&plug_dn, PLUGIN_BASE_DN);

- 			while ((entrystr = dse_read_next_entry(buf, &lastp)) != NULL)

- 			{

- 				char errorbuf[SLAPI_DSE_RETURNTEXT_SIZE];

- 				/*

- 				 * XXXmcs: it would be better to also pass

- 				 * SLAPI_STR2ENTRY_REMOVEDUPVALS in the flags, but

- 				 * duplicate value checking requires that the syntax

- 				 * and schema subsystems be initialized... and they

- 				 * are not yet.

- 				 */

- 				Slapi_Entry	*e = slapi_str2entry(entrystr,

- 							SLAPI_STR2ENTRY_NOT_WELL_FORMED_LDIF);

- 				if (e == NULL)

- 				{

- 					slapi_log_err(SLAPI_LOG_ERR, "slapd_bootstrap_config - "

- 						"The entry [%s] in the configfile %s was empty or could not be parsed\n",

- 						entrystr, configfile, 0);

- 					continue;

- 				}

- 				/* increase file descriptors */

- 				if (!maxdescriptors[0] &&

- 					entry_has_attr_and_value(e, CONFIG_MAXDESCRIPTORS_ATTRIBUTE,

- 									 maxdescriptors, sizeof(maxdescriptors)))

- 				{

- 					if (config_set_maxdescriptors(

- 									CONFIG_MAXDESCRIPTORS_ATTRIBUTE,

- 									maxdescriptors, errorbuf, CONFIG_APPLY)

- 						!= LDAP_SUCCESS)

- 					{

- 						slapi_log_err(SLAPI_LOG_ERR, "slapd_bootstrap_config - "

- 							"%s: %s: %s\n", configfile, CONFIG_MAXDESCRIPTORS_ATTRIBUTE, errorbuf);

- 					}

- 				}

- 

- 				/* see if we need to enable error logging */

- 				if (!logenabled[0] &&

- 					entry_has_attr_and_value(e,

- 											 CONFIG_ERRORLOG_LOGGING_ENABLED_ATTRIBUTE,

- 											 logenabled, sizeof(logenabled)))

- 				{

- 					if (log_set_logging(

- 						CONFIG_ERRORLOG_LOGGING_ENABLED_ATTRIBUTE,

- 						logenabled, SLAPD_ERROR_LOG, errorbuf, CONFIG_APPLY)

- 						!= LDAP_SUCCESS)

- 					{

- 						slapi_log_err(SLAPI_LOG_ERR, "slapd_bootstrap_config - %s: %s: %s\n", 

- 							configfile, CONFIG_ERRORLOG_LOGGING_ENABLED_ATTRIBUTE, errorbuf);

- 					}

- 				}

+     char configfile[MAXPATHLEN + 1];

+     PRFileInfo64 prfinfo;

+     int rc = 0; /* Fail */

+     int done = 0;

+     PRInt32 nr = 0;

+     PRFileDesc *prfd = 0;

+     char returntext[SLAPI_DSE_RETURNTEXT_SIZE] = {0};

+     char *buf = 0;

+     char *lastp = 0;

+     char *entrystr = 0;

+     char tmpfile[MAXPATHLEN + 1];

+ 

+     if (NULL == configdir) {

+         slapi_log_err(SLAPI_LOG_ERR, "slapd_bootstrap_config",

+                       "Passed null config directory\n");

+         return rc; /* Fail */

+     }

+     PR_snprintf(configfile, sizeof(configfile), "%s/%s", configdir, CONFIG_FILENAME);

+     PR_snprintf(tmpfile, sizeof(tmpfile), "%s/%s.bak", configdir, CONFIG_FILENAME);

+     rc = dse_check_file(configfile, tmpfile);

+     if (rc == 0) {

+         /* EVERYTHING IS GOING WRONG, ARRGHHHHHH */

+         slapi_log_err(SLAPI_LOG_ERR, "slapd_bootstrap_config", "No valid configurations can be accessed! You must restore %s from backup!\n", configfile);

+         return 0;

+     }

  

- 				/* set the local user name; needed to set up error log */

- 				if (!_localuser[0] &&

- 					entry_has_attr_and_value(e, CONFIG_LOCALUSER_ATTRIBUTE,

- 								_localuser, sizeof(_localuser)))

- 				{

- 					if (config_set_localuser(CONFIG_LOCALUSER_ATTRIBUTE,

- 						_localuser, errorbuf, CONFIG_APPLY) != LDAP_SUCCESS)

- 					{

- 						slapi_log_err(SLAPI_LOG_ERR, "slapd_bootstrap_config - %s: %s: %s. \n",

- 							configfile, CONFIG_LOCALUSER_ATTRIBUTE, errorbuf);

- 					}

- 				}

- 				

- 				/* set the log file name */

- 				workpath[0] = '\0';

- 				if (!workpath[0] &&

- 					entry_has_attr_and_value(e, CONFIG_ERRORLOG_ATTRIBUTE,

- 								workpath, sizeof(workpath)))

- 				{

- 					if (config_set_errorlog(CONFIG_ERRORLOG_ATTRIBUTE,

- 						workpath, errorbuf, CONFIG_APPLY) != LDAP_SUCCESS)

- 					{

- 						slapi_log_err(SLAPI_LOG_ERR, "slapd_bootstrap_config - %s: %s: %s. \n", 

- 							configfile, CONFIG_ERRORLOG_ATTRIBUTE, errorbuf);

- 					}

- 				}

- 				/* set the error log level */

- 				if (!loglevel[0] &&

- 					entry_has_attr_and_value(e, CONFIG_LOGLEVEL_ATTRIBUTE,

- 						loglevel, sizeof(loglevel)))

- 				{

- 					if (should_detach || !config_get_errorlog_level())

- 					{ /* -d wasn't on command line */

- 						if (config_set_errorlog_level(CONFIG_LOGLEVEL_ATTRIBUTE,

- 							loglevel, errorbuf, CONFIG_APPLY) != LDAP_SUCCESS)

- 						{

- 							slapi_log_err(SLAPI_LOG_ERR, "%s: %s: %s. \n", configfile,

- 									  CONFIG_LOGLEVEL_ATTRIBUTE, errorbuf);

- 						}

- 					}

- 					else

- 					{

- 						if (strcmp(loglevel, "0") ||

- 						    config_get_errorlog_level() != SLAPD_DEFAULT_ERRORLOG_LEVEL)

- 						{

- 							/*

- 							 * loglevel of zero and SLAPD_DEFAULT_ERRORLOG_LEVEL are the

- 							 * same.  Only report an error if they are different.

- 							 */

- 							slapi_log_err(SLAPI_LOG_NOTICE, "slapd_bootstrap_config",

- 								"%s: ignoring %s (since -d %d was given on the command line)\n",

- 								CONFIG_LOGLEVEL_ATTRIBUTE, loglevel, config_get_errorlog_level());

- 						}

- 					}

- 				}

+     if ((rc = PR_GetFileInfo64(configfile, &prfinfo)) != PR_SUCCESS) {

+         PRErrorCode prerr = PR_GetError();

+         slapi_log_err(SLAPI_LOG_ERR, "slapd_bootstrap_config",

+                       "The given config file %s could not be accessed, " SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",

+                       configfile, prerr, slapd_pr_strerror(prerr));

+         return rc;

+     } else if ((prfd = PR_Open(configfile, PR_RDONLY,

+                                SLAPD_DEFAULT_FILE_MODE)) == NULL) {

+         PRErrorCode prerr = PR_GetError();

+         slapi_log_err(SLAPI_LOG_ERR, "slapd_bootstrap_config",

+                       "The given config file %s could not be opened for reading, " SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",

+                       configfile, prerr, slapd_pr_strerror(prerr));

+         return rc; /* Fail */

+     } else {

+         /* read the entire file into core */

+         buf = slapi_ch_malloc(prfinfo.size + 1);

+         if ((nr = slapi_read_buffer(prfd, buf, prfinfo.size)) < 0) {

+             slapi_log_err(SLAPI_LOG_ERR, "slapd_bootstrap_config",

+                           "Could only read %d of %ld bytes from config file %s\n",

+                           nr, (long int)prfinfo.size, configfile);

+             rc = 0; /* Fail */

+             done = 1;

+         }

  

- 				/* set the cert dir; needed in slapd_nss_init */

- 				workpath[0] = '\0';

- 				if (entry_has_attr_and_value(e, CONFIG_CERTDIR_ATTRIBUTE,

- 						workpath, sizeof(workpath)))

- 				{

- 					if (config_set_certdir(CONFIG_CERTDIR_ATTRIBUTE,

- 							workpath, errorbuf, CONFIG_APPLY) != LDAP_SUCCESS)

- 					{

- 						slapi_log_err(SLAPI_LOG_ERR, "slapd_bootstrap_config", "%s: %s: %s. \n",

- 							configfile, CONFIG_CERTDIR_ATTRIBUTE, errorbuf);

- 					}

- 				}

+         (void)PR_Close(prfd);

+         buf[nr] = '\0';

+ 

+         if (!done) {

+             char workpath[MAXPATHLEN + 1];

+             char loglevel[BUFSIZ];

+             char maxdescriptors[BUFSIZ];

+             char val[BUFSIZ];

+             char _localuser[BUFSIZ];

+             char logenabled[BUFSIZ];

+             char schemacheck[BUFSIZ];

+             char syntaxcheck[BUFSIZ];

+             char syntaxlogging[BUFSIZ];

+             char plugintracking[BUFSIZ];

+             char dn_validate_strict[BUFSIZ];

+             char moddn_aci[BUFSIZ];

+             Slapi_DN plug_dn;

+ 

+             workpath[0] = loglevel[0] = maxdescriptors[0] = '\0';

+             val[0] = logenabled[0] = schemacheck[0] = syntaxcheck[0] = '\0';

+             syntaxlogging[0] = _localuser[0] = '\0';

+             plugintracking[0] = dn_validate_strict[0] = moddn_aci[0] = '\0';

+ 

+             /* Convert LDIF to entry structures */

+             slapi_sdn_init_ndn_byref(&plug_dn, PLUGIN_BASE_DN);

+             while ((entrystr = dse_read_next_entry(buf, &lastp)) != NULL) {

+                 char errorbuf[SLAPI_DSE_RETURNTEXT_SIZE];

+                 /*

+                  * XXXmcs: it would be better to also pass

+                  * SLAPI_STR2ENTRY_REMOVEDUPVALS in the flags, but

+                  * duplicate value checking requires that the syntax

+                  * and schema subsystems be initialized... and they

+                  * are not yet.

+                  */

+                 Slapi_Entry *e = slapi_str2entry(entrystr,

+                                                  SLAPI_STR2ENTRY_NOT_WELL_FORMED_LDIF);

+                 if (e == NULL) {

+                     slapi_log_err(SLAPI_LOG_ERR, "slapd_bootstrap_config - "

+                                                  "The entry [%s] in the configfile %s was empty or could not be parsed\n",

+                                   entrystr, configfile, 0);

+                     continue;

+                 }

+                 /* increase file descriptors */

+                 if (!maxdescriptors[0] &&

+                     entry_has_attr_and_value(e, CONFIG_MAXDESCRIPTORS_ATTRIBUTE,

+                                              maxdescriptors, sizeof(maxdescriptors))) {

+                     if (config_set_maxdescriptors(

+                             CONFIG_MAXDESCRIPTORS_ATTRIBUTE,

+                             maxdescriptors, errorbuf, CONFIG_APPLY) != LDAP_SUCCESS) {

+                         slapi_log_err(SLAPI_LOG_ERR, "slapd_bootstrap_config - "

+                                                      "%s: %s: %s\n",

+                                       configfile, CONFIG_MAXDESCRIPTORS_ATTRIBUTE, errorbuf);

+                     }

+                 }

+ 

+                 /* see if we need to enable error logging */

+                 if (!logenabled[0] &&

+                     entry_has_attr_and_value(e,

+                                              CONFIG_ERRORLOG_LOGGING_ENABLED_ATTRIBUTE,

+                                              logenabled, sizeof(logenabled))) {

+                     if (log_set_logging(

+                             CONFIG_ERRORLOG_LOGGING_ENABLED_ATTRIBUTE,

+                             logenabled, SLAPD_ERROR_LOG, errorbuf, CONFIG_APPLY) != LDAP_SUCCESS) {

+                         slapi_log_err(SLAPI_LOG_ERR, "slapd_bootstrap_config - %s: %s: %s\n",

+                                       configfile, CONFIG_ERRORLOG_LOGGING_ENABLED_ATTRIBUTE, errorbuf);

+                     }

+                 }

+ 

+                 /* set the local user name; needed to set up error log */

+                 if (!_localuser[0] &&

+                     entry_has_attr_and_value(e, CONFIG_LOCALUSER_ATTRIBUTE,

+                                              _localuser, sizeof(_localuser))) {

+                     if (config_set_localuser(CONFIG_LOCALUSER_ATTRIBUTE,

+                                              _localuser, errorbuf, CONFIG_APPLY) != LDAP_SUCCESS) {

+                         slapi_log_err(SLAPI_LOG_ERR, "slapd_bootstrap_config - %s: %s: %s. \n",

+                                       configfile, CONFIG_LOCALUSER_ATTRIBUTE, errorbuf);

+                     }

+                 }

+ 

+                 /* set the log file name */

+                 workpath[0] = '\0';

+                 if (!workpath[0] &&

+                     entry_has_attr_and_value(e, CONFIG_ERRORLOG_ATTRIBUTE,

+                                              workpath, sizeof(workpath))) {

+                     if (config_set_errorlog(CONFIG_ERRORLOG_ATTRIBUTE,

+                                             workpath, errorbuf, CONFIG_APPLY) != LDAP_SUCCESS) {

+                         slapi_log_err(SLAPI_LOG_ERR, "slapd_bootstrap_config - %s: %s: %s. \n",

+                                       configfile, CONFIG_ERRORLOG_ATTRIBUTE, errorbuf);

+                     }

+                 }

+                 /* set the error log level */

+                 if (!loglevel[0] &&

+                     entry_has_attr_and_value(e, CONFIG_LOGLEVEL_ATTRIBUTE,

+                                              loglevel, sizeof(loglevel))) {

+                     if (should_detach || !config_get_errorlog_level()) { /* -d wasn't on command line */

+                         if (config_set_errorlog_level(CONFIG_LOGLEVEL_ATTRIBUTE,

+                                                       loglevel, errorbuf, CONFIG_APPLY) != LDAP_SUCCESS) {

+                             slapi_log_err(SLAPI_LOG_ERR, "%s: %s: %s. \n", configfile,

+                                           CONFIG_LOGLEVEL_ATTRIBUTE, errorbuf);

+                         }

+                     }

+                 }

+ 

+                 /* set the cert dir; needed in slapd_nss_init */

+                 workpath[0] = '\0';

+                 if (entry_has_attr_and_value(e, CONFIG_CERTDIR_ATTRIBUTE,

+                                              workpath, sizeof(workpath))) {

+                     if (config_set_certdir(CONFIG_CERTDIR_ATTRIBUTE,

+                                            workpath, errorbuf, CONFIG_APPLY) != LDAP_SUCCESS) {

+                         slapi_log_err(SLAPI_LOG_ERR, "slapd_bootstrap_config", "%s: %s: %s. \n",

+                                       configfile, CONFIG_CERTDIR_ATTRIBUTE, errorbuf);

+                     }

+                 }

+ 

+                 /* set the sasl path; needed in main */

+                 workpath[0] = '\0';

+                 if (entry_has_attr_and_value(e, CONFIG_SASLPATH_ATTRIBUTE,

+                                              workpath, sizeof(workpath))) {

+                     if (config_set_saslpath(CONFIG_SASLPATH_ATTRIBUTE,

+                                             workpath, errorbuf, CONFIG_APPLY) != LDAP_SUCCESS) {

+                         slapi_log_err(SLAPI_LOG_ERR, "slapd_bootstrap_config", "%s: %s: %s. \n",

+                                       configfile, CONFIG_SASLPATH_ATTRIBUTE, errorbuf);

+                     }

+                 }

  

- 				/* set the sasl path; needed in main */

- 				 workpath[0] = '\0';

- 				if (entry_has_attr_and_value(e, CONFIG_SASLPATH_ATTRIBUTE,

- 						workpath, sizeof(workpath)))

- 				{

- 					if (config_set_saslpath(CONFIG_SASLPATH_ATTRIBUTE,

- 							workpath, errorbuf, CONFIG_APPLY) != LDAP_SUCCESS)

- 					{

- 						slapi_log_err(SLAPI_LOG_ERR, "slapd_bootstrap_config", "%s: %s: %s. \n",

- 							configfile, CONFIG_SASLPATH_ATTRIBUTE, errorbuf);

- 					}

- 				}

  #if defined(ENABLE_LDAPI)

  				/* set the ldapi file path; needed in main */

  				workpath[0] = '\0';

file modified
+27 -10
@@ -1506,8 +1506,6 @@

  	int maxthreads = 0;

  	int enable_nunc_stans = 0;

  	long bypasspollcnt = 0;

- 	Connection *pb_conn = NULL;

- 	Operation *pb_op = NULL;

  

  #ifdef ENABLE_NUNC_STANS

  	enable_nunc_stans = config_get_enable_nunc_stans();
@@ -1524,11 +1522,14 @@

  		if( op_shutdown ) {

  			slapi_log_err(SLAPI_LOG_TRACE, "connection_threadmain",

  				"op_thread received shutdown signal\n");

+ 			slapi_pblock_destroy(pb);

  			g_decr_active_threadcnt();

  			return;

  		}

  

  		if (!thread_turbo_flag && !more_data) {

+ 			Connection *pb_conn = NULL;

+ 

  			/* If more data is left from the previous connection_read_operation,

  			   we should finish the op now.  Client might be thinking it's

  			   done sending the request and wait for the response forever.
@@ -1539,7 +1540,6 @@

  			 * Connection wait for new work provides the conn and op for us. 

  			 */

  			slapi_pblock_get(pb, SLAPI_CONNECTION, &pb_conn);

- 			slapi_pblock_get(pb, SLAPI_OPERATION, &pb_op);

  

  			switch (ret) {

  				case CONN_NOWORK:
@@ -1548,6 +1548,7 @@

  				case CONN_SHUTDOWN:

  					slapi_log_err(SLAPI_LOG_TRACE, "connection_threadmain",

  						"op_thread received shutdown signal\n");

+ 					slapi_pblock_destroy(pb);

  					g_decr_active_threadcnt();

  					return;

  				case CONN_FOUND_WORK_TO_DO:
@@ -1556,7 +1557,8 @@

  					   in connection_activity when the conn is added to the

  					   work queue, setup_pr_read_pds won't add the connection prfd

  					   to the poll list */

- 					if(pb_conn->c_opscompleted == 0){

+ 					PR_EnterMonitor(pb_conn->c_mutex);

+ 					if(pb_conn->c_anonlimits_set == 0){

  						/*

  						 * We have a new connection, set the anonymous reslimit idletimeout

  						 * if applicable.
@@ -1577,7 +1579,13 @@

  							}

  						}

  						slapi_ch_free_string( &anon_dn );

+ 						/*

+ 						 * Set connection as initialized to avoid setting anonymous limits

+ 						 * multiple times on the same connection

+ 						 */

+ 						pb_conn->c_anonlimits_set = 1;

  					}

+ 					PR_ExitMonitor(pb_conn->c_mutex);

  					if (connection_call_io_layer_callbacks(pb_conn)) {

  						slapi_log_err(SLAPI_LOG_ERR, "connection_threadmain",

  							"Could not add/remove IO layers from connection\n");
@@ -1778,7 +1786,7 @@

  			connection_release_nolock(conn);

  			PR_ExitMonitor(conn->c_mutex);

  			signal_listner();

-             slapi_pblock_destroy(pb);

+ 			slapi_pblock_destroy(pb);

  			return;

  		}

  		/*
@@ -1793,14 +1801,15 @@

  		/* total number of ops for the server */

  		slapi_counter_increment(ops_completed);

  		/* If this op isn't a persistent search, remove it */

- 		if ( pb_op->o_flags & OP_FLAG_PS ) {

+ 		if ( op->o_flags & OP_FLAG_PS ) {

  			    PR_EnterMonitor(conn->c_mutex);

  			    connection_release_nolock (conn); /* psearch acquires ref to conn - release this one now */

  			    PR_ExitMonitor(conn->c_mutex);

  			    /* ps_add makes a shallow copy of the pb - so we

- 			     * can't free it or init it here - just memset it to 0

+ 			     * can't free it or init it here - just set operation to NULL.

  			     * ps_send_results will call connection_remove_operation_ext to free it

  			     */

+                 slapi_pblock_set(pb, SLAPI_OPERATION, NULL);

  	            slapi_pblock_init(pb);

  		} else {

  			/* delete from connection operation queue & decr refcnt */
@@ -1810,9 +1819,17 @@

  

  			/* If we're in turbo mode, we keep our reference to the connection alive */

  			/* can't use the more_data var because connection could have changed in another thread */

- 			more_data = conn_buffered_data_avail_nolock(conn, &conn_closed) ? 1 : 0;

- 			slapi_log_err(SLAPI_LOG_CONNS,"connection_threadmain", "conn %" PRIu64 " check more_data %d thread_turbo_flag %d\n",

- 			          conn->c_connid,more_data,thread_turbo_flag);

+                         slapi_log_err(SLAPI_LOG_CONNS, "connection_threadmain", "conn %" PRIu64 " check more_data %d thread_turbo_flag %d"

+                             "repl_conn_bef %d, repl_conn_now %d\n",

+                             conn->c_connid, more_data, thread_turbo_flag,

+                             replication_connection, conn->c_isreplication_session);

+                         if (!replication_connection &&  conn->c_isreplication_session) {

+                             /* it a connection that was just flagged as replication connection */

+                             more_data = 0;

+                         } else {

+                             /* normal connection or already established replication connection */

+                             more_data = conn_buffered_data_avail_nolock(conn, &conn_closed) ? 1 : 0;

+                         }

  			if (!more_data) {

  				if (!thread_turbo_flag) {

  					/*

file modified
+6 -2
@@ -368,14 +368,18 @@

  		/* let's revisit the seq num - if the new time is > the old

  		   tiem, we should reset the seq number to remote + 1 if

  		   this won't cause a wrap around */

- 		if (new_time > cur_time) {

+ 		if (new_time >= cur_time) {

  			/* just set seq_num regardless of whether the current one

  			   is < or > than the remote one - the goal of this function

  			   is to make sure we generate CSNs > the remote CSN - if

  			   we have increased the time, we can decrease the seqnum

  			   and still guarantee that any new CSNs generated will be

  			   > any current CSNs we have generated */

- 			gen->state.seq_num = remote_seqnum + 1;

+ 			if (remote_seqnum < gen->state.seq_num) {

+ 				gen->state.seq_num++;

+ 			} else {

+ 				gen->state.seq_num = remote_seqnum + 1;

+ 			}

  		}

  		if (slapi_is_loglevel_set(SLAPI_LOG_REPL)) {

  			slapi_log_err(SLAPI_LOG_REPL, "csngen_adjust_time",

file modified
+516 -293
@@ -22,6 +22,24 @@

  #include "slap.h"

  #include <plhash.h>

  

+ #include <inttypes.h>

+ #include <stddef.h> /* for size_t */

+ 

+ #if defined(HAVE_SYS_ENDIAN_H)

+ #include <sys/endian.h>

+ #elif defined(HAVE_ENDIAN_H)

+ #include <endian.h>

+ #else

+ #error platform header for endian detection not found.

+ #endif

+ 

+ /* See: http://sourceforge.net/p/predef/wiki/Endianness/ */

+ #if defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN

+ #define _le64toh(x) ((uint64_t)(x))

+ #else

+ #define _le64toh(x) le64toh(x)

+ #endif

+ 

  #undef SDN_DEBUG

  

  static void add_rdn_av( char *avstart, char *avend, int *rdn_av_countp,
@@ -33,52 +51,89 @@

  static int does_cn_uses_dn_syntax_in_dns(char *type, char *dn);

  

  /* normalized dn cache related definitions*/

- struct

- ndn_cache_lru

- {

-     struct ndn_cache_lru *prev;

-     struct ndn_cache_lru *next;

-     char *key;

- };

- 

- struct

- ndn_cache_ctx

- {

-     struct ndn_cache_lru *head;

-     struct ndn_cache_lru *tail;

+ struct ndn_cache_stats {

      Slapi_Counter *cache_hits;

      Slapi_Counter *cache_tries;

-     Slapi_Counter *cache_misses;

-     size_t cache_size;

-     size_t cache_max_size;

-     long cache_count;

+     Slapi_Counter *cache_count;

+     Slapi_Counter *cache_size;

+     Slapi_Counter *cache_evicts;

+     size_t max_size;

+     size_t thread_max_size;

+     size_t slots;

  };

  

- struct

- ndn_hash_val

- {

+ struct ndn_cache_value {

+     size_t size;

+     size_t slot;

+     char *dn;

      char *ndn;

-     size_t len;

-     int size;

-     struct ndn_cache_lru *lru_node; /* used to speed up lru shuffling */

+     struct ndn_cache_value *next;

+     struct ndn_cache_value *prev;

+     struct ndn_cache_value *child;

+ };

+ 

+ /*

+  * This uses a similar alloc trick to IDList to keep

+  * The amount of derefs small.

+  */

+ struct ndn_cache {

+     /*

+      * We keep per thread stats and flush them occasionally

+      */

+     size_t max_size;

+     /* Need to track this because we need to provide diffs to counter */

+     size_t last_count;

+     size_t count;

+     /* Number of ops */

+     size_t tries;

+     /* hit vs miss. in theroy miss == tries - hits.*/

+     size_t hits;

+     /* How many values we kicked out */

+     size_t evicts;

+     /* Need to track this because we need to provide diffs to counter */

+     size_t last_size;

+     size_t size;

+ 

+     size_t slots;

+     /*

+      * This is used by siphash to prevent hash bugket attacks

+      */

+     char key[16];

+ 

+     struct ndn_cache_value *head;

+     struct ndn_cache_value *tail;

+     struct ndn_cache_value *table[1];

  };

  

- #define NDN_FLUSH_COUNT 10000 /* number of DN's to remove when cache fills up */

- #define NDN_MIN_COUNT 1000 /* the minimum number of DN's to keep in the cache */

- #define NDN_CACHE_BUCKETS 2053 /* prime number */

+ /*

+  * This means we need 1 MB minimum per thread

+  * 

+  */

+ #define NDN_CACHE_MINIMUM_CAPACITY 1048576

+ /*

+  * This helps us define the number of hashtable slots

+  * to create. We assume an average DN is 64 chars long

+  * This way we end up we a ht entry of:

+  * 8 bytes: from the table pointing to us.

+  * 8 bytes: next ptr

+  * 8 bytes: prev ptr

+  * 8 bytes + 64: dn

+  * 8 bytes + 64: ndn itself.

+  * This gives us 168 bytes. In theory this means

+  * 6241 entries, but we have to clamp this to a power of

+  * two, so we have 8192 slots. In reality, dns may be

+  * shorter *and* the dn may be the same as the ndn

+  * so we *may* store more ndns that this. Again, a good reason

+  * to round the ht size up!

+  */

+ #define NDN_ENTRY_AVG_SIZE 168

+ /*

+  * After how many operations do we sync our per-thread stats.

+  */

+ #define NDN_STAT_COMMIT_FREQUENCY 256

  

- static PLHashNumber ndn_hash_string(const void *key);

  static int ndn_cache_lookup(char *dn, size_t dn_len, char **result, char **udn, int *rc);

- static void ndn_cache_update_lru(struct ndn_cache_lru **node);

  static void ndn_cache_add(char *dn, size_t dn_len, char *ndn, size_t ndn_len);

- static void ndn_cache_delete(char *dn);

- static void ndn_cache_flush(void);

- static void ndn_cache_free(void);

- static int ndn_started = 0;

- static PRLock *lru_lock = NULL;

- static Slapi_RWLock *ndn_cache_lock = NULL;

- static struct ndn_cache_ctx *ndn_cache = NULL;

- static PLHashTable *ndn_cache_hashtable = NULL;

  

  #define ISBLANK(c)	((c) == ' ')

  #define ISBLANKSTR(s)	(((*(s)) == '2') && (*((s)+1) == '0'))
@@ -2770,166 +2825,408 @@

   *

   */

  

+ /* <MIT License>

+  Copyright (c) 2013  Marek Majkowski <marek@popcount.org>

+ 

+  Permission is hereby granted, free of charge, to any person obtaining a copy

+  of this software and associated documentation files (the "Software"), to deal

+  in the Software without restriction, including without limitation the rights

+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell

+  copies of the Software, and to permit persons to whom the Software is

+  furnished to do so, subject to the following conditions:

+ 

+  The above copyright notice and this permission notice shall be included in

+  all copies or substantial portions of the Software.

+ 

+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR

+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,

+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE

+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER

+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,

+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN

+  THE SOFTWARE.

+  </MIT License>

+ 

+  Original location:

+     https://github.com/majek/csiphash/

+ 

+  Solution inspired by code from:

+     Samuel Neves (supercop/crypto_auth/siphash24/little)

+     djb (supercop/crypto_auth/siphash24/little2)

+     Jean-Philippe Aumasson (https://131002.net/siphash/siphash24.c)

+ */

+ 

+ #define ROTATE(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b))))

+ 

+ #define HALF_ROUND(a, b, c, d, s, t) \

+     a += b;                          \

+     c += d;                          \

+     b = ROTATE(b, s) ^ a;            \

+     d = ROTATE(d, t) ^ c;            \

+     a = ROTATE(a, 32);

+ 

+ #define ROUND(v0, v1, v2, v3)           \

+     HALF_ROUND(v0, v1, v2, v3, 13, 16); \

+     HALF_ROUND(v2, v1, v0, v3, 17, 21)

+ 

+ #define cROUND(v0, v1, v2, v3) \

+     ROUND(v0, v1, v2, v3)

+ 

+ #define dROUND(v0, v1, v2, v3) \

+     ROUND(v0, v1, v2, v3);     \

+     ROUND(v0, v1, v2, v3);     \

+     ROUND(v0, v1, v2, v3)

+ 

+ 

+ static uint64_t

+ sds_siphash13(const void *src, size_t src_sz, const char key[16])

+ {

+     const uint64_t *_key = (uint64_t *)key;

+     uint64_t k0 = _le64toh(_key[0]);

+     uint64_t k1 = _le64toh(_key[1]);

+     uint64_t b = (uint64_t)src_sz << 56;

+     const uint64_t *in = (uint64_t *)src;

+ 

+     uint64_t v0 = k0 ^ 0x736f6d6570736575ULL;

+     uint64_t v1 = k1 ^ 0x646f72616e646f6dULL;

+     uint64_t v2 = k0 ^ 0x6c7967656e657261ULL;

+     uint64_t v3 = k1 ^ 0x7465646279746573ULL;

+ 

+     while (src_sz >= 8) {

+         uint64_t mi = _le64toh(*in);

+         in += 1;

+         src_sz -= 8;

+         v3 ^= mi;

+         // cround

+         cROUND(v0, v1, v2, v3);

+         v0 ^= mi;

+     }

+ 

+     uint64_t t = 0;

+     uint8_t *pt = (uint8_t *)&t;

+     uint8_t *m = (uint8_t *)in;

+ 

+     switch (src_sz) {

+     case 7:

+         pt[6] = m[6]; /* FALLTHRU */

+     case 6:

+         pt[5] = m[5]; /* FALLTHRU */

+     case 5:

+         pt[4] = m[4]; /* FALLTHRU */

+     case 4:

+         *((uint32_t *)&pt[0]) = *((uint32_t *)&m[0]);

+         break;

+     case 3:

+         pt[2] = m[2]; /* FALLTHRU */

+     case 2:

+         pt[1] = m[1]; /* FALLTHRU */

+     case 1:

+         pt[0] = m[0]; /* FALLTHRU */

+     }

+     b |= _le64toh(t);

+ 

+     v3 ^= b;

+     // cround

+     cROUND(v0, v1, v2, v3);

+     v0 ^= b;

+     v2 ^= 0xff;

+     // dround

+     dROUND(v0, v1, v2, v3);

+     return (v0 ^ v1) ^ (v2 ^ v3);

+ }

+ 

+ static pthread_key_t ndn_cache_key;

+ static pthread_once_t ndn_cache_key_once = PTHREAD_ONCE_INIT;

+ static struct ndn_cache_stats t_cache_stats = {0};

  /*

-  *  Hashing function using Bernstein's method

+  * WARNING: For some reason we try to use the NDN cache *before*

+  * we have a chance to configure it. As a result, we need to rely

+  * on a trick in the way we start, that we start in one thread

+  * so we can manipulate ints as though they were atomics, then

+  * we start in *one* thread, so it's set, then when threads

+  * fork the get barriers, so we can go from there. However we *CANNOT*

+  * change this at runtime without expensive atomics per op, so lets

+  * not bother until we improve libglobs to be COW.

   */

- static PLHashNumber

- ndn_hash_string(const void *key)

- {

-     PLHashNumber hash = 5381;

-     unsigned char *x = (unsigned char *)key;

-     int c;

+ static int32_t ndn_enabled = 0;

+ 

+ static struct ndn_cache *

+ ndn_thread_cache_create(size_t thread_max_size, size_t slots) {

+     size_t t_cache_size = sizeof(struct ndn_cache) + (slots * sizeof(struct ndn_cache_value *));

+     struct ndn_cache *t_cache = (struct ndn_cache *)slapi_ch_calloc(1, t_cache_size);

+ 

+     t_cache->max_size = thread_max_size;

+     t_cache->slots = slots;

  

-     while ((c = *x++)){

-         hash = ((hash << 5) + hash) ^ c;

+     return t_cache;

+ }

+ 

+ static void

+ ndn_thread_cache_commit_status(struct ndn_cache *t_cache) {

+     /*

+      * Every so often we commit these atomically. We do this infrequently

+      * to avoid the costly atomics.

+      */

+     if (t_cache->tries % NDN_STAT_COMMIT_FREQUENCY == 0) {

+         /* We can just add tries and hits. */

+         slapi_counter_add(t_cache_stats.cache_evicts, t_cache->evicts);

+         slapi_counter_add(t_cache_stats.cache_tries, t_cache->tries);

+         slapi_counter_add(t_cache_stats.cache_hits, t_cache->hits);

+         t_cache->hits = 0;

+         t_cache->tries = 0;

+         t_cache->evicts = 0;

+         /* Count and size need diff */

+         int64_t diff = (t_cache->size - t_cache->last_size);

+         if (diff > 0) {

+             // We have more ....

+             slapi_counter_add(t_cache_stats.cache_size, (uint64_t)diff);

+         } else if (diff < 0) {

+             slapi_counter_subtract(t_cache_stats.cache_size, (uint64_t)llabs(diff));

+         }

+         t_cache->last_size = t_cache->size;

+ 

+         diff = (t_cache->count - t_cache->last_count);

+         if (diff > 0) {

+             // We have more ....

+             slapi_counter_add(t_cache_stats.cache_count, (uint64_t)diff);

+         } else if (diff < 0) {

+             slapi_counter_subtract(t_cache_stats.cache_count, (uint64_t)llabs(diff));

+         }

+         t_cache->last_count = t_cache->count;

+ 

+     }

+ }

+ 

+ static void

+ ndn_thread_cache_value_destroy(struct ndn_cache *t_cache, struct ndn_cache_value *v) {

+     /* Update stats */

+     t_cache->size = t_cache->size - v->size;

+     t_cache->count--;

+     t_cache->evicts++;

+ 

+     if (v == t_cache->head) {

+         t_cache->head = v->prev;

+     }

+     if (v == t_cache->tail) {

+         t_cache->tail = v->next;

+     }

+ 

+     /* Cut the node out. */

+     if (v->next != NULL) {

+         v->next->prev = v->prev;

+     }

+     if (v->prev != NULL) {

+         v->prev->next = v->next;

+     }

+     /* Set the pointer in the table to NULL */

+     /* Now see if we were in a list */

+     struct ndn_cache_value *slot_node = t_cache->table[v->slot];

+     if (slot_node == v) {

+         t_cache->table[v->slot] = v->child;

+     } else {

+         struct ndn_cache_value *former_slot_node = NULL;

+         do {

+             former_slot_node = slot_node;

+             slot_node = slot_node->child;

+         } while(slot_node != v);

+         /* Okay, now slot_node is us, and former is our parent */

+         former_slot_node->child = v->child;

+     }

+ 

+     slapi_ch_free((void **)&(v->dn));

+     slapi_ch_free((void **)&(v->ndn));

+     slapi_ch_free((void **)&v);

+ }

+ 

+ static void

+ ndn_thread_cache_destroy(void *v_cache) {

+     struct ndn_cache *t_cache = (struct ndn_cache *)v_cache;

+     /*

+      * FREE ALL THE NODES!!!

+      */

+     struct ndn_cache_value *node = t_cache->tail;

+     struct ndn_cache_value *next_node = NULL;

+     while (node) {

+         next_node = node->next;

+         ndn_thread_cache_value_destroy(t_cache, node);

+         node = next_node;

+     }

+     slapi_ch_free((void **)&t_cache);

+ }

+ 

+ static void

+ ndn_cache_key_init() {

+     if (pthread_key_create(&ndn_cache_key, ndn_thread_cache_destroy) != 0) {

+         /* Log a scary warning? */

+         slapi_log_err(SLAPI_LOG_ERR, "ndn_cache_init", "Failed to create pthread key, aborting.\n");

      }

-     return hash;

  }

  

  void

  ndn_cache_init()

  {

-     if(!config_get_ndn_cache_enabled() || ndn_started){

+     ndn_enabled = config_get_ndn_cache_enabled();

+     if (ndn_enabled == 0) {

+         /*

+          * Don't configure the keys or anything, need a restart

+          * to enable. We'll just never use ndn cache in this

+          * run.

+          */

          return;

      }

-     ndn_cache_hashtable = PL_NewHashTable( NDN_CACHE_BUCKETS, ndn_hash_string, PL_CompareStrings, PL_CompareValues, 0, 0);

-     ndn_cache = (struct ndn_cache_ctx *)slapi_ch_malloc(sizeof(struct ndn_cache_ctx));

-     ndn_cache->cache_max_size = config_get_ndn_cache_size();

-     ndn_cache->cache_hits = slapi_counter_new();

-     ndn_cache->cache_tries = slapi_counter_new();

-     ndn_cache->cache_misses = slapi_counter_new();

-     ndn_cache->cache_count = 0;

-     ndn_cache->cache_size = sizeof(struct ndn_cache_ctx) + sizeof(PLHashTable) + sizeof(PLHashTable);

-     ndn_cache->head = NULL;

-     ndn_cache->tail = NULL;

-     ndn_started = 1;

-     if ( NULL == ( lru_lock = PR_NewLock()) ||  NULL == ( ndn_cache_lock = slapi_new_rwlock())) {

-         ndn_cache_destroy();

-         slapi_log_err(SLAPI_LOG_ERR, "ndn_cache_init", "Failed to create locks.  Disabling cache.\n" );

+ 

+     /* Create the pthread key */

+     (void)pthread_once(&ndn_cache_key_once, ndn_cache_key_init);

+ 

+     /* Create the global stats. */

+     t_cache_stats.max_size = config_get_ndn_cache_size();

+     t_cache_stats.cache_evicts = slapi_counter_new();

+     t_cache_stats.cache_tries = slapi_counter_new();

+     t_cache_stats.cache_hits = slapi_counter_new();

+     t_cache_stats.cache_count = slapi_counter_new();

+     t_cache_stats.cache_size = slapi_counter_new();

+     /* Get thread numbers and calc the per thread size */

+     int32_t maxthreads = (int32_t)config_get_threadnumber();

+     size_t tentative_size = t_cache_stats.max_size / maxthreads;

+     if (tentative_size < NDN_CACHE_MINIMUM_CAPACITY) {

+         tentative_size = NDN_CACHE_MINIMUM_CAPACITY;

+         t_cache_stats.max_size = NDN_CACHE_MINIMUM_CAPACITY * maxthreads;

+     }

+     t_cache_stats.thread_max_size = tentative_size;

+ 

+     /*

+      * Slots *must* be a power of two, even if the number of entries

+      * we store will be *less* than this.

+      */

+     size_t possible_elements = tentative_size / NDN_ENTRY_AVG_SIZE;

+     /*

+      * So this is like 1048576 / 168, so we get 6241. Now we need to

+      * shift this to get the number of bits.

+      */

+     size_t shifts = 0;

+     while (possible_elements > 0) {

+         shifts++;

+         possible_elements = possible_elements >> 1;

      }

+     /*

+      * So now we can use this to make the slot count.

+      */

+     t_cache_stats.slots = 1 << shifts;

+     /* Done? */

+     return;

  }

  

  void

  ndn_cache_destroy()

  {

-     if(!ndn_started){

+     if (ndn_enabled == 0) {

          return;

      }

-     if(lru_lock){

-         PR_DestroyLock(lru_lock);

-         lru_lock = NULL;

-     }

-     if(ndn_cache_lock){

-         slapi_destroy_rwlock(ndn_cache_lock);

-         ndn_cache_lock = NULL;

-     }

-     if(ndn_cache_hashtable){

-         ndn_cache_free();

-         PL_HashTableDestroy(ndn_cache_hashtable);

-         ndn_cache_hashtable = NULL;

-     }

-     config_set_ndn_cache_enabled(CONFIG_NDN_CACHE, "off", NULL, 1 );

-     slapi_counter_destroy(&ndn_cache->cache_hits);

-     slapi_counter_destroy(&ndn_cache->cache_tries);

-     slapi_counter_destroy(&ndn_cache->cache_misses);

-     slapi_ch_free((void **)&ndn_cache);

- 

-     ndn_started = 0;

+     slapi_counter_destroy(&(t_cache_stats.cache_tries));

+     slapi_counter_destroy(&(t_cache_stats.cache_hits));

+     slapi_counter_destroy(&(t_cache_stats.cache_count));

+     slapi_counter_destroy(&(t_cache_stats.cache_size));

+     slapi_counter_destroy(&(t_cache_stats.cache_evicts));

  }

  

  int

  ndn_cache_started()

  {

-     return ndn_started;

+     return ndn_enabled;

  }

  

  /*

   *  Look up this dn in the ndn cache

   */

  static int

- ndn_cache_lookup(char *dn, size_t dn_len, char **result, char **udn, int *rc)

+ ndn_cache_lookup(char *dn, size_t dn_len, char **ndn, char **udn, int *rc)

  {

-     struct ndn_hash_val *ndn_ht_val = NULL;

-     char *ndn, *key;

-     int rv = 0;

- 

-     if(NULL == udn){

-         return rv;

+     if (ndn_enabled == 0 || NULL == udn) {

+         return 0;

      }

      *udn = NULL;

-     if(ndn_started == 0){

-         return rv;

-     }

-     if(dn_len == 0){

-         *result = dn;

+ 

+     if (dn_len == 0) {

+         *ndn = dn;

          *rc = 0;

          return 1;

      }

-     slapi_counter_increment(ndn_cache->cache_tries);

-     slapi_rwlock_rdlock(ndn_cache_lock);

-     ndn_ht_val = (struct ndn_hash_val *)PL_HashTableLookupConst(ndn_cache_hashtable, dn);

-     if(ndn_ht_val){

-         ndn_cache_update_lru(&ndn_ht_val->lru_node);

-         slapi_counter_increment(ndn_cache->cache_hits);

-         if ((ndn_ht_val->len != dn_len) || 

-             /* even if the lengths match, dn may not be normalized yet.

-              * (e.g., 'cn="o=ABC",o=XYZ' vs. 'cn=o\3DABC,o=XYZ') */

-             (memcmp(dn, ndn_ht_val->ndn, dn_len))){

-             *rc = 1; /* free result */

-             ndn = slapi_ch_malloc(ndn_ht_val->len + 1);

-             memcpy(ndn, ndn_ht_val->ndn, ndn_ht_val->len);

-             ndn[ndn_ht_val->len] = '\0';

-             *result = ndn;

-         } else {

-             /* the dn was already normalized, just return the dn as the result */

-             *result = dn;

-             *rc = 0;

-         }

-         rv = 1;

-     } else {

-         /* copy/preserve the udn, so we can use it as the key when we add dn's to the hashtable */

-         key = slapi_ch_malloc(dn_len + 1);

-         memcpy(key, dn, dn_len);

-         key[dn_len] = '\0';

-         *udn = key;

+ 

+     struct ndn_cache *t_cache = pthread_getspecific(ndn_cache_key);

+     if (t_cache == NULL) {

+         t_cache = ndn_thread_cache_create(t_cache_stats.thread_max_size, t_cache_stats.slots);

+         pthread_setspecific(ndn_cache_key, t_cache);

+         /* If we have no cache, we can't look up ... */

+         return 0;

      }

-     slapi_rwlock_unlock(ndn_cache_lock);

  

-     return rv;

- }

+     t_cache->tries++;

  

- /*

-  *  Move this lru node to the top of the list

-  */

- static void

- ndn_cache_update_lru(struct ndn_cache_lru **node)

- {

-     struct ndn_cache_lru *prev, *next, *curr_node = *node;

+     /*

+      * Hash our DN ...

+      */

+     uint64_t dn_hash = sds_siphash13(dn, dn_len, t_cache->key);

+     /* Where should it be? */

+     size_t expect_slot = dn_hash % t_cache->slots;

  

-     if(curr_node == NULL){

-         return;

-     }

-     PR_Lock(lru_lock);

-     if(curr_node->prev == NULL){

-         /* already the top node */

-         PR_Unlock(lru_lock);

-         return;

-     }

-     prev = curr_node->prev;

-     next = curr_node->next;

-     if(next){

-         next->prev = prev;

-         prev->next = next;

-     } else {

-         /* this was the tail, so reset the tail */

-         ndn_cache->tail = prev;

-         prev->next = NULL;

+     /*

+      * Is it there?

+      */

+     if (t_cache->table[expect_slot] != NULL) {

+         /*

+          * Check it really matches, could be collision.

+          */

+         struct ndn_cache_value *node = t_cache->table[expect_slot];

+         while (node != NULL) {

+             if (strcmp(dn, node->dn) == 0) {

+                 /*

+                  * Update LRU

+                  * Are we already the tail? If so, we can just skip.

+                  * remember, this means in a set of 1, we will always be tail

+                  */

+                 if (t_cache->tail != node) {

+                     /*

+                      * Okay, we are *not* the tail. We could be anywhere between

+                      * tail -> ... -> x -> head

+                      * or even, we are the head ourself.

+                      */

+                     if (t_cache->head == node) {

+                         /* We are the head, update head to our predecessor */

+                         t_cache->head = node->prev;

+                         /* Remember, the head has no next. */

+                         t_cache->head->next = NULL;

+                     } else {

+                         /* Right, we aren't the head, so we have a next node. */

+                         node->next->prev = node->prev;

+                     }

+                     /* Because we must be in the middle somewhere, we can assume next and prev exist. */

+                     node->prev->next = node->next;

+                     /*

+                      * Tail can't be NULL if we have a value in the cache, so we can

+                      * just deref this.

+                      */

+                     node->next = t_cache->tail;

+                     t_cache->tail->prev = node;

+                     t_cache->tail = node;

+                     node->prev = NULL;

+                 }

+                 /* Update that we have a hit.*/

+                 t_cache->hits++;

+                 /* Cope the NDN to the caller. */

+                 *ndn = slapi_ch_strdup(node->ndn);

+                 /* Indicate to the caller to free this. */

+                 *rc = 1;

+                 ndn_thread_cache_commit_status(t_cache);

+                 return 1;

+             }

+             node = node->child;

+         }

      }

-     curr_node->prev = NULL;

-     curr_node->next = ndn_cache->head;

-     ndn_cache->head->prev = curr_node;

-     ndn_cache->head = curr_node;

-     PR_Unlock(lru_lock);

+     /* If we miss, we need to duplicate dn to udn here. */

+     *udn = slapi_ch_strdup(dn);

+     *rc = 0;

+     ndn_thread_cache_commit_status(t_cache);

+     return 0;

  }

  

  /*
@@ -2938,176 +3235,102 @@

  static void

  ndn_cache_add(char *dn, size_t dn_len, char *ndn, size_t ndn_len)

  {

-     struct ndn_hash_val *ht_entry;

-     struct ndn_cache_lru *new_node = NULL;

-     PLHashEntry *he;

-     int size;

- 

-     if(ndn_started == 0 || dn_len == 0){

+     if (ndn_enabled == 0) {

          return;

      }

-     if(strlen(ndn) > ndn_len){

+     if (dn_len == 0) {

+         return;

+     }

+     if (strlen(ndn) > ndn_len) {

          /* we need to null terminate the ndn */

          *(ndn + ndn_len) = '\0';

      }

      /*

       *  Calculate the approximate memory footprint of the hash entry, key, and lru entry.

       */

-     size = (dn_len * 2) + ndn_len + sizeof(PLHashEntry) + sizeof(struct ndn_hash_val) + sizeof(struct ndn_cache_lru);

+     struct ndn_cache_value *new_value = (struct ndn_cache_value *)slapi_ch_calloc(1, sizeof(struct ndn_cache_value));

+     new_value->size = sizeof(struct ndn_cache_value) + dn_len + ndn_len;

+     /* DN is alloc for us */

+     new_value->dn = dn;

+     /* But we need to copy ndn */

+     new_value->ndn = slapi_ch_strdup(ndn);

+ 

      /*

-      *  Create our LRU node

+      * Get our local cache out.

       */

-     new_node = (struct ndn_cache_lru *)slapi_ch_malloc(sizeof(struct ndn_cache_lru));

-     if(new_node == NULL){

-         slapi_log_err(SLAPI_LOG_ERR, "ndn_cache_add", "Failed to allocate new lru node.\n");

-         return;

+     struct ndn_cache *t_cache = pthread_getspecific(ndn_cache_key);

+     if (t_cache == NULL) {

+         t_cache = ndn_thread_cache_create(t_cache_stats.thread_max_size, t_cache_stats.slots);

+         pthread_setspecific(ndn_cache_key, t_cache);

      }

-     new_node->prev = NULL;

-     new_node->key = dn; /* dn has already been allocated */

      /*

-      *  Its possible this dn was added to the hash by another thread.

+      * Hash the DN

       */

-     slapi_rwlock_wrlock(ndn_cache_lock);

-     ht_entry = (struct ndn_hash_val *)PL_HashTableLookupConst(ndn_cache_hashtable, dn);

-     if(ht_entry){

-         /* already exists, free the node and return */

-         slapi_rwlock_unlock(ndn_cache_lock);

-         slapi_ch_free_string(&new_node->key);

-         slapi_ch_free((void **)&new_node);

-         return;

-     }

+     uint64_t dn_hash = sds_siphash13(new_value->dn, dn_len, t_cache->key);

      /*

-      *  Create the hash entry

+      * Get the insert slot: This works because the number spaces of dn_hash is

+      * a 64bit int, and slots is a power of two. As a result, we end up with

+      * even distribution of the values.

       */

-     ht_entry = (struct ndn_hash_val *)slapi_ch_malloc(sizeof(struct ndn_hash_val));

-     if(ht_entry == NULL){

-         slapi_rwlock_unlock(ndn_cache_lock);

-         slapi_log_err(SLAPI_LOG_ERR, "ndn_cache_add", "Failed to allocate new hash entry.\n");

-         slapi_ch_free_string(&new_node->key);

-         slapi_ch_free((void **)&new_node);

-         return;

-     }

-     ht_entry->ndn = slapi_ch_malloc(ndn_len + 1);

-     memcpy(ht_entry->ndn, ndn, ndn_len);

-     ht_entry->ndn[ndn_len] = '\0';

-     ht_entry->len = ndn_len;

-     ht_entry->size = size;

-     ht_entry->lru_node = new_node;

+     size_t insert_slot = dn_hash % t_cache->slots;

+     /* Track this for free */

+     new_value->slot = insert_slot;

+ 

      /*

-      *  Check if our cache is full

+      * Okay, check if we have space, else we need to trim nodes from

+      * the LRU

       */

-     PR_Lock(lru_lock); /* grab the lru lock now, as ndn_cache_flush needs it */

-     if(ndn_cache->cache_max_size != 0 && ((ndn_cache->cache_size + size) > ndn_cache->cache_max_size)){

-         ndn_cache_flush();

+     while (t_cache->head && (t_cache->size + new_value->size) > t_cache->max_size) {

+         struct ndn_cache_value *trim_node = t_cache->head;

+         ndn_thread_cache_value_destroy(t_cache, trim_node);

      }

+ 

      /*

-      * Set the ndn cache lru nodes

+      * Add it!

       */

-     if(ndn_cache->head == NULL && ndn_cache->tail == NULL){

-         /* this is the first node */

-         ndn_cache->head = new_node;

-         ndn_cache->tail = new_node;

-         new_node->next = NULL;

+     if (t_cache->table[insert_slot] == NULL) {

+         t_cache->table[insert_slot] = new_value;

      } else {

-         new_node->next = ndn_cache->head;

-         if(ndn_cache->head)

-             ndn_cache->head->prev = new_node;

+         /*

+          * Hash collision! We need to replace the bucket then ....

+          * insert at the head of the slot to make this simpler.

+          */

+         new_value->child = t_cache->table[insert_slot];

+         t_cache->table[insert_slot] = new_value;

      }

-     ndn_cache->head = new_node;

-     PR_Unlock(lru_lock);

+ 

      /*

-      *  Add the new object to the hashtable, and update our stats

+      * Finally, stick this onto the tail because it's the newest.

       */

-     he = PL_HashTableAdd(ndn_cache_hashtable, new_node->key, (void *)ht_entry);

-     if(he == NULL){

-         slapi_log_err(SLAPI_LOG_ERR, "ndn_cache_add", "Failed to add new entry to hash(%s)\n",dn);

-     } else {

-         ndn_cache->cache_count++;

-         ndn_cache->cache_size += size;

+     if (t_cache->head == NULL) {

+         t_cache->head = new_value;

      }

-     slapi_rwlock_unlock(ndn_cache_lock);

- }

- 

- /*

-  *  cache is full, remove the least used dn's.  lru_lock/ndn_cache write lock are already taken

-  */

- static void

- ndn_cache_flush(void)

- {

-     struct ndn_cache_lru *node, *next, *flush_node;

-     int i;

- 

-     node = ndn_cache->tail;

-     for(i = 0; node && i < NDN_FLUSH_COUNT && ndn_cache->cache_count > NDN_MIN_COUNT; i++){

-         flush_node = node;

-         /* update the lru */

-         next = node->prev;

-         next->next = NULL;

-         ndn_cache->tail = next;

-         node = next;

-         /* now update the hash */

-         ndn_cache->cache_count--;

-         ndn_cache_delete(flush_node->key);

-         slapi_ch_free_string(&flush_node->key);

-         slapi_ch_free((void **)&flush_node);

+     if (t_cache->tail != NULL) {

+         new_value->next = t_cache->tail;

+         t_cache->tail->prev = new_value;

      }

+     t_cache->tail = new_value;

  

-     slapi_log_err(SLAPI_LOG_CACHE, "ndn_cache_flush","Flushed cache.\n");

- }

- 

- static void

- ndn_cache_free(void)

- {

-     struct ndn_cache_lru *node, *next, *flush_node;

- 

-     if(!ndn_cache){

-         return;

-     }

- 

-     node = ndn_cache->tail;

-     while(node && ndn_cache->cache_count){

-         flush_node = node;

-         /* update the lru */

-         next = node->prev;

-         if(next){

-             next->next = NULL;

-         }

-         ndn_cache->tail = next;

-         node = next;

-         /* now update the hash */

-         ndn_cache->cache_count--;

-         ndn_cache_delete(flush_node->key);

-         slapi_ch_free_string(&flush_node->key);

-         slapi_ch_free((void **)&flush_node);

-     }

- }

- 

- /* this is already "write" locked from ndn_cache_add */

- static void

- ndn_cache_delete(char *dn)

- {

-     struct ndn_hash_val *ht_entry;

+     /*

+      * And update the stats.

+      */

+     t_cache->size = t_cache->size + new_value->size;

+     t_cache->count++;

  

-     ht_entry = (struct ndn_hash_val *)PL_HashTableLookupConst(ndn_cache_hashtable, dn);

-     if(ht_entry){

-         ndn_cache->cache_size -= ht_entry->size;

-         slapi_ch_free_string(&ht_entry->ndn);

-         slapi_ch_free((void **)&ht_entry);

-         PL_HashTableRemove(ndn_cache_hashtable, dn);

-     }

  }

  

  /* stats for monitor */

  void

- ndn_cache_get_stats(PRUint64 *hits, PRUint64 *tries, size_t *size, size_t *max_size, long *count)

- {

-     slapi_rwlock_rdlock(ndn_cache_lock);

-     *hits = slapi_counter_get_value(ndn_cache->cache_hits);

-     *tries = slapi_counter_get_value(ndn_cache->cache_tries);

-     *size = ndn_cache->cache_size;

-     *max_size = ndn_cache->cache_max_size;

-     *count = ndn_cache->cache_count;

-     slapi_rwlock_unlock(ndn_cache_lock);

+ ndn_cache_get_stats(PRUint64 *hits, PRUint64 *tries, size_t *size, size_t *max_size, size_t *thread_size, size_t *evicts, size_t *slots, long *count)

+ {

+     *max_size = t_cache_stats.max_size;

+     *thread_size = t_cache_stats.thread_max_size;

+     *slots = t_cache_stats.slots;

+     *evicts = slapi_counter_get_value(t_cache_stats.cache_evicts);

+     *hits = slapi_counter_get_value(t_cache_stats.cache_hits);

+     *tries = slapi_counter_get_value(t_cache_stats.cache_tries);

+     *size = slapi_counter_get_value(t_cache_stats.cache_size);

+     *count = slapi_counter_get_value(t_cache_stats.cache_count);

  }

  

  /* Common ancestor sdn is allocated.

file modified
+71 -37
@@ -40,6 +40,8 @@

  #include "slap.h"

  #include <pwd.h>

  

+ #include <unistd.h> /* provides fsync/close */

+ 

  /* #define SLAPI_DSE_DEBUG */ 	/* define this to force trace log	*/

  								/* messages to always be logged		*/

  
@@ -72,11 +74,11 @@

  struct dse_callback

  {

      int operation;

- 	int flags;

- 	Slapi_DN *base;

- 	int scope;

- 	char *filter;				/* NULL means match all entries */

-     Slapi_Filter *slapifilter;	/* NULL means match all entries */

+     int flags;

+     Slapi_DN *base;

+     int scope;

+     char *filter;               /* NULL means match all entries */

+     Slapi_Filter *slapifilter;  /* NULL means match all entries */

      int (*fn)(Slapi_PBlock *,Slapi_Entry *,Slapi_Entry *,int*,char*,void *);

      void *fn_arg;

      struct slapdplugin *plugin;
@@ -89,13 +91,14 @@

      char *dse_tmpfile; /* and written to when changes are made via LDAP */

      char *dse_fileback; /* contain the latest info, just before a new change */

      char *dse_filestartOK; /* contain the latest info with which the server has successfully started */

+     char *dse_configdir; /* The location of config files - allows us to fsync the dir post rename */

      Avlnode *dse_tree;

      struct dse_callback *dse_callback;

      Slapi_RWLock *dse_rwlock; /* a read-write lock to protect the whole dse backend */

- 	char **dse_filelist; /* these are additional read only files used to */

- 						 /* initialize the dse */

- 	int  dse_is_updateable; /* if non-zero, this DSE can be written to */

- 	int  dse_readonly_error_reported; /* used to ensure that read-only errors are logged only once */

+     char **dse_filelist; /* these are additional read only files used to */

+                          /* initialize the dse */

+     int  dse_is_updateable; /* if non-zero, this DSE can be written to */

+     int  dse_readonly_error_reported; /* used to ensure that read-only errors are logged only once */

  };

  

  struct dse_node
@@ -361,37 +364,39 @@

  			if (!strstr(filename, realconfigdir))

  			{

  				pdse->dse_filename = slapi_ch_smprintf("%s/%s", realconfigdir, filename );

- 			}

- 			else

+ 			} else {

  				pdse->dse_filename = slapi_ch_strdup(filename);

+ 			}

  

  			if (!strstr(tmpfilename, realconfigdir)) {

  				pdse->dse_tmpfile = slapi_ch_smprintf("%s/%s", realconfigdir, tmpfilename );

- 			}

- 			else

+ 			} else {

  				pdse->dse_tmpfile = slapi_ch_strdup(tmpfilename);

+ 			}

+ 

+ 			pdse->dse_configdir = slapi_ch_strdup(realconfigdir);

  

  			if ( backfilename != NULL )

  			{

  				if (!strstr(backfilename, realconfigdir)) {

  					pdse->dse_fileback = slapi_ch_smprintf("%s/%s", realconfigdir, backfilename );

- 				}

- 				else

+ 				} else {

  					pdse->dse_fileback = slapi_ch_strdup(backfilename);

- 			}

- 			else

+ 				}

+ 			} else {

  				pdse->dse_fileback = NULL;

+             }

  

  			if ( startokfilename != NULL )

  			{

  				if (!strstr(startokfilename, realconfigdir)) {

  					pdse->dse_filestartOK = slapi_ch_smprintf("%s/%s", realconfigdir, startokfilename );

- 				}

- 				else

+ 				} else {

  					pdse->dse_filestartOK = slapi_ch_strdup(startokfilename);

- 			}

- 			else

+ 				}

+ 			} else {

  				pdse->dse_filestartOK = NULL;

+ 			}

  

  			pdse->dse_tree= NULL;

  			pdse->dse_callback= NULL;
@@ -440,6 +445,7 @@

      slapi_ch_free((void **)&(pdse->dse_tmpfile));

      slapi_ch_free((void **)&(pdse->dse_fileback));

      slapi_ch_free((void **)&(pdse->dse_filestartOK));

+     slapi_ch_free((void **)&(pdse->dse_configdir));

      dse_callback_deletelist(&pdse->dse_callback);

      charray_free(pdse->dse_filelist);

      nentries = avl_free(pdse->dse_tree, dse_internal_delete_entry);
@@ -643,31 +649,50 @@

      int rc= 0; /* Fail */

      PRFileInfo64 prfinfo;

  

-     if (PR_GetFileInfo64( filename, &prfinfo ) == PR_SUCCESS) {

-         if ( prfinfo.size > 0) {

-             return (1);

+     if (PR_GetFileInfo64(filename, &prfinfo) == PR_SUCCESS) {

+         if (prfinfo.size > 0) {

+             /* File exists and has content. */

+             return 1;

          } else {

-             rc = PR_Delete (filename);

+             slapi_log_err(SLAPI_LOG_INFO, "dse_check_file",

+                           "The config %s has zero length. Attempting restore ... \n", filename);

+             rc = PR_Delete(filename);

          }

+     } else {

+         slapi_log_err(SLAPI_LOG_INFO, "dse_check_file",

+                       "The config %s can not be accessed. Attempting restore ... (reason: %d)\n", filename, rc);

      }

  

      if (backupname) {

-         rc = PR_Rename (backupname, filename);

-     } else {

-         return (0);

-     }

+         if (PR_GetFileInfo64(backupname, &prfinfo) != PR_SUCCESS) {

+             slapi_log_err(SLAPI_LOG_INFO, "dse_check_file",

+                           "The backup %s can not be accessed. Check it exists and permissions.\n", backupname);

+             return 0;

+         }

+ 

+         if (prfinfo.size <= 0) {

+             slapi_log_err(SLAPI_LOG_ERR, "dse_check_file",

+                       "The backup file %s has zero length, refusing to restore it.\n", backupname);

+             return 0;

+         }

+ 

+         rc = PR_Rename(backupname, filename);

+         if (rc != PR_SUCCESS) {

+             slapi_log_err(SLAPI_LOG_INFO, "dse_check_file",

+                       "The configuration file %s was NOT able to be restored from %s, error %d\n", filename, backupname, rc);

+             return 0;

+         }

  

-     if ( PR_GetFileInfo64( filename, &prfinfo ) == PR_SUCCESS && prfinfo.size > 0 ) {

          slapi_log_err(SLAPI_LOG_INFO, "dse_check_file",

-             "The configuration file %s was restored from backup %s\n", filename, backupname);

-         return (1);

+                   "The configuration file %s was restored from backup %s\n", filename, backupname);

+         return 1;

+ 

      } else {

-         slapi_log_err(SLAPI_LOG_ERR, "dse_check_file",

-             "The configuration file %s was not restored from backup %s, error %d\n",

-              filename, backupname, rc);

-         return (0);

+         slapi_log_err(SLAPI_LOG_INFO, "dse_check_file", "No backup filename provided.\n");

+         return 0;

      }

  }

+ 

  static int

  dse_read_one_file(struct dse *pdse, const char *filename, Slapi_PBlock *pb,

          int primary_file )
@@ -991,8 +1016,9 @@

      FPWrapper	fpw;

      int rc = 0;

  

- 	if (dont_ever_write_dse_files)

+ 	if (dont_ever_write_dse_files) {

  		return rc;

+ 	}

  

      fpw.fpw_rc = 0;

  	fpw.fpw_prfd = NULL;
@@ -1042,6 +1068,14 @@

  							pdse->dse_tmpfile, pdse->dse_filename,

  							rc, slapd_system_strerror( rc ));

                  }

+ 				/*

+ 				 * We have now written to the tmp location, and renamed it

+ 				 * we need to open and fsync the dir to make the rename stick.

+ 				 */

+ 				int fp_configdir = open(pdse->dse_configdir, O_PATH | O_DIRECTORY);

+ 				fsync(fp_configdir);

+ 				close(fp_configdir);

+ 

              }

          }

  		if (fpw.fpw_prfd)

@@ -1878,6 +1878,16 @@

  		be_addsuffix(be,&monitor);

  		be_addsuffix(be,&config);

  

+         /*

+          * Now that the be's are in place, we can

+          * setup the mapping tree.

+          */

+ 

+         if (mapping_tree_init()) {

+             slapi_log_err(SLAPI_LOG_EMERG, "setup_internal_backends", "Failed to init mapping tree\n");

+             exit(1);

+         }

+ 

  		add_internal_entries();

  

  		add_easter_egg_entry();

file modified
+5 -5
@@ -486,7 +486,7 @@

  			f->f_sub_initial = val;

  			eval = (char*)slapi_escape_filter_value(val, -1);

  			if(eval) {

- 				if (fstr_len < strlen(*fstr) + strlen(eval) + 1) {

+ 				if (fstr_len <= strlen(*fstr) + strlen(eval) + 1) {

  				    fstr_len += (strlen(eval) + 1) * 2;

  				    *fstr = slapi_ch_realloc(*fstr, fstr_len);

  				}
@@ -500,7 +500,7 @@

  			charray_add(&f->f_sub_any, val);

  			eval = (char*)slapi_escape_filter_value(val, -1);

  			if(eval){

- 				if (fstr_len < strlen(*fstr) + strlen(eval) + 1) {

+ 				if (fstr_len <= strlen(*fstr) + strlen(eval) + 1) {

  				    fstr_len += (strlen(eval) + 1) * 2;

  				    *fstr = slapi_ch_realloc(*fstr, fstr_len);

  				}
@@ -518,7 +518,7 @@

  			f->f_sub_final = val;

  			eval = (char*)slapi_escape_filter_value( val, -1);

  			if(eval){

- 				if (fstr_len < strlen(*fstr) + strlen(eval) + 1) {

+ 				if (fstr_len <= strlen(*fstr) + strlen(eval) + 1) {

  				    fstr_len += (strlen(eval) + 1) * 2;

  				    *fstr = slapi_ch_realloc(*fstr, fstr_len);

  				}
@@ -544,7 +544,7 @@

  	}

  

  	filter_compute_hash(f);

- 	if (fstr_len < strlen(*fstr) + 3) {

+ 	if (fstr_len <= strlen(*fstr) + 3) {

  		fstr_len += 3;

  		*fstr = slapi_ch_realloc(*fstr, fstr_len);

  	}
@@ -783,7 +783,7 @@

  		break;

  

  	default:

- 		slapi_log_err(SLAPI_LOG_ERR, "slapi_filter_free", "nknown type 0x%lX\n",

+ 		slapi_log_err(SLAPI_LOG_ERR, "slapi_filter_free", "Unknown type 0x%lX\n",

  		    f->f_choice);

  		break;

  	}

file modified
+54 -15
@@ -7091,9 +7091,30 @@

      return retVal;

  }

  

+ char **

+ config_get_allowed_sasl_mechs_array(void)

+ {

+     /*

+      * array of mechs. If is null, returns NULL thanks to ch_array_dup.

+      * Caller must free!

+      */

+     char **retVal;

+     slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();

+ 

+     CFG_LOCK_READ(slapdFrontendConfig);

+     retVal = slapi_ch_array_dup(slapdFrontendConfig->allowed_sasl_mechs_array);

+     CFG_UNLOCK_READ(slapdFrontendConfig);

+ 

+     return retVal;

+ }

+ 

  char *

- config_get_allowed_sasl_mechs()

+ config_get_allowed_sasl_mechs(void)

  {

+     /*

+      * Space seperated list of allowed mechs

+      * if this is NULL, means *all* mechs are allowed!

+      */

      char *retVal;

      slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();

  
@@ -7114,22 +7135,38 @@

          return LDAP_SUCCESS;

      }

  

-     /* cyrus sasl doesn't like comma separated lists */

-     remove_commas(value);

- 

-     if(invalid_sasl_mech(value)){

-         slapi_log_err(SLAPI_LOG_ERR,"config_set_allowed_sasl_mechs",

-                 "Invalid value/character for sasl mechanism (%s).  Use ASCII "

-                 "characters, upto 20 characters, that are upper-case letters, "

-                 "digits, hyphens, or underscores\n", value);

-         return LDAP_UNWILLING_TO_PERFORM;

+     /* During a reset, the value is "", so we have to handle this case. */

+     if (strcmp(value, "") != 0) {

+         char *nval = slapi_ch_strdup(value);

+ 

+         /* cyrus sasl doesn't like comma separated lists */

+         remove_commas(nval);

+ 

+         if (invalid_sasl_mech(nval)) {

+             slapi_log_err(SLAPI_LOG_ERR, "config_set_allowed_sasl_mechs",

+                           "Invalid value/character for sasl mechanism (%s).  Use ASCII "

+                           "characters, upto 20 characters, that are upper-case letters, "

+                           "digits, hyphens, or underscores\n",

+                           nval);

+             slapi_ch_free_string(&nval);

+             return LDAP_UNWILLING_TO_PERFORM;

+         }

+         CFG_LOCK_WRITE(slapdFrontendConfig);

+         slapi_ch_free_string(&slapdFrontendConfig->allowed_sasl_mechs);

+         slapi_ch_array_free(slapdFrontendConfig->allowed_sasl_mechs_array);

+         slapdFrontendConfig->allowed_sasl_mechs = nval;

+         slapdFrontendConfig->allowed_sasl_mechs_array = slapi_str2charray_ext(nval, " ", 0);

+         CFG_UNLOCK_WRITE(slapdFrontendConfig);

+     } else {

+         /* If this value is "", we need to set the list to *all* possible mechs */

+         CFG_LOCK_WRITE(slapdFrontendConfig);

+         slapi_ch_free_string(&slapdFrontendConfig->allowed_sasl_mechs);

+         slapi_ch_array_free(slapdFrontendConfig->allowed_sasl_mechs_array);

+         slapdFrontendConfig->allowed_sasl_mechs = NULL;

+         slapdFrontendConfig->allowed_sasl_mechs_array = NULL;

+         CFG_UNLOCK_WRITE(slapdFrontendConfig);

      }

  

-     CFG_LOCK_WRITE(slapdFrontendConfig);

-     slapi_ch_free_string(&slapdFrontendConfig->allowed_sasl_mechs);

-     slapdFrontendConfig->allowed_sasl_mechs = slapi_ch_strdup(value);

-     CFG_UNLOCK_WRITE(slapdFrontendConfig);

- 

      return LDAP_SUCCESS;

  }

  
@@ -7701,6 +7738,8 @@

                   * but as its real value.

                   */

                  ival = LDAP_DEBUG_ANY;

+             } else {

+                 ival = *(int *)value;

              }

              slapi_entry_attr_set_int(e, cgas->attr_name, ival);

          }

file modified
+5 -10
@@ -1026,16 +1026,6 @@

  

  		ps_init_psearch_system();   /* must come before plugin_startall() */

  

- 		/* Initailize the mapping tree */

- 

- 		if (mapping_tree_init())

- 		{

- 			slapi_log_err(SLAPI_LOG_EMERG, "main", "Failed to init mapping tree\n");

- 			return_value = 1;

- 			goto cleanup;

- 		}

- 

- 

  		/* initialize UniqueID generator - must be done once backends are started

  		   and event queue is initialized but before plugins are started */

  		/* Note: This DN is no need to be normalized. */
@@ -1157,6 +1147,11 @@

  #if defined( hpux )

  	exit( return_value );

  #else

+ 	/*

+ 	 * Server has stopped, lets force everything to disk: logs

+ 	 * db, dse.ldif, all of it.

+ 	 */

+ 	sync();

  	return return_value;

  #endif

  }

@@ -88,13 +88,13 @@

   *      release backend lock 

   *

   */

- static Slapi_RWLock    *myLock;    /* global lock on the mapping tree structures */

+ static Slapi_RWLock *myLock = NULL; /* global lock on the mapping tree structures */

  

  

  static mapping_tree_node *mapping_tree_root = NULL;

- static int mapping_tree_inited = 0;

- static int mapping_tree_freed = 0;

- static int extension_type = -1;    /* type returned from the factory */

+ static int32_t mapping_tree_inited = 0;

+ static int32_t mapping_tree_freed = 0;

+ static int extension_type = -1; /* type returned from the factory */

  

  /* The different states a mapping tree node can be in. */

  #define    MTN_DISABLED                 0    /* The server acts like the node isn't there. */
@@ -1674,22 +1674,24 @@

  {

      Slapi_DN *dn;

      mapping_tree_node *node;

-     backend ** be_list = (backend **) slapi_ch_malloc(sizeof(backend *));

+     backend **be_list = (backend **)slapi_ch_malloc(sizeof(backend *));

+     int *be_states = (int *)slapi_ch_malloc(sizeof(int));

  

      be_list[0] = be;

+     be_states[0] = SLAPI_BE_STATE_ON;

  

      dn = slapi_sdn_new_dn_byval(subtree);

-     node= mapping_tree_node_new(

-             dn,

-             be_list,

-             NULL, /* backend_name */

-             NULL,

-             1,    /* number of backends at this node */

-             1,    /* size of backend list structure */

-             NULL, /* referral */

-             parent,

-             MTN_BACKEND,

-             1, /* The config  node is a private node.

+     node = mapping_tree_node_new(

+         dn,

+         be_list,

+         NULL, /* backend_name */

+         be_states, /* be state */

+         1,    /* number of backends at this node */

+         1,    /* size of backend list structure */

+         NULL, /* referral */

+         parent,

+         MTN_BACKEND,

+         1,                    /* The config  node is a private node.

                  *  People can't see or change it. */

              NULL, NULL, NULL, 0); /* no distribution */

      return node;
@@ -1737,17 +1739,20 @@

      

      /* we call this function from a single thread, so it should be ok */

  

-     if(mapping_tree_freed){

-     /* shutdown has been detected */

-       return 0;

-     }

- 

-     if (mapping_tree_inited)

+     if (__atomic_load_4(&mapping_tree_freed, __ATOMIC_RELAXED)) {

+         /* shutdown has been detected */

          return 0;

+     }

  

-     /* ONREPL - I have moved this up because otherwise we can endup calling this 

+     /* ONREPL - I have moved this up because otherwise we can endup calling this

       * function recursively */

+     if (myLock != NULL) {

+         return 0;

+     }

+     myLock = slapi_new_rwlock();

+     slapi_rwlock_wrlock(myLock);

  

+     /* Should be fenced by the rwlock. */

      mapping_tree_inited = 1;

  

      slapi_register_supported_control(MTN_CONTROL_USE_ONE_BACKEND_OID,
@@ -1755,10 +1760,8 @@

      slapi_register_supported_control(MTN_CONTROL_USE_ONE_BACKEND_EXT_OID,

                       SLAPI_OPERATION_SEARCH);

  

-     myLock = slapi_new_rwlock();

- 

-     be= slapi_be_select_by_instance_name(DSE_BACKEND);

-     mapping_tree_root= add_internal_mapping_tree_node("", be, NULL);

+     be = slapi_be_select_by_instance_name(DSE_BACKEND);

+     mapping_tree_root = add_internal_mapping_tree_node("", be, NULL);

  

      /* We also need to add the config and schema backends to the mapping tree.

       * They are special in that users will not know about it's node in the 
@@ -1772,17 +1775,23 @@

      node= add_internal_mapping_tree_node("cn=schema", be, mapping_tree_root);

      mapping_tree_node_add_child(mapping_tree_root, node);

  

-     /* 

+     slapi_rwlock_unlock(myLock);

+ 

+     /*

       * Now we need to look under cn=mapping tree, cn=config to find the rest

       * of the mapping tree entries.

       * Builds the mapping tree from entries in the DIT.  This function just

       * calls mapping_tree_node_get_children with the special case for the

       * root node.

       */

-     if (mapping_tree_node_get_children(mapping_tree_root, 1))

+ 

+     if (mapping_tree_node_get_children(mapping_tree_root, 1)) {

          return -1;

+     }

  

+     slapi_rwlock_wrlock(myLock);

      mtn_create_extension(mapping_tree_root);

+     slapi_rwlock_unlock(myLock);

  

      /* setup the dse callback functions for the ldbm instance config entry */

      {
@@ -1855,8 +1864,8 @@

       */ 

      slapi_unregister_backend_state_change_all();

      /* recursively free tree nodes */

-     mtn_free_node (&mapping_tree_root);

-     mapping_tree_freed = 1;

+     mtn_free_node(&mapping_tree_root);

+     __atomic_store_4(&mapping_tree_freed, 1, __ATOMIC_RELAXED);

  }

  

  /* This function returns the first node to parse when a search is done 
@@ -2098,14 +2107,12 @@

      mapping_tree_node *target_node = NULL;

      int ret = 0;

  

-     if(mapping_tree_freed){

+     if (__atomic_load_4(&mapping_tree_freed, __ATOMIC_RELAXED)) {

          /* shutdown detected */

          goto done;

      }

  

-     if(!mapping_tree_inited) {

-         mapping_tree_init();

-     }

+     PR_ASSERT(mapping_tree_inited == 1);

  

      if (target_sdn) {

          mtn_lock();
@@ -2172,8 +2179,8 @@

      int fixup = 0;

      

  

-     if(mapping_tree_freed){

-         /* shutdown detected */ 

+     if (__atomic_load_4(&mapping_tree_freed, __ATOMIC_RELAXED)) {

+         /* shutdown detected */

          return LDAP_OPERATIONS_ERROR;

      }

  
@@ -2190,9 +2197,7 @@

      target_sdn = operation_get_target_spec (op);

      fixup = operation_is_flag_set(op, OP_FLAG_TOMBSTONE_FIXUP);

  

-     if(!mapping_tree_inited) {

-         mapping_tree_init();

-     } 

+     PR_ASSERT(mapping_tree_inited == 1);

  

      be[0] = NULL;

      if (referral) {
@@ -2203,8 +2208,9 @@

  

      /* Get the mapping tree node that is the best match for the target dn. */

      target_node = slapi_get_mapping_tree_node_by_dn(target_sdn);

-     if (target_node == NULL)

+     if (target_node == NULL) {

          target_node = mapping_tree_root;

+     }

  

      /* The processing of the base scope root DSE search and all other LDAP operations on "" 

       *  will be transferred to the internal DSE backend 
@@ -2281,8 +2287,8 @@

      Slapi_DN *sdn = NULL;

      int flag_partial_result = 0;

      int op_type;

-     

-     if(mapping_tree_freed){

+ 

+     if (__atomic_load_4(&mapping_tree_freed, __ATOMIC_RELAXED)) {

          return LDAP_OPERATIONS_ERROR;

      }

  
@@ -2302,9 +2308,7 @@

      slapi_pblock_get(pb, SLAPI_OPERATION_TYPE, &op_type);

      slapi_pblock_get(pb, SLAPI_SEARCH_SCOPE, &scope);

  

-     if(!mapping_tree_inited){

-         mapping_tree_init();

-     } 

+     PR_ASSERT(mapping_tree_inited == 1);

  

      mtn_lock();

  
@@ -2463,8 +2467,8 @@

      Slapi_Operation *op;

      int ret;

      int need_unlock = 0;

-     

-     if(mapping_tree_freed){

+ 

+     if (__atomic_load_4(&mapping_tree_freed, __ATOMIC_RELAXED)) {

          return LDAP_OPERATIONS_ERROR;

       }

  
@@ -2650,7 +2654,7 @@

      int flag_stop = 0;

      struct slapi_componentid *cid = NULL;

  

-     if(mapping_tree_freed){

+     if (__atomic_load_4(&mapping_tree_freed, __ATOMIC_RELAXED)) {

          /* shut down detected */

          return LDAP_OPERATIONS_ERROR; 

      }
@@ -2734,21 +2738,22 @@

                      } else {

                          /* This MTN has not been linked to its backend

                           * instance yet. */

-                         target_node->mtn_be[*index] =

-                             slapi_be_select_by_instance_name(

-                                 target_node->mtn_backend_names[*index]);

-                         *be = target_node->mtn_be[*index];

-                         if(*be==NULL) {

-                             slapi_log_err(SLAPI_LOG_BACKLDBM, "mtn_get_be",

-                                 "Warning: Mapping tree node entry for %s "

-                                 "point to an unknown backend : %s\n",

-                                 slapi_sdn_get_dn(target_node->mtn_subtree),

-                                 target_node->mtn_backend_names[*index]);

-                             /* Well there's still not backend instance for

-                              * this MTN, so let's have the default backend

-                              * deal with this.

-                              */

-                             *be = defbackend_get_backend();

+                         /* WARNING: internal memory dse backends don't provide NAMES */

+                         if (target_node->mtn_backend_names != NULL) {

+                             target_node->mtn_be[*index] = slapi_be_select_by_instance_name(target_node->mtn_backend_names[*index]);

+                             *be = target_node->mtn_be[*index];

+                             if (*be == NULL) {

+                                 slapi_log_err(SLAPI_LOG_BACKLDBM, "mtn_get_be",

+                                               "Warning: Mapping tree node entry for %s "

+                                               "point to an unknown backend : %s\n",

+                                               slapi_sdn_get_dn(target_node->mtn_subtree),

+                                               target_node->mtn_backend_names[*index]);

+                                 /* Well there's still not backend instance for

+                                  * this MTN, so let's have the default backend

+                                  * deal with this.

+                                  */

+                                 *be = defbackend_get_backend();

+                             }

                          }

                      }

                  }
@@ -2760,10 +2765,11 @@

              result = LDAP_OPERATIONS_ERROR;

                      *be = defbackend_get_backend();

                  }

-                 if (flag_stop)

+                 if (flag_stop) {

                      *index = SLAPI_BE_NO_BACKEND;

-                 else

+                 } else {

                      (*index)++;

+                 }

              }

          }        

      } else {
@@ -2837,7 +2843,7 @@

      mapping_tree_node *highest_match_node = NULL;

      mapping_tree_node *current;

  

-     if(mapping_tree_freed){

+     if (__atomic_load_4(&mapping_tree_freed, __ATOMIC_RELAXED)) {

          /* shutdown detected */

          return NULL;

      }
@@ -2864,7 +2870,7 @@

  {

      mapping_tree_node *found_node = NULL;

  

-     if(mapping_tree_freed){

+     if (__atomic_load_4(&mapping_tree_freed, __ATOMIC_RELAXED)) {

          /* shutdown detected */

          return NULL;

      }
@@ -2910,7 +2916,7 @@

      mapping_tree_node *current_best_match = mapping_tree_root;

      mapping_tree_node *next_best_match = mapping_tree_root;

  

-     if(mapping_tree_freed){

+     if (__atomic_load_4(&mapping_tree_freed, __ATOMIC_RELAXED)) {

          /* shutdown detected */

          return NULL;

      }
@@ -2944,7 +2950,7 @@

      int i;

      mapping_tree_node *found_node = NULL;

  

-     if(mapping_tree_freed){

+     if (__atomic_load_4(&mapping_tree_freed, __ATOMIC_RELAXED)) {

          /* shutdown detected */

          return NULL;

      }
@@ -2995,7 +3001,7 @@

  {

      char *dn = NULL;

  

-     if(mapping_tree_freed){

+     if (__atomic_load_4(&mapping_tree_freed, __ATOMIC_RELAXED)) {

          /* shutdown detected */

          return NULL;

      }
@@ -3022,7 +3028,7 @@

      char *dn = NULL;

      Slapi_DN *sdn = NULL;

  

-     if(mapping_tree_freed){

+     if (__atomic_load_4(&mapping_tree_freed, __ATOMIC_RELAXED)) {

          /* shutdown detected */

          return NULL;

      }

file modified
+8 -6
@@ -2848,7 +2848,7 @@

  ************************************/

  int

  plugin_setup(Slapi_Entry *plugin_entry, struct slapi_componentid *group,

- 		slapi_plugin_init_fnptr p_initfunc, int add_entry __attribute__((unused)), char *returntext)

+ 		slapi_plugin_init_fnptr p_initfunc, int add_entry, char *returntext)

  {

  	int ii = 0;

  	char attrname[SLAPD_TYPICAL_ATTRIBUTE_NAME_MAX_LENGTH];
@@ -3142,11 +3142,13 @@

  		add_plugin_entry_dn(dn_copy);

  	}

  

-     /* make a copy of the plugin entry for our own use because it will

-        be freed later by the caller */

-     Slapi_Entry *e_copy = slapi_entry_dup(plugin_entry);

-     /* new_plugin_entry(&plugin_entries, plugin_entry, plugin); */

-     new_plugin_entry(&dep_plugin_entries, e_copy, plugin);

+ 	if (add_entry) {

+ 		/* make a copy of the plugin entry for our own use because it will

+ 		   be freed later by the caller */

+ 		Slapi_Entry *e_copy = slapi_entry_dup(plugin_entry);

+ 		/* new_plugin_entry(&plugin_entries, plugin_entry, plugin); */

+ 		new_plugin_entry(&dep_plugin_entries, e_copy, plugin);

+ 	}

  

  PLUGIN_CLEANUP:

  	if (status) {

file modified
+116 -63
@@ -145,43 +145,119 @@

  	slapi_log_err(SLAPI_LOG_FILTER, "plugin_mr_bind", "<=\n");

  }

  

+ void

+ mr_indexer_init_pb(Slapi_PBlock* src_pb, Slapi_PBlock* dst_pb)

+ {

+     char* oid;

+     char *type;

+     uint32_t usage;

+     void *object;

+     IFP destroyFn;

+     IFP indexFn, indexSvFn;

+     

+     /* matching rule plugin arguments */

+     slapi_pblock_get(src_pb, SLAPI_PLUGIN_MR_OID,             &oid);

+     slapi_pblock_get(src_pb, SLAPI_PLUGIN_MR_TYPE,            &type);

+     slapi_pblock_get(src_pb, SLAPI_PLUGIN_MR_USAGE,           &usage);

+     

+     slapi_pblock_set(dst_pb, SLAPI_PLUGIN_MR_OID,             oid);

+     slapi_pblock_set(dst_pb, SLAPI_PLUGIN_MR_TYPE,            type);

+     slapi_pblock_set(dst_pb, SLAPI_PLUGIN_MR_USAGE,           &usage);

+     

+     /* matching rule plugin functions */

+     slapi_pblock_get(src_pb, SLAPI_PLUGIN_MR_INDEX_FN,          &indexFn);

+     slapi_pblock_get(src_pb, SLAPI_PLUGIN_MR_INDEX_SV_FN,       &indexSvFn);

+     

+     slapi_pblock_set(dst_pb, SLAPI_PLUGIN_MR_INDEX_FN,          indexFn);

+     slapi_pblock_set(dst_pb, SLAPI_PLUGIN_MR_INDEX_SV_FN,       indexSvFn);

+ 

+     /* common */

+     slapi_pblock_get(src_pb, SLAPI_PLUGIN_OBJECT,        &object);

+     slapi_pblock_get(src_pb, SLAPI_PLUGIN_DESTROY_FN,    &destroyFn);

+ 

+     slapi_pblock_set(dst_pb, SLAPI_PLUGIN_OBJECT,        object);

+     slapi_pblock_set(dst_pb, SLAPI_PLUGIN_DESTROY_FN,    destroyFn);

+ 

+ 

+ }

+ 

+ /*

+  *  Retrieves the matching rule plugin able to index/sort the provided OID/type

+  * 

+  *  The Matching rules able to index/sort a given OID are stored in a global list: global_mr_oids

+  *

+  *  The retrieval is done in 3 phases:

+  *      - It first searches (in global_mr_oids) for the already bound OID->MR

+  *      - Else, look first in old style MR plugin

+  *        for each registered 'syntax' and 'matchingrule' plugins having a

+  *        SLAPI_PLUGIN_MR_INDEXER_CREATE_FN, it binds (plugin_mr_bind) the first

+  *        plugin that support the OID

+  *      - Else, look in new style MR plugin

+  *        for each registered 'syntax' and 'matchingrule' plugins, it binds (plugin_mr_bind) the first

+  *        plugin that contains OID in its plg_mr_names

+  *

+  * Inputs:

+  *  SLAPI_PLUGIN_MR_OID

+  *      should contain the OID of the matching rule that you want used for indexing or sorting.

+  *  SLAPI_PLUGIN_MR_TYPE

+  *      should contain the attribute type that you want used for indexing or sorting.

+  *  SLAPI_PLUGIN_MR_USAGE

+  *      should specify if the indexer will be used for indexing (SLAPI_PLUGIN_MR_USAGE_INDEX)

+  *      or for sorting (SLAPI_PLUGIN_MR_USAGE_SORT)

+  *

+  *

+  * Output:

+  *

+  *  SLAPI_PLUGIN_MR_OID

+  *      contain the OFFICIAL OID of the matching rule that you want used for indexing or sorting.

+  *  SLAPI_PLUGIN_MR_INDEX_FN

+  *      specifies the indexer function responsible for indexing or sorting of struct berval **

+  *  SLAPI_PLUGIN_MR_INDEX_SV_FN

+  *      specifies the indexer function responsible for indexing or sorting of Slapi_Value **

+  *  SLAPI_PLUGIN_OBJECT

+  *      contain any information that you want passed to the indexer function.

+  *  SLAPI_PLUGIN_DESTROY_FN

+  *      specifies the function responsible for freeing any memory allocated by this indexer factory function.

+  *      For example, memory allocated for a structure that you pass to the indexer function using SLAPI_PLUGIN_OBJECT.

+  *

+  */

  int /* an LDAP error code, hopefully LDAP_SUCCESS */

  slapi_mr_indexer_create (Slapi_PBlock* opb)

  {

      int rc;

      char* oid;

      if (!(rc = slapi_pblock_get (opb, SLAPI_PLUGIN_MR_OID, &oid)))

-     {

- 		IFP createFn = NULL;

- 		struct slapdplugin* mrp = plugin_mr_find_registered (oid);

- 		if (mrp != NULL)

- 		{

- 		    if (!(rc = slapi_pblock_set (opb, SLAPI_PLUGIN, mrp)) &&

- 				!(rc = slapi_pblock_get (opb, SLAPI_PLUGIN_MR_INDEXER_CREATE_FN, &createFn)) &&

- 				createFn != NULL)

- 			{

- 				rc = createFn (opb);

- 		    }

- 		}

- 		else

- 		{

- 		    /* call each plugin, until one is able to handle this request. */

- 		    rc = LDAP_UNAVAILABLE_CRITICAL_EXTENSION;

-             // We need to get the type and usage from the caller.

-             char *type;

-             uint32_t usage;

-             slapi_pblock_get(opb, SLAPI_PLUGIN_MR_TYPE, &type);

-             slapi_pblock_get(opb, SLAPI_PLUGIN_MR_USAGE, &usage);

-             for (mrp = get_plugin_list(PLUGIN_LIST_MATCHINGRULE); mrp != NULL; mrp = mrp->plg_next)

-             {

+ {

+         IFP createFn = NULL;

+         struct slapdplugin* mrp = plugin_mr_find_registered(oid);

+         if (mrp != NULL) {

+             /* Great the matching OID -> MR plugin was already found, just reuse it */

+             if (!(rc = slapi_pblock_set(opb, SLAPI_PLUGIN, mrp)) &&

+                     !(rc = slapi_pblock_get(opb, SLAPI_PLUGIN_MR_INDEXER_CREATE_FN, &createFn)) &&

+                     createFn != NULL) {

+                 rc = createFn(opb);

+             }

+         } else {

+             /* We need to find in the MR plugins list, the MR plugin that will be able to handle OID

+              *

+              * It can be "old style" MR plugin (i.e. collation) that define indexer

+              *

+              * It can be "now style" MR plugin that contain OID string in 'plg_mr_names'

+              * (ie. ces, cis, bin...) where plg_mr_names is defined in 'mr_plugin_table' in each file

+              * ces.c, cis.c...

+              * New style MR plugin have NULL indexer create function but rather use a default indexer

+              */

+ 

+             /* Look for a old syntax-style mr plugin

+              * call each plugin, until one is able to handle this request.

+              */

+             rc = LDAP_UNAVAILABLE_CRITICAL_EXTENSION;

+ 

+             for (mrp = get_plugin_list(PLUGIN_LIST_MATCHINGRULE); mrp != NULL; mrp = mrp->plg_next) {

  

                  Slapi_PBlock *pb = slapi_pblock_new();

+                 mr_indexer_init_pb(opb, pb);

                  slapi_pblock_set(pb, SLAPI_PLUGIN, mrp);

-                 /* From filtercmp.c and matchrule.c, these are the values we need to set. into pb */

-                 slapi_pblock_set(pb, SLAPI_PLUGIN_MR_OID, oid);

-                 slapi_pblock_set(pb, SLAPI_PLUGIN_MR_TYPE, type);

-                 slapi_pblock_set(pb, SLAPI_PLUGIN_MR_USAGE, &usage);

- 

                  /* This is associated with the pb_plugin struct, so it comes with mrp */

                  if (slapi_pblock_get(pb, SLAPI_PLUGIN_MR_INDEXER_CREATE_FN, &createFn)) {

                      /* plugin not a matchingrule type */
@@ -193,14 +269,11 @@

                      IFP indexFn = NULL;

                      IFP indexSvFn = NULL;

                      /* These however, are in the pblock direct, so we need to copy them. */

-                     slapi_pblock_get(opb, SLAPI_PLUGIN_MR_INDEX_FN, &indexFn);

-                     slapi_pblock_get(opb, SLAPI_PLUGIN_MR_INDEX_SV_FN, &indexSvFn);

-                     slapi_pblock_set(pb, SLAPI_PLUGIN_MR_INDEX_FN, indexFn);

-                     slapi_pblock_set(pb, SLAPI_PLUGIN_MR_INDEX_SV_FN, indexSvFn);

+                     slapi_pblock_get(pb, SLAPI_PLUGIN_MR_INDEX_FN, &indexFn);

+                     slapi_pblock_get(pb, SLAPI_PLUGIN_MR_INDEX_SV_FN, &indexSvFn);

                      if (indexFn || indexSvFn) {

                          /* Success: this plugin can handle it. */

-                         /* call create on the opb? */

-                         createFn(opb);

+                         mr_indexer_init_pb(pb, opb);

                          plugin_mr_bind(oid, mrp); /* for future reference */

                          rc = 0; /* success */

                          slapi_pblock_destroy(pb);
@@ -213,37 +286,12 @@

                  /* look for a new syntax-style mr plugin */

                  struct slapdplugin *pi = plugin_mr_find(oid);

                  if (pi) {

-                     Slapi_PBlock *pb = slapi_pblock_new();

-                     slapi_pblock_set(pb, SLAPI_PLUGIN_MR_OID, oid);

-                     slapi_pblock_set(pb, SLAPI_PLUGIN_MR_TYPE, type);

-                     slapi_pblock_set(pb, SLAPI_PLUGIN_MR_USAGE, &usage);

-                     slapi_pblock_set(pb, SLAPI_PLUGIN, pi);

-                     rc = default_mr_indexer_create(pb);

+                     slapi_pblock_set(opb, SLAPI_PLUGIN, pi);

+                     rc = default_mr_indexer_create(opb);

                      if (!rc) {

-                         /* On success, copy the needed values in. These are added by default_mr_indexer_create */

-                         void *pb_object = NULL;

-                         IFP destroy_fn = NULL;

-                         IFP index_fn = NULL;

-                         IFP index_sv_fn = NULL;

- 

-                         slapi_pblock_get(pb, SLAPI_PLUGIN_OBJECT, &pb_object);

-                         slapi_pblock_get(pb, SLAPI_PLUGIN_DESTROY_FN, &destroy_fn);

-                         slapi_pblock_get(pb, SLAPI_PLUGIN_MR_INDEX_FN, &index_fn);

-                         slapi_pblock_get(pb, SLAPI_PLUGIN_MR_INDEX_SV_FN, &index_sv_fn);

- 

-                         /* SLAPI_PLUGIN_MR_INDEXER_CREATE_FN, and SLAPI_PLUGIN_MR_FILTER_CREATE_FN, are part of pb_plugin */

-                         slapi_pblock_set(opb, SLAPI_PLUGIN, pi);

-                         slapi_pblock_set(opb, SLAPI_PLUGIN_MR_OID, oid);

-                         slapi_pblock_set(opb, SLAPI_PLUGIN_MR_TYPE, type);

-                         slapi_pblock_set(opb, SLAPI_PLUGIN_MR_USAGE, &usage);

-                         slapi_pblock_set(opb, SLAPI_PLUGIN_OBJECT, pb_object);

-                         slapi_pblock_set(opb, SLAPI_PLUGIN_DESTROY_FN, destroy_fn);

-                         slapi_pblock_set(opb, SLAPI_PLUGIN_MR_INDEX_FN, index_fn);

-                         slapi_pblock_set(opb, SLAPI_PLUGIN_MR_INDEX_SV_FN, index_sv_fn);

- 

-                         plugin_mr_bind (oid, pi); /* for future reference */

+                         plugin_mr_bind(oid, pi); /* for future reference */

                      }

-                     slapi_pblock_destroy(pb);

+                     slapi_pblock_set(opb, SLAPI_PLUGIN, NULL);

                  }

              }

          }
@@ -725,6 +773,11 @@

  	slapi_pblock_set(pb, SLAPI_PLUGIN_MR_INDEX_FN, mr_wrap_mr_index_fn);

  	slapi_pblock_set(pb, SLAPI_PLUGIN_MR_INDEX_SV_FN, mr_wrap_mr_index_sv_fn);

  	slapi_pblock_set(pb, SLAPI_PLUGIN_DESTROY_FN, default_mr_indexer_destroy);

+ 

+         /* Note the two following setting are in the slapdplugin struct SLAPI_PLUGIN

+          * so they are not really output of the function but will just

+          * be stored in the bound (OID <--> plugin) list (plugin_mr_find_registered/plugin_mr_bind)

+          */

  	slapi_pblock_set(pb, SLAPI_PLUGIN_MR_INDEXER_CREATE_FN, default_mr_indexer_create);

  	slapi_pblock_set(pb, SLAPI_PLUGIN_MR_FILTER_CREATE_FN, default_mr_filter_create);

  	rc = 0;

@@ -553,6 +553,7 @@

  int config_get_ndn_cache_enabled(void);

  int config_get_return_orig_type_switch(void);

  char *config_get_allowed_sasl_mechs(void);

+ char **config_get_allowed_sasl_mechs_array(void);

  int config_set_allowed_sasl_mechs(const char *attrname, char *value, char *errorbuf, int apply);

  int config_get_schemamod(void);

  int config_set_ignore_vattrs(const char *attrname, char *value, char *errorbuf, int apply);
@@ -971,7 +972,7 @@

   * pw_mgmt.c

   */

  void pw_init( void );

- int need_new_pw( Slapi_PBlock *pb, long *t,  Slapi_Entry *e, int pwresponse_req );

+ int need_new_pw(Slapi_PBlock *pb, Slapi_Entry *e, int pwresponse_req);

  int update_pw_info( Slapi_PBlock *pb , char *old_pw );

  int check_pw_syntax( Slapi_PBlock *pb, const Slapi_DN *sdn, Slapi_Value **vals, 

  	char **old_pw, Slapi_Entry *e, int mod_op );
@@ -981,7 +982,6 @@

  int check_account_lock( Slapi_PBlock *pb, Slapi_Entry * bind_target_entry, int pwresponse_req, int account_inactivation_only /*no wire/no pw policy*/);

  int check_pw_minage( Slapi_PBlock *pb, const Slapi_DN *sdn, struct berval **vals) ;

  void add_password_attrs( Slapi_PBlock *pb, Operation *op, Slapi_Entry *e );

- 

  int add_shadow_ext_password_attrs(Slapi_PBlock *pb, Slapi_Entry **e);

  

  /*

file modified
+22 -1
@@ -1517,7 +1517,7 @@

  			sp = slapi_ch_strdup(slapi_value_get_string(valp));

  			ep = sp + strlen(sp);

  			ep = ldap_utf8prevn(sp, ep, toklen);

- 			if (!ep || (sp >= ep)) {

+ 			if (!ep || (sp > ep)) {

  				slapi_ch_free_string(&sp);

  				continue;

  			}
@@ -1780,6 +1780,27 @@

  				goto done;

  			}

  

+ 			/* Set the default values */

+ 			pwdpolicy->pw_mintokenlength = SLAPD_DEFAULT_PW_MINTOKENLENGTH;

+ 			pwdpolicy->pw_minlength = SLAPD_DEFAULT_PW_MINLENGTH;

+ 			pwdpolicy->pw_mindigits = SLAPD_DEFAULT_PW_MINDIGITS;

+ 			pwdpolicy->pw_minalphas = SLAPD_DEFAULT_PW_MINALPHAS;

+ 			pwdpolicy->pw_minuppers = SLAPD_DEFAULT_PW_MINUPPERS;

+ 			pwdpolicy->pw_minlowers = SLAPD_DEFAULT_PW_MINLOWERS;

+ 			pwdpolicy->pw_minspecials = SLAPD_DEFAULT_PW_MINSPECIALS;

+ 			pwdpolicy->pw_min8bit = SLAPD_DEFAULT_PW_MIN8BIT;

+ 			pwdpolicy->pw_maxrepeats = SLAPD_DEFAULT_PW_MAXREPEATS;

+ 			pwdpolicy->pw_mincategories = SLAPD_DEFAULT_PW_MINCATEGORIES;

+ 			pwdpolicy->pw_mintokenlength = SLAPD_DEFAULT_PW_MINTOKENLENGTH;

+ 			pwdpolicy->pw_maxage = SLAPD_DEFAULT_PW_MAXAGE;

+ 			pwdpolicy->pw_minage = SLAPD_DEFAULT_PW_MINAGE;

+ 			pwdpolicy->pw_warning = SLAPD_DEFAULT_PW_WARNING;

+ 			pwdpolicy->pw_inhistory = SLAPD_DEFAULT_PW_INHISTORY;

+ 			pwdpolicy->pw_maxfailure = SLAPD_DEFAULT_PW_MAXFAILURE;

+ 			pwdpolicy->pw_lockduration = SLAPD_DEFAULT_PW_LOCKDURATION;

+ 			pwdpolicy->pw_resetfailurecount = SLAPD_DEFAULT_PW_RESETFAILURECOUNT;

+ 			pwdpolicy->pw_gracelimit = SLAPD_DEFAULT_PW_GRACELIMIT;

+ 

  			/* set the default passwordLegacyPolicy setting */

  			pwdpolicy->pw_is_legacy = 1;

  

file modified
+229 -227
@@ -22,237 +22,239 @@

  /* prototypes                                                               */

  /****************************************************************************/

  

- /* need_new_pw() is called when non rootdn bind operation succeeds with authentication */ 

+ /*

+  * need_new_pw() is called when non rootdn bind operation succeeds with authentication

+  *

+  * Return  0 - password is okay

+  * Return -1 - password is expired, abort bind

+  */

  int

- need_new_pw( Slapi_PBlock *pb, long *t, Slapi_Entry *e, int pwresponse_req )

+ need_new_pw(Slapi_PBlock *pb, Slapi_Entry *e, int pwresponse_req)

  {

- 	time_t 		cur_time, pw_exp_date;

- 	Slapi_Mods smods;

- 	double		diff_t = 0;

- 	char 		*cur_time_str = NULL;

- 	char *passwordExpirationTime = NULL;

- 	char *timestring;

- 	char *dn;

- 	const Slapi_DN *sdn;

- 	passwdPolicy *pwpolicy = NULL;

- 	int	pwdGraceUserTime = 0;

- 	char graceUserTime[16] = {0};

- 	Connection *pb_conn = NULL;

- 

- 	if (NULL == e) {

- 		return (-1);

- 	}

- 	slapi_mods_init (&smods, 0);

- 	sdn = slapi_entry_get_sdn_const( e );

- 	dn = slapi_entry_get_ndn( e );

- 	pwpolicy = new_passwdPolicy(pb, dn);

- 

- 	/* after the user binds with authentication, clear the retry count */

- 	if ( pwpolicy->pw_lockout == 1)

- 	{

- 		if(slapi_entry_attr_get_int( e, "passwordRetryCount") > 0)

- 		{

- 			slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, "passwordRetryCount", "0");

- 		}

- 	}

- 

- 	cur_time = current_time();

- 

- 	/* get passwordExpirationTime attribute */

- 	passwordExpirationTime= slapi_entry_attr_get_charptr(e, "passwordExpirationTime");

- 

- 	if (passwordExpirationTime == NULL)

- 	{

- 		/* password expiration date is not set.

- 		 * This is ok for data that has been loaded via ldif2ldbm

- 		 * Set expiration time if needed,

- 		 * don't do further checking and return 0 */

- 		if (pwpolicy->pw_exp == 1) {

- 			pw_exp_date = time_plus_sec(cur_time, pwpolicy->pw_maxage);

- 

- 			timestring = format_genTime (pw_exp_date);

- 			slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, "passwordExpirationTime", timestring);

- 			slapi_ch_free_string(&timestring);

- 			slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, "passwordExpWarned", "0");

- 			

- 			pw_apply_mods(sdn, &smods);

- 		} else if (pwpolicy->pw_lockout == 1) {

- 			pw_apply_mods(sdn, &smods);

- 		}

- 		slapi_mods_done(&smods);

- 		return ( 0 );

- 	}

- 

- 	pw_exp_date = parse_genTime(passwordExpirationTime);

- 

- 	slapi_ch_free_string(&passwordExpirationTime);

- 

- 	slapi_pblock_get(pb, SLAPI_CONNECTION, &pb_conn);

- 

- 	/* Check if password has been reset */

- 	if ( pw_exp_date == NO_TIME ) {

- 

- 		/* check if changing password is required */  

- 		if ( pwpolicy->pw_must_change ) {

- 			/* set c_needpw for this connection to be true.  this client 

- 			   now can only change its own password */

- 			pb_conn->c_needpw = 1;

- 			*t=0;

- 			/* We need to include "changeafterreset" error in

- 			 * passwordpolicy response control. So, we will not be

- 			 * done here. We remember this scenario by (c_needpw=1)

- 			 * and check it before sending the control from various

- 			 * places. We will also add LDAP_CONTROL_PWEXPIRED control

- 			 * as the return value used to be (1).

- 			 */

- 			goto skip;

- 		}

- 		/* Mark that first login occured */

- 		pw_exp_date = NOT_FIRST_TIME;

- 		timestring = format_genTime(pw_exp_date);

- 		slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, "passwordExpirationTime", timestring);

- 		slapi_ch_free_string(&timestring);

- 	}

+     time_t cur_time, pw_exp_date;

+     Slapi_Mods smods;

+     double diff_t = 0;

+     char *cur_time_str = NULL;

+     char *passwordExpirationTime = NULL;

+     char *timestring;

+     char *dn;

+     const Slapi_DN *sdn;

+     passwdPolicy *pwpolicy = NULL;

+     int pwdGraceUserTime = 0;

+     char graceUserTime[16] = {0};

+     Connection *pb_conn = NULL;

+     long t;

+ 

+     if (NULL == e) {

+         return (-1);

+     }

+     slapi_mods_init(&smods, 0);

+     sdn = slapi_entry_get_sdn_const(e);

+     dn = slapi_entry_get_ndn(e);

+     pwpolicy = new_passwdPolicy(pb, dn);

+ 

+     /* after the user binds with authentication, clear the retry count */

+     if (pwpolicy->pw_lockout == 1) {

+         if (slapi_entry_attr_get_int(e, "passwordRetryCount") > 0) {

+             slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, "passwordRetryCount", "0");

+         }

+     }

+ 

+     cur_time = current_time();

+ 

+     /* get passwordExpirationTime attribute */

+     passwordExpirationTime = slapi_entry_attr_get_charptr(e, "passwordExpirationTime");

+ 

+     if (passwordExpirationTime == NULL) {

+         /* password expiration date is not set.

+          * This is ok for data that has been loaded via ldif2ldbm

+          * Set expiration time if needed,

+          * don't do further checking and return 0 */

+         if (pwpolicy->pw_exp == 1) {

+             pw_exp_date = time_plus_sec(cur_time, pwpolicy->pw_maxage);

+ 

+             timestring = format_genTime(pw_exp_date);

+             slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, "passwordExpirationTime", timestring);

+             slapi_ch_free_string(&timestring);

+             slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, "passwordExpWarned", "0");

+ 

+             pw_apply_mods(sdn, &smods);

+         } else if (pwpolicy->pw_lockout == 1) {

+             pw_apply_mods(sdn, &smods);

+         }

+         slapi_mods_done(&smods);

+         return (0);

+     }

+ 

+     pw_exp_date = parse_genTime(passwordExpirationTime);

+ 

+     slapi_ch_free_string(&passwordExpirationTime);

+ 

+     slapi_pblock_get(pb, SLAPI_CONNECTION, &pb_conn);

+ 

+     /* Check if password has been reset */

+     if (pw_exp_date == NO_TIME) {

+ 

+         /* check if changing password is required */

+         if (pwpolicy->pw_must_change) {

+             /* set c_needpw for this connection to be true.  this client

+                now can only change its own password */

+             pb_conn->c_needpw = 1;

+             t = 0;

+             /* We need to include "changeafterreset" error in

+              * passwordpolicy response control. So, we will not be

+              * done here. We remember this scenario by (c_needpw=1)

+              * and check it before sending the control from various

+              * places. We will also add LDAP_CONTROL_PWEXPIRED control

+              * as the return value used to be (1).

+              */

+             goto skip;

+         }

+         /* Mark that first login occured */

+         pw_exp_date = NOT_FIRST_TIME;

+         timestring = format_genTime(pw_exp_date);

+         slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, "passwordExpirationTime", timestring);

+         slapi_ch_free_string(&timestring);

+     }

  

  skip:

- 	/* if password never expires, don't need to go on; return 0 */

- 	if ( pwpolicy->pw_exp == 0 ) {

- 		/* check for "changeafterreset" condition */

- 		if (pb_conn->c_needpw == 1) {

- 			if (pwresponse_req) {

- 				slapi_pwpolicy_make_response_control ( pb, -1, -1, LDAP_PWPOLICY_CHGAFTERRESET );

- 			} 

- 			slapi_add_pwd_control ( pb, LDAP_CONTROL_PWEXPIRED, 0);

- 		}

- 		pw_apply_mods(sdn, &smods);

- 		slapi_mods_done(&smods);

- 		return ( 0 );

- 	}

- 

- 	/* check if password expired.  If so, abort bind. */

- 	cur_time_str = format_genTime ( cur_time );

- 	if ((pw_exp_date != NO_TIME) && (pw_exp_date != NOT_FIRST_TIME) &&

- 	    (diff_t = difftime(pw_exp_date, parse_genTime(cur_time_str))) <= 0) {

- 		slapi_ch_free_string(&cur_time_str); /* only need this above */

- 		/* password has expired. Check the value of 

- 		 * passwordGraceUserTime and compare it

- 		 * against the value of passwordGraceLimit */

- 		pwdGraceUserTime = slapi_entry_attr_get_int( e, "passwordGraceUserTime");

- 		if ( pwpolicy->pw_gracelimit > pwdGraceUserTime ) {

- 			pwdGraceUserTime++;

- 			sprintf ( graceUserTime, "%d", pwdGraceUserTime );

- 			slapi_mods_add_string(&smods, LDAP_MOD_REPLACE,

- 				"passwordGraceUserTime", graceUserTime);

- 			pw_apply_mods(sdn, &smods);

- 			slapi_mods_done(&smods);

- 			if (pwresponse_req) {

- 				/* check for "changeafterreset" condition */

- 				if (pb_conn->c_needpw == 1) {

- 					slapi_pwpolicy_make_response_control( pb, -1, 

- 						((pwpolicy->pw_gracelimit) - pwdGraceUserTime),

- 						LDAP_PWPOLICY_CHGAFTERRESET);

- 				} else {

- 					slapi_pwpolicy_make_response_control( pb, -1, 

- 						((pwpolicy->pw_gracelimit) - pwdGraceUserTime),

- 						-1);

- 				}

- 			}

- 			

- 			if (pb_conn->c_needpw == 1) {

- 				slapi_add_pwd_control ( pb, LDAP_CONTROL_PWEXPIRED, 0);

- 			}

- 			return ( 0 );

- 		}

- 

- 		/* password expired and user exceeded limit of grace attemps.

- 		 * Send result and also the control */

- 		slapi_add_pwd_control ( pb, LDAP_CONTROL_PWEXPIRED, 0);

- 		if (pwresponse_req) {

- 			slapi_pwpolicy_make_response_control ( pb, -1, -1, LDAP_PWPOLICY_PWDEXPIRED );

- 		}

- 		slapi_send_ldap_result ( pb, LDAP_INVALID_CREDENTIALS, NULL,

- 			"password expired!", 0, NULL );

- 		

- 		/* abort bind */

- 		/* pass pb to do_unbind().  pb->pb_op->o_opid and 

- 		   pb->pb_op->o_tag are not right but I don't see 

- 		   do_unbind() checking for those.   We might need to 

- 		   create a pb for unbind operation.  Also do_unbind calls

- 		   pre and post ops.  Maybe we don't want to call them */

- 		if (pb_conn && (LDAP_VERSION2 == pb_conn->c_ldapversion)) {

- 			Operation *pb_op = NULL;

- 			slapi_pblock_get(pb, SLAPI_OPERATION, &pb_op);

- 			/* We close the connection only with LDAPv2 connections */

- 			disconnect_server( pb_conn, pb_op->o_connid,

- 				pb_op->o_opid, SLAPD_DISCONNECT_UNBIND, 0);

- 		}

- 		/* Apply current modifications */

- 		pw_apply_mods(sdn, &smods);

- 		slapi_mods_done(&smods);

- 		return (-1);

- 	}

- 	slapi_ch_free((void **) &cur_time_str );

- 

- 	/* check if password is going to expire within "passwordWarning" */

- 	/* Note that if pw_exp_date is NO_TIME or NOT_FIRST_TIME,

- 	 * we must send warning first and this changes the expiration time.

- 	 * This is done just below since diff_t is 0 

- 	 */

- 	if ( diff_t <= pwpolicy->pw_warning ) {

- 		int pw_exp_warned = 0;

- 		

- 		pw_exp_warned = slapi_entry_attr_get_int( e, "passwordExpWarned");

- 		if ( !pw_exp_warned ){

- 			/* first time send out a warning */

- 			/* reset the expiration time to current + warning time 

- 			 * and set passwordExpWarned to true

- 			 */

- 			if (pb_conn->c_needpw != 1) {

- 				pw_exp_date = time_plus_sec(cur_time, pwpolicy->pw_warning);

- 			}

- 			

- 			timestring = format_genTime(pw_exp_date);

- 			slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, "passwordExpirationTime", timestring);

- 			slapi_ch_free_string(&timestring);

- 

- 			slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, "passwordExpWarned", "1");

- 			

- 			*t = pwpolicy->pw_warning;

- 

- 		} else {

- 			*t = (long)diff_t; /* jcm: had to cast double to long */

- 		}

- 

- 		pw_apply_mods(sdn, &smods);

- 		slapi_mods_done(&smods);

- 		if (pwresponse_req) {

- 			/* check for "changeafterreset" condition */

- 			if (pb_conn->c_needpw == 1) {

- 				slapi_pwpolicy_make_response_control( pb, *t, -1, LDAP_PWPOLICY_CHGAFTERRESET);

- 			} else {

- 				slapi_pwpolicy_make_response_control( pb, *t, -1, -1);

- 			}

- 		}

- 

- 		if (pb_conn->c_needpw == 1) {

- 			slapi_add_pwd_control ( pb, LDAP_CONTROL_PWEXPIRED, 0);

- 		}

- 		return (2);

- 	} else {

- 		if (pwresponse_req && pwpolicy->pw_send_expiring) {

- 			slapi_pwpolicy_make_response_control( pb, diff_t, -1, -1);

- 			slapi_add_pwd_control(pb, LDAP_CONTROL_PWEXPIRING, diff_t);

- 		}

- 	}

- 

- 	pw_apply_mods(sdn, &smods);

- 	slapi_mods_done(&smods);

- 	/* Leftover from "changeafterreset" condition */

- 	if (pb_conn->c_needpw == 1) {

- 		slapi_add_pwd_control ( pb, LDAP_CONTROL_PWEXPIRED, 0);

- 	}

- 	/* passes checking, return 0 */

- 	return( 0 );

+     /* if password never expires, don't need to go on; return 0 */

+     if (pwpolicy->pw_exp == 0) {

+         /* check for "changeafterreset" condition */

+         if (pb_conn->c_needpw == 1) {

+             if (pwresponse_req) {

+                 slapi_pwpolicy_make_response_control(pb, -1, -1, LDAP_PWPOLICY_CHGAFTERRESET);

+             }

+             slapi_add_pwd_control(pb, LDAP_CONTROL_PWEXPIRED, 0);

+         }

+         pw_apply_mods(sdn, &smods);

+         slapi_mods_done(&smods);

+         return (0);

+     }

+ 

+     /* check if password expired.  If so, abort bind. */

+     cur_time_str = format_genTime(cur_time);

+     if ((pw_exp_date != NO_TIME) && (pw_exp_date != NOT_FIRST_TIME) &&

+         (diff_t = difftime(pw_exp_date, parse_genTime(cur_time_str))) <= 0) {

+         slapi_ch_free_string(&cur_time_str); /* only need this above */

+         /* password has expired. Check the value of

+          * passwordGraceUserTime and compare it

+          * against the value of passwordGraceLimit */

+         pwdGraceUserTime = slapi_entry_attr_get_int(e, "passwordGraceUserTime");

+         if (pwpolicy->pw_gracelimit > pwdGraceUserTime) {

+             pwdGraceUserTime++;

+             sprintf(graceUserTime, "%d", pwdGraceUserTime);

+             slapi_mods_add_string(&smods, LDAP_MOD_REPLACE,

+                                   "passwordGraceUserTime", graceUserTime);

+             pw_apply_mods(sdn, &smods);

+             slapi_mods_done(&smods);

+             if (pwresponse_req) {

+                 /* check for "changeafterreset" condition */

+                 if (pb_conn->c_needpw == 1) {

+                     slapi_pwpolicy_make_response_control(pb, -1,

+                                                          ((pwpolicy->pw_gracelimit) - pwdGraceUserTime),

+                                                          LDAP_PWPOLICY_CHGAFTERRESET);

+                 } else {

+                     slapi_pwpolicy_make_response_control(pb, -1,

+                                                          ((pwpolicy->pw_gracelimit) - pwdGraceUserTime),

+                                                          -1);

+                 }

+             }

+             slapi_add_pwd_control(pb, LDAP_CONTROL_PWEXPIRED, 0);

+             return (0);

+         }

+ 

+         /* password expired and user exceeded limit of grace attemps.

+          * Send result and also the control */

+         slapi_add_pwd_control(pb, LDAP_CONTROL_PWEXPIRED, 0);

+         if (pwresponse_req) {

+             slapi_pwpolicy_make_response_control(pb, -1, -1, LDAP_PWPOLICY_PWDEXPIRED);

+         }

+         slapi_send_ldap_result(pb, LDAP_INVALID_CREDENTIALS, NULL,

+                                "password expired!", 0, NULL);

+ 

+         /* abort bind */

+         /* pass pb to do_unbind().  pb->pb_op->o_opid and

+            pb->pb_op->o_tag are not right but I don't see

+            do_unbind() checking for those.   We might need to

+            create a pb for unbind operation.  Also do_unbind calls

+            pre and post ops.  Maybe we don't want to call them */

+         if (pb_conn && (LDAP_VERSION2 == pb_conn->c_ldapversion)) {

+             Operation *pb_op = NULL;

+             slapi_pblock_get(pb, SLAPI_OPERATION, &pb_op);

+             /* We close the connection only with LDAPv2 connections */

+             disconnect_server(pb_conn, pb_op->o_connid,

+                               pb_op->o_opid, SLAPD_DISCONNECT_UNBIND, 0);

+         }

+         /* Apply current modifications */

+         pw_apply_mods(sdn, &smods);

+         slapi_mods_done(&smods);

+         return (-1);

+     }

+     slapi_ch_free((void **)&cur_time_str);

+ 

+     /* check if password is going to expire within "passwordWarning" */

+     /* Note that if pw_exp_date is NO_TIME or NOT_FIRST_TIME,

+      * we must send warning first and this changes the expiration time.

+      * This is done just below since diff_t is 0

+      */

+     if (diff_t <= pwpolicy->pw_warning) {

+         int pw_exp_warned = 0;

+ 

+         pw_exp_warned = slapi_entry_attr_get_int(e, "passwordExpWarned");

+         if (!pw_exp_warned) {

+             /* first time send out a warning */

+             /* reset the expiration time to current + warning time

+              * and set passwordExpWarned to true

+              */

+             if (pb_conn->c_needpw != 1) {

+                 pw_exp_date = time_plus_sec(cur_time, pwpolicy->pw_warning);

+             }

+ 

+             timestring = format_genTime(pw_exp_date);

+             slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, "passwordExpirationTime", timestring);

+             slapi_ch_free_string(&timestring);

+ 

+             slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, "passwordExpWarned", "1");

+ 

+             t = pwpolicy->pw_warning;

+ 

+         } else {

+             t = (long)diff_t; /* jcm: had to cast double to long */

+         }

+ 

+         pw_apply_mods(sdn, &smods);

+         slapi_mods_done(&smods);

+         if (pwresponse_req) {

+             /* check for "changeafterreset" condition */

+             if (pb_conn->c_needpw == 1) {

+                 slapi_pwpolicy_make_response_control(pb, t, -1, LDAP_PWPOLICY_CHGAFTERRESET);

+             } else {

+                 slapi_pwpolicy_make_response_control(pb, t, -1, -1);

+             }

+         }

+ 

+         if (pb_conn->c_needpw == 1) {

+             slapi_add_pwd_control(pb, LDAP_CONTROL_PWEXPIRED, 0);

+         } else {

+             slapi_add_pwd_control(pb, LDAP_CONTROL_PWEXPIRING, t);

+         }

+         return (0);

+     } else {

+         if (pwresponse_req && pwpolicy->pw_send_expiring) {

+             slapi_pwpolicy_make_response_control(pb, diff_t, -1, -1);

+             slapi_add_pwd_control(pb, LDAP_CONTROL_PWEXPIRING, diff_t);

+         }

+     }

+ 

+     pw_apply_mods(sdn, &smods);

+     slapi_mods_done(&smods);

+     /* Leftover from "changeafterreset" condition */

+     if (pb_conn->c_needpw == 1) {

+         slapi_add_pwd_control(pb, LDAP_CONTROL_PWEXPIRED, 0);

+     }

+     /* passes checking, return 0 */

+     return (0);

  }

  

  /* Called once from main */

@@ -55,15 +55,17 @@

  int

  pw_verify_be_dn(Slapi_PBlock *pb, Slapi_Entry **referral)

  {

-     int rc = 0;

+     int rc = SLAPI_BIND_SUCCESS;

      Slapi_Backend *be = NULL;

  

-     if (slapi_mapping_tree_select(pb, &be, referral, NULL, 0) != LDAP_SUCCESS) {

+     int mt_result = slapi_mapping_tree_select(pb, &be, referral, NULL, 0);

+     if (mt_result != LDAP_SUCCESS) {

          return SLAPI_BIND_NO_BACKEND;

      }

  

      if (*referral) {

-         slapi_be_Unlock(be);

+         /* If we have a referral, this is NULL */

+         PR_ASSERT(be == NULL);

          return SLAPI_BIND_REFERRAL;

      }

  
@@ -109,14 +111,10 @@

      slapi_pblock_get(pb, SLAPI_BIND_CREDENTIALS, &cred);

      slapi_pblock_get(pb, SLAPI_BIND_METHOD, &method);

  

-     if (pb_sdn != NULL || cred != NULL) {

+     if (pb_sdn == NULL) {

          return LDAP_OPERATIONS_ERROR;

      }

  

-     if (*referral) {

-         return SLAPI_BIND_REFERRAL;

-     }

- 

      /* We need a slapi_sdn_isanon? */

      if (method == LDAP_AUTH_SIMPLE && cred->bv_len == 0) {

          return SLAPI_BIND_ANONYMOUS;
@@ -130,7 +128,11 @@

      if (slapi_mapping_tree_select(pb, &be, referral, NULL, 0) != LDAP_SUCCESS) {

          return SLAPI_BIND_NO_BACKEND;

      }

-     slapi_be_Unlock(be);

+ 

+     if (*referral) {

+         PR_ASSERT(be == NULL);

+         return SLAPI_BIND_REFERRAL;

+     }

  

      slapi_pblock_set(pb, SLAPI_BACKEND, be);

      slapi_pblock_set(pb, SLAPI_PLUGIN, be->be_database);
@@ -138,6 +140,7 @@

      set_db_default_result_handlers(pb);

  

      /* The backend associated with this identity is real. */

+     slapi_be_Unlock(be);

  

      return SLAPI_BIND_SUCCESS;

  }

@@ -351,6 +351,11 @@

  	slapi_pblock_get (pb, SLAPI_OPERATION, &operation);

  	slapi_pblock_get(pb, SLAPI_CONNECTION, &conn);

  

+ 	if (operation == NULL) {

+ 		slapi_log_err(SLAPI_LOG_ERR, "send_ldap_result_ext", "No operation found: slapi_search_internal_set_pb was incomplete (invalid 'base' ?)\n");

+ 		return;

+ 	}

+ 

  	if (operation->o_status == SLAPI_OP_STATUS_RESULT_SENT) {

  		return; /* result already sent */

  	}

file modified
+63 -84
@@ -169,8 +169,6 @@

          }

      } else if (strcasecmp(option, "auxprop_plugin") == 0) {

          *result = "iDS";

-     } else if (strcasecmp(option, "mech_list") == 0){

-         *result = config_get_allowed_sasl_mechs();

      }

  

      if (*result) *len = strlen(*result);
@@ -581,12 +579,8 @@

          slapi_pblock_set(pb, SLAPI_BIND_METHOD, &method);

          /* Feed it to pw_verify_be_dn */

          bind_result = pw_verify_be_dn(pb, &referral);

-         /* Now check the result, and unlock be if needed. */

-         if (bind_result == SLAPI_BIND_SUCCESS || bind_result == SLAPI_BIND_ANONYMOUS) {

-             Slapi_Backend *be = NULL;

-             slapi_pblock_get(pb, SLAPI_BACKEND, &be);

-             slapi_be_Unlock(be);

-         } else if (bind_result == SLAPI_BIND_REFERRAL) {

+         /* Now check the result. */

+         if (bind_result == SLAPI_BIND_REFERRAL) {

              /* If we have a referral do we ignore it for sasl? */

              slapi_entry_free(referral);

          }
@@ -753,7 +747,10 @@

   */

  char **ids_sasl_listmech(Slapi_PBlock *pb)

  {

-     char **ret, **others;

+     char **ret;

+     char **config_ret;

+     char **sup_ret;

+     char **others;

      const char *str;

      char *dupstr;

      sasl_conn_t *sasl_conn;
@@ -766,32 +763,53 @@

      slapi_pblock_get(pb, SLAPI_CONNECTION, &pb_conn);

  

      /* hard-wired mechanisms and slapi plugin registered mechanisms */

-     ret = slapi_get_supported_saslmechanisms_copy();

+     sup_ret = slapi_get_supported_saslmechanisms_copy();

  

-     if (pb_conn == NULL) {

-         return ret;

+     /* If we have a connection, get the provided list from SASL */

+     if (pb_conn != NULL) {

+         sasl_conn = (sasl_conn_t*)pb_conn->c_sasl_conn;

+         if (sasl_conn != NULL) {

+             /* sasl library mechanisms are connection dependent */

+             PR_EnterMonitor(pb_conn->c_mutex);

+             if (sasl_listmech(sasl_conn,

+                               NULL,     /* username */

+                               "", ",", "",

+                               &str, NULL, NULL) == SASL_OK) {

+                 slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_listmech", "sasl library mechs: %s\n", str);

+                 /* merge into result set */

+                 dupstr = slapi_ch_strdup(str);

+                 others = slapi_str2charray_ext(dupstr, ",", 0 /* don't list duplicate mechanisms */);

+ 

+                 charray_merge(&sup_ret, others, 1);

+                 charray_free(others);

+                 slapi_ch_free((void**)&dupstr);

+             }

+             PR_ExitMonitor(pb_conn->c_mutex);

+         }

      }

  

-     sasl_conn = (sasl_conn_t*)pb_conn->c_sasl_conn;

-     if (sasl_conn == NULL) {

-         return ret;

-     }

+     /* Get the servers "allowed" list */

+     config_ret = config_get_allowed_sasl_mechs_array();

  

-     /* sasl library mechanisms are connection dependent */

-     PR_EnterMonitor(pb_conn->c_mutex);

-     if (sasl_listmech(sasl_conn, 

-                       NULL,     /* username */

-                       "", ",", "",

-                       &str, NULL, NULL) == SASL_OK) {

-         slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_listmech", "sasl library mechs: %s\n", str);

-         /* merge into result set */

-         dupstr = slapi_ch_strdup(str);

-         others = slapi_str2charray_ext(dupstr, ",", 0 /* don't list duplicate mechanisms */);

-         charray_merge(&ret, others, 1);

-         charray_free(others);

-         slapi_ch_free((void**)&dupstr);

+     /* Remove any content that isn't in the allowed list */

+     if (config_ret != NULL) {

+         /* Get the set of supported mechs in the intersection of the two */

+         ret = charray_intersection(sup_ret, config_ret);

+         charray_free(sup_ret);

+         charray_free(config_ret);

+     } else {

+         /* The allowed list was empty, just take our supported list. */

+         ret = sup_ret;

      }

-     PR_ExitMonitor(pb_conn->c_mutex);

+ 

+     /*

+      * https://pagure.io/389-ds-base/issue/49231

+      * Because of the way that SASL mechs are managed in bind.c and saslbind.c

+      * even if EXTERNAL was *not* in the list of allowed mechs, it was allowed

+      * in the bind process because it bypasses lots of our checking. As a result

+      * we have to always present it.

+      */

+     charray_assert_present(&ret, "EXTERNAL");

  

      slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_listmech", "<=\n");

  
@@ -806,44 +824,21 @@

  static int

  ids_sasl_mech_supported(Slapi_PBlock *pb, const char *mech)

  {

-   int i, ret = 0;

-   char **mechs;

-   char *dupstr;

-   const char *str;

-   int sasl_result = 0;

-   Connection *pb_conn = NULL;

-   slapi_pblock_get(pb, SLAPI_CONNECTION, &pb_conn);

- 

-   sasl_conn_t *sasl_conn = (sasl_conn_t *)pb_conn->c_sasl_conn;

+     slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_mech_supported", "=>\n");

  

-   slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_mech_supported", "=>\n");

+     char **allowed_mechs = ids_sasl_listmech(pb);

  

- 

-   /* sasl_listmech is not thread-safe - caller must lock pb_conn */

-   sasl_result = sasl_listmech(sasl_conn, 

-                     NULL,     /* username */

-                     "", ",", "",

-                     &str, NULL, NULL);

-   if (sasl_result != SASL_OK) {

-     return 0;

-   }

- 

-   dupstr = slapi_ch_strdup(str);

-   mechs = slapi_str2charray(dupstr, ",");

- 

-   for (i = 0; mechs[i] != NULL; i++) {

-     if (strcasecmp(mech, mechs[i]) == 0) {

-       ret = 1;

-       break;

+     /* 0 indicates "now allowed" */

+     int allowed_mech_present = 0;

+     if (allowed_mechs != NULL) {

+         /* Returns 1 if present and allowed. */

+         allowed_mech_present = charray_inlist(allowed_mechs, (char *)mech);

+         charray_free(allowed_mechs);

      }

-   }

  

-   charray_free(mechs);

-   slapi_ch_free((void**)&dupstr);

+     slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_mech_supported", "<=\n");

  

-   slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_mech_supported", "<=\n");

- 

-   return ret;

+     return allowed_mech_present;

  }

  

  /*
@@ -852,7 +847,6 @@

  void ids_sasl_check_bind(Slapi_PBlock *pb)

  {

      int rc, isroot;

-     long t;

      sasl_conn_t *sasl_conn;

      struct propctx *propctx;

      sasl_ssf_t *ssfp;
@@ -907,9 +901,9 @@

       * different error code to SASL_NOMECH.  Must be called

       * while holding the pb_conn lock

       */

-     if (!ids_sasl_mech_supported(pb, mech)) {

-       rc = SASL_NOMECH;

-       goto sasl_check_result;

+     if (ids_sasl_mech_supported(pb, mech) == 0) {

+         rc = SASL_NOMECH;

+         goto sasl_check_result;

      }

  

      /* can't do any harm */
@@ -1091,23 +1085,8 @@

          set_db_default_result_handlers(pb);

  

          /* check password expiry */

-         if (!isroot) {

-             int pwrc;

- 

-             pwrc = need_new_pw(pb, &t, bind_target_entry, pwresponse_requested);

-             

-             switch (pwrc) {

-             case 1:

-                 slapi_add_pwd_control(pb, LDAP_CONTROL_PWEXPIRED, 0);

-                 break;

-             case 2:

-                 slapi_add_pwd_control(pb, LDAP_CONTROL_PWEXPIRING, t);

-                 break;

-             case -1:

-                 goto out;

-             default:

-                 break;

-             }

+         if (!isroot && need_new_pw(pb, bind_target_entry, pwresponse_requested) == -1) {

+             goto out;

          }

  

          /* attach the sasl data */

@@ -1592,6 +1592,7 @@

  	PRUint64		c_maxthreadsblocked; /* # of operations blocked by maxthreads */

  	int				c_opsinitiated;	/* # ops initiated/next op id	  */

  	PRInt32			c_opscompleted;	/* # ops completed		  */

+ 	uint64_t        c_anonlimits_set; /* default anon limits are set */

  	PRInt32			c_threadnumber; /* # threads used in this conn    */

  	int				c_refcnt;	/* # ops refering to this conn    */

  	PRMonitor		*c_mutex;	/* protect each conn structure; need to be re-entrant */ 
@@ -2425,6 +2426,7 @@

    int pagedsizelimit;

    char *default_naming_context; /* Default naming context (normalized) */

    char *allowed_sasl_mechs;     /* comma/space separated list of allowed sasl mechs */

+   char **allowed_sasl_mechs_array;     /* Array of allow sasl mechs */

    int sasl_max_bufsize;         /* The max receive buffer size for SASL */

  

    /* disk monitoring */

@@ -28,7 +28,7 @@

  #endif

  

  /* Provides our int types and platform specific requirements. */

- #include <slapi_pal.h>

+ #include "slapi_pal.h"

  

  #include "prtypes.h"

  #include "ldap.h"
@@ -6779,6 +6779,12 @@

   */

  time_t slapi_current_time( void );

  

+ /**

+  * Returns the current system time as a hr clock

+  *

+  * \return timespec of the current monotonic time.

+  */

+ struct timespec slapi_current_rel_time_hr(void);

  

  /*

   * Plugin and parameter block related macros (remainder of this file).
@@ -8066,6 +8072,36 @@

   */

  void    DS_Sleep(PRIntervalTime ticks);

  

+ /**

+  * Increment a 64bitintegral atomicly

+  *

+  * \param ptr - pointer to integral to increment

+  * \param memorder - __ATOMIC_RELAXED, __ATOMIC_CONSUME, __ATOMIC_ACQUIRE,

+  * __ATOMIC_RELEASE, __ATOMIC_ACQ_REL, __ATOMIC_SEQ_CST

+  * \return - new value of ptr

+  */

+ uint64_t slapi_atomic_incr_64(uint64_t *ptr, int memorder);

+ 

+ /**

+  * Decrement a 64bitintegral atomicly

+  *

+  * \param ptr - pointer to integral to decrement

+  * \param memorder - __ATOMIC_RELAXED, __ATOMIC_CONSUME, __ATOMIC_ACQUIRE,

+  * __ATOMIC_RELEASE, __ATOMIC_ACQ_REL, __ATOMIC_SEQ_CST

+  * \return - new value of ptr

+  */

+ uint64_t slapi_atomic_decr_64(uint64_t *ptr, int memorder);

+ 

+ /**

+  * Diffs two timespects a - b into *diff. This is useful with

+  * clock_monotonic to find time taken to perform operations.

+  *

+  * \param struct timespec a the "end" time.

+  * \param struct timespec b the "start" time.

+  * \param struct timespec c the difference.

+  */

+ void slapi_timespec_diff(struct timespec *a, struct timespec *b, struct timespec *diff);

+ 

  #ifdef __cplusplus

  }

  #endif

@@ -193,7 +193,7 @@

     a csn from the set.*/

  int csn_increment_subsequence (CSN *csn);

  

- void csnplFreeCSN (void *arg);

+ void csnplFreeCSNPL_CTX (void *arg);

  /*

   * csnset.c

   */
@@ -380,7 +380,7 @@

  void ndn_cache_init(void);

  void ndn_cache_destroy(void);

  int ndn_cache_started(void);

- void ndn_cache_get_stats(PRUint64 *hits, PRUint64 *tries, size_t *size, size_t *max_size, long *count);

+ void ndn_cache_get_stats(PRUint64 *hits, PRUint64 *tries, size_t *size, size_t *max_size, size_t *thread_size, size_t *evicts, size_t *slots, long *count);

  #define NDN_DEFAULT_SIZE 20971520 /* 20mb - size of normalized dn cache */

  

  /* filter.c */
@@ -831,8 +831,11 @@

  char ** cool_charray_dup( char **a );

  void cool_charray_free( char **array );

  void charray_subtract( char **a, char **b, char ***c );

+ char **charray_intersection(char **a, char **b);

  int charray_get_index(char **array, char *s);

  int charray_normdn_add(char ***chararray, char *dn, char *errstr);

+ void charray_assert_present(char ***a, char *s);

+ 

  

  /******************************************************************************

   * value array routines.

@@ -269,3 +269,33 @@

      return value;

  }

  

+ /*

+  * atomic increment functions (64bit)

+  */

+ uint64_t

+ slapi_atomic_incr_64(uint64_t *ptr, int memorder)

+ {

+ #ifdef ATOMIC_64BIT_OPERATIONS

+     return __atomic_add_fetch_8(ptr, 1, memorder);

+ #else

+     PRInt32 *pr_ptr = (PRInt32 *)ptr;

+     return PR_AtomicIncrement(pr_ptr);

+ #endif

+ }

+ 

+ /*

+  * atomic decrement functions (64bit)

+  */

+ 

+ uint64_t

+ slapi_atomic_decr_64(uint64_t *ptr, int memorder)

+ {

+ #ifdef ATOMIC_64BIT_OPERATIONS

+     return __atomic_sub_fetch_8(ptr, 1, memorder);

+ #else

+     PRInt32 *pr_ptr = (PRInt32 *)ptr;

+     return PR_AtomicDecrement(pr_ptr);

+ #endif

+ }

+ 

+ 

@@ -11,8 +11,6 @@

   * specific issues.

   */

  

- /* Provide ch_malloc etc. */

- #include <slapi-plugin.h>

  /* Provide slapi_log_err macro wrapper */

  #include <slapi-private.h>

  #include <slapi_pal.h>
@@ -155,7 +153,16 @@

  

      /* Both memtotal and memavail are in kb */

      memtotal = memtotal * 1024;

-     memavail = memavail * 1024;

+ 

+     /*

+      * Oracle Enterprise Linux doesn't provide a valid memavail value, so fall

+      * back to 80% of memtotal.

+      */

+     if (memavail == 0) {

+         memavail = memtotal * 0.8;

+     } else {

+         memavail = memavail * 1024;

+     }

  

      /* If it's possible, get our cgroup info */

      uint64_t cg_mem_soft = 0;

@@ -19,13 +19,7 @@

  

  #pragma once

  

- #include <config.h>

- 

- #ifdef HAVE_INTTYPES_H

  #include <inttypes.h>

- #else

- #error Need to define portable format macros such as PRIu64

- #endif /* HAVE_INTTYPES_H */

  

  /**

   * Structure that contains our system memory information in bytes and pages.

@@ -458,23 +458,23 @@

               * around.  Recreate it since we don't know what state it is in. */

              if (sem_unlink(stats_sem_name) != 0) {

                  slapi_log_err(SLAPI_LOG_EMERG, "snmp_collator_create_semaphore",

-                         "Failed to delete old semaphore for stats file (%s). "

-                         "Error %d (%s).\n", stats_sem_name, errno, slapd_system_strerror(errno) );

+                         "Failed to delete old semaphore for stats file (/dev/shm/sem.%s). "

+                         "Error %d (%s).\n", stats_sem_name + 1, errno, slapd_system_strerror(errno) );

                  exit(1);

              }

  

              if ((stats_sem = sem_open(stats_sem_name, O_CREAT | O_EXCL, SLAPD_DEFAULT_FILE_MODE, 1)) == SEM_FAILED) {

                  /* No dice */

                  slapi_log_err(SLAPI_LOG_EMERG, "snmp_collator_create_semaphore",

-                         "Failed to create semaphore for stats file (%s). Error %d (%s).\n",

-                         stats_sem_name, errno, slapd_system_strerror(errno) );

+                         "Failed to create semaphore for stats file (/dev/shm/sem.%s). Error %d (%s).\n",

+                         stats_sem_name + 1, errno, slapd_system_strerror(errno) );

                  exit(1);

              }

          } else {

              /* Some other problem occurred creating the semaphore. */

              slapi_log_err(SLAPI_LOG_EMERG, "snmp_collator_create_semaphore",

-                     "Failed to create semaphore for stats file (%s). Error %d.(%s)\n",

-                     stats_sem_name, errno, slapd_system_strerror(errno) );

+                     "Failed to create semaphore for stats file (/dev/shm/sem.%s). Error %d.(%s)\n",

+                     stats_sem_name + 1, errno, slapd_system_strerror(errno) );

              exit(1);

          }

      }

file modified
+26
@@ -144,6 +144,32 @@

      return current_time();

  }

  

+ struct timespec

+ slapi_current_rel_time_hr(void)

+ {

+     struct timespec now;

+     clock_gettime(CLOCK_MONOTONIC, &now);

+     return now;

+ }

+ 

+ void

+ slapi_timespec_diff(struct timespec *a, struct timespec *b, struct timespec *diff)

+ {

+     /* Now diff the two */

+     time_t sec = a->tv_sec - b->tv_sec;

+     int32_t nsec = a->tv_nsec - b->tv_nsec;

+ 

+     if (nsec < 0) {

+         /* It's negative so take one second */

+         sec -= 1;

+         /* And set nsec to to a whole value */

+         nsec = 1000000000 - nsec;

+     }

+ 

+     diff->tv_sec = sec;

+     diff->tv_nsec = nsec;

+ }

+ 

  time_t

  time_plus_sec (time_t l, long r)

      /* return the point in time 'r' seconds after 'l'. */

file modified
+1 -1
@@ -132,7 +132,7 @@

      }

      for ( ;n > 0; --n) {

          prev = ldap_utf8prev(prev);

-         if ((prev <= s) && (n > 0)) {

+         if ((n > 0) && (prev < s)) {

              return NULL;

          }

      }

file modified
+36 -10
@@ -153,6 +153,11 @@

                      break;

                  }

                  do {

+                     if (bufSpace < 4) {

+                         memcpy(bufNext, "..", 2);

+                         bufNext += 2;

+                         goto bail;

+                     }

                      if (esc == UTIL_ESCAPE_BACKSLASH) {

                          /* *s is '\\' */

                          /* If *(s+1) and *(s+2) are both hex digits,
@@ -169,11 +174,6 @@

                          if (!(flags & DOESCAPE_FLAGS_HEX_NOESC)) {

                              *bufNext++ = '\\'; --bufSpace;

                          }

-                         if (bufSpace < 3) {

-                             memcpy(bufNext, "..", 2);

-                             bufNext += 2;

-                             goto bail;

-                         }

                          PR_snprintf(bufNext, 3, "%02x", *(unsigned char*)s);

                          bufNext += 2; bufSpace -= 2;

                      }
@@ -224,9 +224,10 @@

  

  struct filter_ctx {

    char *buf;

-   char attr[ATTRSIZE];

+   char *attr;

    int attr_position;

    int attr_found;

+   size_t attr_size;

    int buf_size;

    int buf_len;

    int next_arg_needs_esc_norm;
@@ -265,7 +266,7 @@

       *  Start collecting the attribute name so we can use the correct

       *  syntax normalization func.

       */

-     if(ctx->attr_found == 0 && ctx->attr_position < (ATTRSIZE - 1)){

+     if(ctx->attr_found == 0 && ctx->attr_position < (ctx->attr_size - 1)) {

          if(ctx->attr[0] == '\0'){

              if(strstr(val,"=")){

                  /* we have an attr we need to record */
@@ -279,6 +280,14 @@

                   *  attr with val.  The next pass should be '=', otherwise we will

                   *  reset it.

                   */

+                 if (slen > ctx->attr_size) {

+                     if (ctx->attr_size == ATTRSIZE) {

+                         ctx->attr = slapi_ch_calloc(sizeof(char), slen+1);

+                     } else {

+                         ctx->attr = slapi_ch_realloc(ctx->attr, sizeof(char) * (slen+1));

+                     }

+                     ctx->attr_size = slen+1;

+                 }

                  memcpy(ctx->attr, val, slen);

                  ctx->attr_position = slen;

              }
@@ -288,9 +297,20 @@

              } else {

                  if(special_attr_char(val[0])){

                      /* this is not an attribute, we should not be collecting this, reset everything */

-                     memset(ctx->attr, '\0', ATTRSIZE);

+                     memset(ctx->attr, '\0', ctx->attr_size);

                      ctx->attr_position = 0;

                  } else {

+                     /* we can be adding char by char and overrun allocated size */

+                     if (ctx->attr_position >= ctx->attr_size) {

+                         if (ctx->attr_size == ATTRSIZE) {

+                             char *ctxattr = slapi_ch_calloc(sizeof(char), ctx->attr_size + ATTRSIZE);

+                             memcpy(ctxattr, ctx->attr, ctx->attr_size);

+                             ctx->attr = ctxattr;

+                         } else {

+                             ctx->attr = slapi_ch_realloc(ctx->attr, sizeof(char) * (ctx->attr_size + ATTRSIZE));

+                         }

+                         ctx->attr_size = ctx->attr_size + ATTRSIZE;

+                     }

                      memcpy(ctx->attr + ctx->attr_position, val, 1);

                      ctx->attr_position++;

                  }
@@ -363,7 +383,7 @@

          ctx->next_arg_needs_esc_norm = 0;

          ctx->attr_found = 0;

          ctx->attr_position = 0;

-         memset(ctx->attr, '\0', ATTRSIZE);

+         memset(ctx->attr, '\0', ctx->attr_size);

          slapi_ch_free_string(&buf);

  

          return filter_len;
@@ -402,13 +422,15 @@

  {

      struct filter_ctx ctx = {0};

      va_list args;

+     char attr_static[ATTRSIZE] = {0};

      char *buf;

      int rc;

  

      buf = slapi_ch_calloc(sizeof(char), FILTER_BUF + 1);

      ctx.buf = buf;

-     memset(ctx.attr,'\0', ATTRSIZE);

      ctx.attr_position = 0;

+     ctx.attr = attr_static;

+     ctx.attr_size = ATTRSIZE;

      ctx.attr_found = 0;

      ctx.buf_len = FILTER_BUF;

      ctx.buf_size = 0;
@@ -424,6 +446,10 @@

      }

      va_end(args);

  

+     if (ctx.attr_size > ATTRSIZE) {

+         slapi_ch_free_string(&ctx.attr);

+     }

+ 

      return ctx.buf;

  }

  

file modified
+1 -1
@@ -887,7 +887,7 @@

  

  	/* Offset between UUID formatted times and time() formatted times.

         UUID UTC base time is October 15, 1582. time() base time is January 1, 1970.*/

- 	*uuid_time = cur_time * SEQ_PER_SEC + I64(0x01B21DD213814000);

+     *uuid_time = (uuid_time_t)cur_time * SEQ_PER_SEC + I64(0x01B21DD213814000);

  }

  

  /* ONREPL */

file modified
+104 -71
@@ -741,7 +741,10 @@

      size_t i = 0;

      size_t j = 0;

      int nextValue = 0;

+     int nv = 0;

      int numValues = 0;

+     Slapi_Value **va2 = NULL;

+     size_t *sorted2 = NULL;

  

      /* Loop over all the values freeing the old ones. */

      for(i = 0; i < vs->num; i++)
@@ -752,91 +755,122 @@

          } else {

              j = i;

          }

-         csnset_purge(&(vs->va[j]->v_csnset),csn);

-         if (vs->va[j]->v_csnset == NULL) {

-             slapi_value_free(&vs->va[j]);

-             vs->va[j] = NULL;

-         } else if (vs->va[j] != NULL) {

-             /* This value survived, we should count it. */

-             numValues++;

+         if (vs->va[j]) {

+             csnset_purge(&(vs->va[j]->v_csnset),csn);

+             if (vs->va[j]->v_csnset == NULL) {

+                 slapi_value_free(&vs->va[j]);

+                 /* Set the removed value to NULL so we know later to skip it */

+                 vs->va[j] = NULL;

+                 if (vs->sorted) {

+                     /* Mark the value in sorted for removal */

+                     vs->sorted[i] = -1;

+                 }

+             } else {

+                 /* This value survived, we should count it. */

+                 numValues++;

+             }

          }

      }

  

-     /* Now compact the value/sorted list. */

+     /* Compact vs->va and vs->sorted only when there're

+      * remaining values ie: numValues is greater than 0 */

      /*

-      * Because we want to preserve the sorted array, this is complicated.

+      * Algorithm explination: We start with a pair of arrays - the attrs, and the sorted array that provides

+      * a lookup into it:

+      *

+      * va: [d e a c b] sorted: [2 4 3 0 1]

+      *

+      * When we remove the element b, we NULL it, and we have to mark the place where it "was" as a -1 to

+      * flag it's removal.

+      *

+      * va: [d e a c NULL] sorted: [2 -1 3 0 1]

+      *

+      * Now a second va is created with the reduced allocation,

+      *

+      * va2: [ X X X X ] ....

       *

-      *  We have an array of values:

-      *  [ b, a, c, NULL, e, NULL, NULL, d]

-      *  And an array of indicies that are sorted.

-      *  [ 1, 0, 2, 7, 4, 3, 5, 6 ]

-      *  Were we to iterate over the sorted array, we get refs to the values in

-      * some order.

-      *  The issue is now we must *remove* from both the values *and* the sorted.

+      * Now we loop over sorted, skipping -1 that we find. In a new counter we create new sorted

+      * references, and move the values compacting them in the process.

+      * va: [d e a c NULL]

+      * va2: [a x x x]

+      * sorted: [_0 -1 3 0 1]

       *

-      * Previously, we just discarded this, because too hard. Now we try to keep

-      * it. The issue is that this is surprisingly hard to actually keep in

-      * sync.

+      * Looping a few more times would yield:

       *

-      * We can't just blindly move the values down: That breaks the sorted array

-      * and we would need to iterate over the sorted array multiple times to

-      * achieve this.

+      * va2: [a c x x]

+      * sorted: [_0 _1 3 0 1]

+      *

+      * va2: [a c d x]

+      * sorted: [_0 _1 _2 0 1]

+      *

+      * va2: [a c d e]

+      * sorted: [_0 _1 _2 _3 1]

+      *

+      * Not only does this sort va, but with sorted, we have a faster lookup, and it will benefit cache

+      * lookup.

       *

-      * It's actually going to be easier to just ditch the sorted, compact vs

-      * and then qsort the array.

       */

+     if (numValues > 0) {

+         if(vs->sorted) {

+             /* Let's allocate va2 and sorted2 */

+             va2 = (Slapi_Value **) slapi_ch_malloc( (numValues + 1) * sizeof(Slapi_Value *));

+             sorted2 = (size_t *) slapi_ch_malloc( (numValues + 1)* sizeof(size_t));

+         }

  

-     j = 0;

-     while (nextValue < numValues && j < vs->num)

-     {

-         /* nextValue is what we are looking at now

-          * j tracks along the array getting next elements.

-          *

-          *  [ b, a, c, NULL, e, NULL, NULL, d]

-          *             ^nv   ^j

-          *  [ b, a, c, e, NULL, NULL, NULL, d]

-          *             ^nv ^j

-          *  [ b, a, c, e, NULL, NULL, NULL, d]

-          *                ^nv    ^j

-          *  [ b, a, c, e, NULL, NULL, NULL, d]

-          *                ^nv         ^j

-          *  [ b, a, c, e, NULL, NULL, NULL, d]

-          *                ^nv               ^j

-          *  [ b, a, c, e, d, NULL, NULL, NULL]

-          *                ^nv              ^j

-          */

-         if (vs->va[nextValue] == NULL) {

-             /* Advance j till we find something */

-             while (vs->va[j] == NULL) {

-                 j++;

+         /* I is the index for the *new* va2 array */

+         for(i=0; i<vs->num; i++) {

+             if (vs->sorted) {

+                 /* Skip any removed values from the index */

+                 while((nv < vs->num) && (-1 == vs->sorted[nv])) {

+                     nv++;

+                 }

+                 /* We have a remaining value, add it to the va */

+                 if(nv < vs->num) {

+                     va2[i] = vs->va[vs->sorted[nv]];

+                     sorted2[i] = i;

+                     nv++;

+                 }

+             } else {

+                 while((nextValue < vs->num) && (NULL == vs->va[nextValue])) {

+                     nextValue++;

+                 }

+ 

+                 if(nextValue < vs->num) {

+                     vs->va[i] = vs->va[nextValue];

+                     nextValue++;

+                 } else {

+                     break;

+                 }

              }

-             /* We have something! */

-             vs->va[nextValue] = vs->va[j];

+         }

+ 

+         if (vs->sorted) {

+             /* Finally replace the valuearray and adjust num, max */

+             slapi_ch_free((void **)&vs->va);

+             slapi_ch_free((void **)&vs->sorted);

+             vs->va = va2;

+             vs->sorted = sorted2;

+             vs->num = numValues;

+             vs->max = vs->num + 1;

+         } else {

+             vs->num = numValues;

+         }

+ 

+         for (j = vs->num; j < vs->max; j++) {

              vs->va[j] = NULL;

+             if (vs->sorted) {

+                 vs->sorted[j] = -1;

+             }

          }

-         nextValue++;

-     }

-     /* Fix up the number of values */

-     vs->num = numValues;

-     /* Should we re-alloc values to be smaller? */

-     /* Other parts of DS are lazy. Lets clean our list */

-     for (j = vs->num; j < vs->max; j++) {

-         vs->va[j] = NULL;

+     } else {

+         slapi_valueset_done(vs);

      }

  

-     /* All the values were deleted, we can discard the whole array. */

-     if(vs->num == 0) {

-         if(vs->sorted) {

-             slapi_ch_free ((void **)&vs->sorted);

-         }

-         slapi_ch_free ((void **)&vs->va);

-         vs->va = NULL;

-         vs->max = 0;

-     } else if (vs->sorted != NULL) {

-         /* We still have values! rebuild the sorted array */

+     /* We still have values but not sorted array! rebuild it */

+     if(vs->num > VALUESET_ARRAY_SORT_THRESHOLD && vs->sorted == NULL) {

+         vs->sorted = (size_t *) slapi_ch_malloc( vs->max* sizeof(size_t));

          valueset_array_to_sorted(a, vs);

      }

- 

  #ifdef DEBUG

      PR_ASSERT(vs->num == 0 || (vs->num > 0 && vs->va[0] != NULL));

      size_t index = 0;
@@ -847,7 +881,6 @@

          PR_ASSERT(vs->va[index] == NULL);

      }

  #endif

- 

      /* return the number of remaining values */

      return numValues;

  }
@@ -1054,11 +1087,11 @@

      while (1) {

          do {

              i++;

-         } while ( valueset_value_cmp(a, vs->va[vs->sorted[i]], vs->va[pivot]) < 0);

+         } while (i < vs->max && valueset_value_cmp(a, vs->va[vs->sorted[i]], vs->va[pivot]) < 0);

  

          do {

              j--;

-         } while ( valueset_value_cmp(a, vs->va[vs->sorted[j]], vs->va[pivot]) > 0);

+         } while (valueset_value_cmp(a, vs->va[vs->sorted[j]], vs->va[pivot]) > 0 && j > 0);

  

          if (i >= j) {

              break;

file modified
+81 -51
@@ -1533,10 +1533,12 @@

  typedef struct _vattr_sp vattr_sp;

  

  /* Service provider handle */

- struct _vattr_sp_handle {

- 	vattr_sp *sp;

- 	struct _vattr_sp_handle *next; /* So we can link them together in the map */

- 	void *hint; /* Hint to the SP */

+ struct _vattr_sp_handle

+ {

+     vattr_sp *sp;

+     struct _vattr_sp_handle *next; /* So we can link them together in the map */

+     void *hint;                    /* Hint to the SP */

+     uint64_t rc;

  };

  

  /* Calls made by Service Providers */
@@ -1763,7 +1765,7 @@

  

   */

  

- #define VARRT_MAP_HASHTABLE_SIZE 10

+ #define VARRT_MAP_HASHTABLE_SIZE 32

  

  /* Attribute map oject */

  /* Needs to contain: a linked list of pointers to provider handles handles,
@@ -1848,8 +1850,18 @@

  	return 0;

  }

  

- void vattr_map_entry_free(vattr_map_entry *vae) {

-     slapi_ch_free((void **)&(vae->sp_list));

+ void

+ vattr_map_entry_free(vattr_map_entry *vae)

+ {

+     vattr_sp_handle *list_entry = vae->sp_list;

+     while (list_entry != NULL) {

+         vattr_sp_handle *next_entry = list_entry->next;

+         if (slapi_atomic_decr_64(&(list_entry->rc), __ATOMIC_RELAXED) == 0) {

+             /* Only free on RC 0 */

+             slapi_ch_free((void **)&list_entry);

+         }

+         list_entry = next_entry;

+     }

      slapi_ch_free_string(&(vae->type_name));

      slapi_ch_free((void **)&vae);

  }
@@ -2143,16 +2155,9 @@

  

  vattr_map_entry *vattr_map_entry_new(char *type_name, vattr_sp_handle *sph, void* hint)

  {

- 	vattr_map_entry *result = NULL;

- 	vattr_sp_handle *sp_copy = NULL;

- 

- 	sp_copy = (vattr_sp_handle*)slapi_ch_calloc(1, sizeof (vattr_sp_handle));

- 	sp_copy->sp = sph->sp;

- 	sp_copy->hint = hint;

- 

- 	result = (vattr_map_entry*)slapi_ch_calloc(1, sizeof (vattr_map_entry));

- 	result->type_name = slapi_ch_strdup(type_name);

- 	result->sp_list = sp_copy;

+     vattr_map_entry *result = (vattr_map_entry *)slapi_ch_calloc(1, sizeof(vattr_map_entry));

+     result->type_name = slapi_ch_strdup(type_name);

+     result->sp_list = sph;

  

  	/* go get schema */

  	result->objectclasses = vattr_map_entry_build_schema(type_name);
@@ -2268,40 +2273,65 @@

  So any SP which relinquishes its need to handle a type needs to continue

  to handle the calls on it, but return nothing */

  /* DBDB need to sort out memory ownership here, it's not quite right */

- 

- int vattr_map_sp_insert(char *type_to_add, vattr_sp_handle *sp, void *hint)

- {

- 	int result = 0;

- 	vattr_map_entry *map_entry = NULL;

- 	/* Is this type already there ? */

- 	result = vattr_map_lookup(type_to_add,&map_entry);

- 	/* If it is, add this SP to the list, safely even if readers are traversing the list at the same time */

- 	if (0 == result) {

- 		int found = 0;

- 		vattr_sp_handle *list_entry = NULL;

- 		/* Walk the list checking that the daft SP isn't already here */

- 		for (list_entry = map_entry->sp_list ; list_entry; list_entry = list_entry->next) {

- 			if (list_entry == sp) {

- 				found = 1;

- 				break;

- 			}

- 		}

- 		/* If it is, we do nothing */

- 		if(found) {

- 			return 0;

- 		}

- 		/* We insert the SP handle into the linked list at the head */

- 		sp->next = map_entry->sp_list;

- 		map_entry->sp_list = sp;

- 	} else {

- 	/* If not, add it */

- 		map_entry = vattr_map_entry_new(type_to_add,sp,hint);

- 		if (NULL == map_entry) {

- 			return ENOMEM;

- 		}

- 		return vattr_map_insert(map_entry);

- 	}

- 	return 0;

+ /*

+  * This function was inconsistent. We would allocated and "kind of",

+  * copy the sp_handle here for the vattr_map_entry_new path. But we

+  * would "take ownership" for the existing entry and the list addition

+  * path. Instead now, EVERY sp_handle we take, we take ownership of

+  * and the CALLER must allocate a new one each time.

+  *

+  * Better idea, is that regattr should just take the fn pointers

+  * and callers never *see* the sp_handle structure at all.

+  *

+  * This leaves us with some quirks today. First: if you have plugin A

+  * and B, A registers attr 1 and B 1 and 2, it's possible that if you

+  * register A1 first, then B1, you have B->A in next. Then when you

+  * register B2, because we take 0==result from map_lookup, we add sp

+  * "as is" to the map. This means that B2 now has the same next to A1

+  * handle. This won't add a bug, because A1 won't be able to service the

+  * attr, but it could cause some head scratching ...

+  *

+  * Again, to fix this, the whole vattr external interface needs a

+  * redesign ... :(

+  */

+ int

+ vattr_map_sp_insert(char *type_to_add, vattr_sp_handle *sp, void *hint)

+ {

+     int result = 0;

+     vattr_map_entry *map_entry = NULL;

+     /* Is this type already there ? */

+     result = vattr_map_lookup(type_to_add, &map_entry);

+     /* If it is, add this SP to the list, safely even if readers are traversing the list at the same time */

+     if (0 == result) {

+         int found = 0;

+         vattr_sp_handle *list_entry = NULL;

+         /* Walk the list checking that the daft SP isn't already here */

+         for (list_entry = map_entry->sp_list; list_entry; list_entry = list_entry->next) {

+             if (list_entry == sp) {

+                 found = 1;

+                 break;

+             }

+         }

+         /* If it is, we do nothing */

+         if (found) {

+             return 0;

+         }

+         /* Increase the ref count of the sphandle */

+         slapi_atomic_incr_64(&(sp->rc), __ATOMIC_RELAXED);

+         /* We insert the SP handle into the linked list at the head */

+         sp->next = map_entry->sp_list;

+         map_entry->sp_list = sp;

+     } else {

+         /* If not, add it */

+         /* Claim a reference on the sp ... */

+         slapi_atomic_incr_64(&(sp->rc), __ATOMIC_RELAXED);

+         map_entry = vattr_map_entry_new(type_to_add, sp, hint);

+         if (NULL == map_entry) {

+             return ENOMEM;

+         }

+         return vattr_map_insert(map_entry);

+     }

+     return 0;

  }

  

  /*

file modified
+1 -1
@@ -47,7 +47,7 @@

  The backend database type (default: ldbm database).

  .TP

  .B \fB\-a\fR \fIbackupdir\fR

- The directory where the backup should be stored.

+ The directory where the backup should be stored.  This directory is a symbolic link to the actual backup files located under "nsslapd-bakdir" directory that is set in the "cn=config" entry.

  .TP

  .B \fB\-A\fR \fIbackupdir\fR

  This is similar to \fB-a\fR, except that a sub-directory of \fIbackupdir\fR will be created for the backup, and the name of the sub-directory will be a timestamp of the form \fIserver-instance-date_time\fR.

file modified
+5 -1
@@ -18,7 +18,7 @@

  .SH NAME 

  vlvindex - Directory Server script for VLV indexing

  .SH SYNOPSIS

- vlvindex [\-Z serverID] \-n backendname | {\-s includesuffix}* \-T vlvTag [\-d debuglevel] [\-h]

+ vlvindex [\-Z serverID] \-n backendname | {\-s includesuffix}* \-T vlvTag [\-d debuglevel] [\-v] [\-h]

  .SH DESCRIPTION

  Creates virtual list view (VLV) indexes, known in the Directory Server Console as browsing indexes. VLV indexes introduce flexibility in the way search results are viewed.  VLV index configuration must already exist prior to running this script. The Directory Server must be stopped before running this script.

  .SH OPTIONS
@@ -40,6 +40,10 @@

  .B \fB\-d\fR \fIDebug Level\fR

  Settings the debugging level.

  .TP

+ .B \fB\-v\fR

+ .br

+ Display the version.

+ .TP

  .B \fB\-h\fR

  .br

  Display the usage.

@@ -32,31 +32,21 @@

  #include <inttypes.h>

  #include <stddef.h> /* for size_t */

  

- #if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \

-     __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__

- #  define _le64toh(x) ((uint64_t)(x))

- #elif defined(_WIN32)

- /* Windows is always little endian, unless you're on xbox360

-    http://msdn.microsoft.com/en-us/library/b0084kay(v=vs.80).aspx */

- #  define _le64toh(x) ((uint64_t)(x))

- #elif defined(__APPLE__)

- #  include <libkern/OSByteOrder.h>

- #  define _le64toh(x) OSSwapLittleToHostInt64(x)

+ #include <config.h>

+ 

+ #if defined(HAVE_SYS_ENDIAN_H)

+ #  include <sys/endian.h>

+ #elif defined(HAVE_ENDIAN_H)

+ #  include <endian.h>

  #else

+ #  error platform header for endian detection not found.

+ #endif

  

  /* See: http://sourceforge.net/p/predef/wiki/Endianness/ */

- #  if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)

- #    include <sys/endian.h>

- #  else

- #    include <endian.h>

- #  endif

- #  if defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \

-     __BYTE_ORDER == __LITTLE_ENDIAN

- #    define _le64toh(x) ((uint64_t)(x))

- #  else

- #    define _le64toh(x) le64toh(x)

- #  endif

- 

+ #if defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN

+ #  define _le64toh(x) ((uint64_t)(x))

+ #else

+ #  define _le64toh(x) le64toh(x)

  #endif

  

  

@@ -14,6 +14,14 @@

  

  #include "test_sds.h"

  

+ #if defined(HAVE_SYS_ENDIAN_H)

+ #  include <sys/endian.h>

+ #elif defined(HAVE_ENDIAN_H)

+ #  include <endian.h>

+ #else

+ #  error platform header for endian detection not found.

+ #endif

+ 

  static void

  test_siphash(void **state __attribute__((unused))) {

  
@@ -26,7 +34,7 @@

      uint64_t test_b = 13042610424265326907;

  

      // Initial simple test

-     value = 5;

+     value = htole64(5);

      hashout = sds_siphash13(&value, sizeof(uint64_t), key);

      assert_true(hashout == test_a);

  

@@ -47,6 +47,9 @@

  /* For signaling tests */

  #include <signal.h>

  

+ /* NS requries inttypes.h */

+ #include <inttypes.h>

+ 

  /* For NS itself */

  #include <nunc-stans.h>

  /* We need the internal headers for state checks */

@@ -16,6 +16,8 @@

  #include <setjmp.h>

  #include <cmocka.h>

  

+ #include <inttypes.h>

+ 

  #include <nunc-stans.h>

  

  #include <stdio.h>

@@ -33,6 +33,11 @@

      slapi_pal_meminfo *mi = spal_meminfo_get();

      uint64_t request = 0;

      mi->system_available_bytes = 0;

+     /*

+      * PPC 64 has a large page size, so we trigger the 16 page min.

+      * As a result, we set the pagesize to 4k just for this test.

+      */

+     mi->pagesize_bytes = 4096;

      assert_true(util_is_cachesize_sane(mi, &request) == UTIL_CACHESIZE_ERROR);

  

      // Set the values to known quantities
@@ -47,6 +52,16 @@

      assert_true(util_is_cachesize_sane(mi, &request) == UTIL_CACHESIZE_REDUCED);

      assert_true(request <= 75000);

  

+     /*

+      * we need to test the minimum reduction function. We make a request that gets

+      * reduced, but falls under the threshold of 16 * page. So we expect the result

+      * to be 16 * pagesize.

+      */

+     mi->system_available_bytes = 65536;

+     request = 65537;

+     assert_true(util_is_cachesize_sane(mi, &request) == UTIL_CACHESIZE_REDUCED);

+     assert_true(request == (16 * mi->pagesize_bytes));

+ 

      spal_meminfo_destroy(mi);

  }

  

@@ -8,7 +8,7 @@

  

  #include "../../test_slapd.h"

  

- #include <nss3/nss.h>

+ #include <nss.h>

  #include <pwdstorage.h>

  

  int