]> git.sur5r.net Git - openldap/commitdiff
Introduction of a new Berkeley DB version 2 (!) specific backend.
authorKurt Spanier <ksp@openldap.org>
Fri, 5 Feb 1999 16:23:03 +0000 (16:23 +0000)
committerKurt Spanier <ksp@openldap.org>
Fri, 5 Feb 1999 16:23:03 +0000 (16:23 +0000)
BEWARE: the backend will be compiled.
        the backend will NOT be invoked, yet.
        the backend CANNOT be invoked, yet, because it is NOT yet
            integrated into the new initialization/startup environment
            of the slapd server.

36 files changed:
servers/slapd/back-bdb2/Makefile.in [new file with mode: 0644]
servers/slapd/back-bdb2/Version.c [new file with mode: 0644]
servers/slapd/back-bdb2/abandon.c [new file with mode: 0644]
servers/slapd/back-bdb2/add.c [new file with mode: 0644]
servers/slapd/back-bdb2/alias.c [new file with mode: 0644]
servers/slapd/back-bdb2/attr.c [new file with mode: 0644]
servers/slapd/back-bdb2/back-bdb2.h [new file with mode: 0644]
servers/slapd/back-bdb2/bind.c [new file with mode: 0644]
servers/slapd/back-bdb2/cache.c [new file with mode: 0644]
servers/slapd/back-bdb2/close.c [new file with mode: 0644]
servers/slapd/back-bdb2/compare.c [new file with mode: 0644]
servers/slapd/back-bdb2/config.c [new file with mode: 0644]
servers/slapd/back-bdb2/dbcache.c [new file with mode: 0644]
servers/slapd/back-bdb2/delete.c [new file with mode: 0644]
servers/slapd/back-bdb2/dn2id.c [new file with mode: 0644]
servers/slapd/back-bdb2/external.h [new file with mode: 0644]
servers/slapd/back-bdb2/filterindex.c [new file with mode: 0644]
servers/slapd/back-bdb2/group.c [new file with mode: 0644]
servers/slapd/back-bdb2/id2children.c [new file with mode: 0644]
servers/slapd/back-bdb2/id2entry.c [new file with mode: 0644]
servers/slapd/back-bdb2/idl.c [new file with mode: 0644]
servers/slapd/back-bdb2/index.c [new file with mode: 0644]
servers/slapd/back-bdb2/init.c [new file with mode: 0644]
servers/slapd/back-bdb2/kerberos.c [new file with mode: 0644]
servers/slapd/back-bdb2/modify.c [new file with mode: 0644]
servers/slapd/back-bdb2/modrdn.c [new file with mode: 0644]
servers/slapd/back-bdb2/nextid.c [new file with mode: 0644]
servers/slapd/back-bdb2/porter.c [new file with mode: 0644]
servers/slapd/back-bdb2/proto-back-bdb2.h [new file with mode: 0644]
servers/slapd/back-bdb2/search.c [new file with mode: 0644]
servers/slapd/back-bdb2/startup.c [new file with mode: 0644]
servers/slapd/back-bdb2/timing.c [new file with mode: 0644]
servers/slapd/back-bdb2/txn.c [new file with mode: 0644]
servers/slapd/back-bdb2/txn.h [new file with mode: 0644]
servers/slapd/back-bdb2/unbind.c [new file with mode: 0644]
servers/slapd/back-ldbm/alias.c

diff --git a/servers/slapd/back-bdb2/Makefile.in b/servers/slapd/back-bdb2/Makefile.in
new file mode 100644 (file)
index 0000000..b2ca6b4
--- /dev/null
@@ -0,0 +1,38 @@
+XSRCS = version.c
+SRCS   = idl.c add.c search.c cache.c dbcache.c dn2id.c id2entry.c \
+               index.c id2children.c nextid.c abandon.c compare.c group.c \
+               modify.c modrdn.c delete.c init.c config.c bind.c attr.c \
+               filterindex.c unbind.c kerberos.c close.c alias.c startup.c \
+               timing.c porter.c txn.c
+OBJS   = idl.o add.o search.o cache.o dbcache.o dn2id.o id2entry.o \
+               index.o id2children.o nextid.o abandon.o compare.o group.o \
+               modify.o modrdn.o delete.o init.o config.o bind.o attr.o \
+               filterindex.o unbind.o kerberos.o close.o alias.o startup.o \
+               timing.o porter.o txn.o
+
+LDAP_INCDIR= ../../../include       
+LDAP_LIBDIR= ../../../libraries
+
+BUILD_OPT = "--enable-bdb2"
+BUILD_SRV = @BUILD_BDB2@
+
+XINCPATH = -I.. -I$(srcdir)/..
+
+PROGRAMS = libback-bdb2.a
+
+all-local-srv: FORCE
+       $(MAKE) $(MFLAGS) libback-bdb2.a
+
+libback-bdb2.a:        version.o
+       $(AR) ruv $@ $(OBJS) version.o
+       @$(RANLIB) $@
+       @touch ../.backend
+
+version.c:     $(OBJS) $(LDAP_LIBDEPEND)
+       $(RM) $@
+       (u=$${USER-root} v=`$(CAT) $(VERSIONFILE)` d=`$(PWD)` \
+        h=`$(HOSTNAME)` t=`$(DATE)`; \
+        $(SED) -e "s|%WHEN%|$${t}|" \
+               -e "s|%WHOANDWHERE%|$${u}@$${h}:$${d}|" \
+               -e "s|%VERSION%|$${v}|" \
+               < $(srcdir)/Version.c > $@)
diff --git a/servers/slapd/back-bdb2/Version.c b/servers/slapd/back-bdb2/Version.c
new file mode 100644 (file)
index 0000000..6231e05
--- /dev/null
@@ -0,0 +1,13 @@
+/*
+ * Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+static char Versionstr[] = "  bdb2 backend %VERSION% (%WHEN%)\n\t%WHOANDWHERE%\n";
diff --git a/servers/slapd/back-bdb2/abandon.c b/servers/slapd/back-bdb2/abandon.c
new file mode 100644 (file)
index 0000000..1d9c1e9
--- /dev/null
@@ -0,0 +1,55 @@
+/* abandon.c - ldbm backend abandon routine */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#include <ac/string.h>
+
+#include "slap.h"
+#include "back-bdb2.h"
+
+
+/*ARGSUSED*/
+static int
+bdb2i_back_abandon_internal(
+       Backend    *be,
+       Connection *c,
+       Operation  *o,
+       int        msgid )
+{
+       return 0;
+}
+
+
+int
+bdb2_back_abandon(
+       Backend    *be,
+       Connection *c,
+       Operation  *o,
+       int        msgid )
+{
+       struct timeval  time1, time2;
+       char   *elapsed_time;
+       int    ret;
+
+       gettimeofday( &time1, NULL );
+
+       ret = bdb2i_back_abandon_internal( be, c, o, msgid );
+
+       if ( bdb2i_do_timing ) {
+
+               gettimeofday( &time2, NULL);
+               elapsed_time = bdb2i_elapsed( time1, time2 );
+               Debug( LDAP_DEBUG_ANY, "ABND elapsed=%s\n",
+                               elapsed_time, 0, 0 );
+               free( elapsed_time );
+
+       }
+
+       return( ret );
+}
+
+
diff --git a/servers/slapd/back-bdb2/add.c b/servers/slapd/back-bdb2/add.c
new file mode 100644 (file)
index 0000000..ef3b103
--- /dev/null
@@ -0,0 +1,289 @@
+/* add.c - ldap bdb2 back-end add routine */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "back-bdb2.h"
+#include "proto-back-bdb2.h"
+
+static int
+bdb2i_back_add_internal(
+    Backend    *be,
+    Connection *conn,
+    Operation  *op,
+    Entry      *e
+)
+{
+       struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+       char            *pdn;
+       Entry           *p = NULL;
+       int                     rootlock = 0;
+       int                     rc = -1; 
+
+       Debug(LDAP_DEBUG_ARGS, "==> bdb2i_back_add: %s\n", e->e_dn, 0, 0);
+
+       /* nobody else can add until we lock our parent */
+       ldap_pvt_thread_mutex_lock(&li->li_add_mutex);
+
+       if ( ( bdb2i_dn2id( be, e->e_ndn ) ) != NOID ) {
+               ldap_pvt_thread_mutex_unlock(&li->li_add_mutex);
+               entry_free( e );
+               send_ldap_result( conn, op, LDAP_ALREADY_EXISTS, "", "" );
+               return( -1 );
+       }
+
+       if ( global_schemacheck && oc_schema_check( e ) != 0 ) {
+               ldap_pvt_thread_mutex_unlock(&li->li_add_mutex);
+
+               Debug( LDAP_DEBUG_TRACE, "entry failed schema check\n",
+                       0, 0, 0 );
+
+               entry_free( e );
+               send_ldap_result( conn, op, LDAP_OBJECT_CLASS_VIOLATION, "",
+                   "" );
+               return( -1 );
+       }
+
+       /*
+        * Get the parent dn and see if the corresponding entry exists.
+        * If the parent does not exist, only allow the "root" user to
+        * add the entry.
+        */
+
+       if ( (pdn = dn_parent( be, e->e_ndn )) != NULL ) {
+               char *matched = NULL;
+
+               /* get parent with writer lock */
+               if ( (p = bdb2i_dn2entry_w( be, pdn, &matched )) == NULL ) {
+                       ldap_pvt_thread_mutex_unlock(&li->li_add_mutex);
+                       Debug( LDAP_DEBUG_TRACE, "parent does not exist\n", 0,
+                           0, 0 );
+                       send_ldap_result( conn, op, LDAP_NO_SUCH_OBJECT,
+                           matched, "" );
+
+                       if ( matched != NULL ) {
+                               free( matched );
+                       }
+
+                       entry_free( e );
+                       free( pdn );
+                       return -1;
+               }
+
+               /* don't need the add lock anymore */
+               ldap_pvt_thread_mutex_unlock(&li->li_add_mutex);
+
+               free(pdn);
+
+               if ( matched != NULL ) {
+                       free( matched );
+               }
+
+               if ( ! access_allowed( be, conn, op, p,
+                       "children", NULL, ACL_WRITE ) )
+               {
+                       Debug( LDAP_DEBUG_TRACE, "no access to parent\n", 0,
+                           0, 0 );
+                       send_ldap_result( conn, op, LDAP_INSUFFICIENT_ACCESS,
+                           "", "" );
+
+                       /* free parent and writer lock */
+                       bdb2i_cache_return_entry_w( &li->li_cache, p ); 
+
+                       entry_free( e );
+                       return -1;
+               }
+
+       } else {
+               /* no parent, must be adding entry to root */
+               if ( ! be_isroot( be, op->o_ndn ) ) {
+                       ldap_pvt_thread_mutex_unlock(&li->li_add_mutex);
+                       Debug( LDAP_DEBUG_TRACE, "no parent & not root\n", 0,
+                           0, 0 );
+                       send_ldap_result( conn, op, LDAP_INSUFFICIENT_ACCESS,
+                           "", "" );
+
+                       entry_free( e );
+                       return -1;
+               }
+
+               /*
+                * no parent, acquire the root write lock
+                * and release the add lock.
+                */
+               ldap_pvt_thread_mutex_lock(&li->li_root_mutex);
+               rootlock = 1;
+               ldap_pvt_thread_mutex_unlock(&li->li_add_mutex);
+       }
+
+       /* acquire required reader/writer lock */
+       if (entry_rdwr_lock(e, 1)) {
+               if( p != NULL) {
+                       /* free parent and writer lock */
+                       bdb2i_cache_return_entry_w( &li->li_cache, p ); 
+               }
+
+               if ( rootlock ) {
+                       /* release root lock */
+                       ldap_pvt_thread_mutex_unlock(&li->li_root_mutex);
+               }
+
+               Debug( LDAP_DEBUG_ANY, "add: could not lock entry\n",
+                       0, 0, 0 );
+
+               entry_free(e);
+
+               send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR, "", "" );
+               return( -1 );
+       }
+
+       e->e_id = bdb2i_next_id( be );
+
+       /*
+        * Try to add the entry to the cache, assign it a new dnid
+        * This should only fail if the entry already exists.
+        */
+
+       if ( bdb2i_cache_add_entry_lock( &li->li_cache, e, ENTRY_STATE_CREATING )
+                                                               != 0 ) {
+               if( p != NULL) {
+                       /* free parent and writer lock */
+                       bdb2i_cache_return_entry_w( &li->li_cache, p ); 
+               }
+               if ( rootlock ) {
+                       /* release root lock */
+                       ldap_pvt_thread_mutex_unlock(&li->li_root_mutex);
+               }
+
+               Debug( LDAP_DEBUG_ANY, "cache_add_entry_lock failed\n", 0, 0,
+                   0 );
+               bdb2i_next_id_return( be, e->e_id );
+                
+               entry_rdwr_unlock(e, 1);
+               entry_free( e );
+
+               send_ldap_result( conn, op, LDAP_ALREADY_EXISTS, "", "" );
+               return( -1 );
+       }
+
+       /*
+        * add it to the id2children index for the parent
+        */
+
+       if ( bdb2i_id2children_add( be, p, e ) != 0 ) {
+               Debug( LDAP_DEBUG_TRACE, "bdb2i_id2children_add failed\n", 0,
+                   0, 0 );
+               send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR, "", "" );
+
+               goto return_results;
+       }
+
+       /*
+        * Add the entry to the attribute indexes, then add it to
+        * the id2children index, dn2id index, and the id2entry index.
+        */
+
+       /* attribute indexes */
+       if ( bdb2i_index_add_entry( be, e ) != 0 ) {
+               Debug( LDAP_DEBUG_TRACE, "bdb2i_index_add_entry failed\n", 0,
+                   0, 0 );
+               send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR, "", "" );
+
+               goto return_results;
+       }
+
+       /* dn2id index */
+       if ( bdb2i_dn2id_add( be, e->e_ndn, e->e_id ) != 0 ) {
+               Debug( LDAP_DEBUG_TRACE, "bdb2i_dn2id_add failed\n", 0,
+                   0, 0 );
+               send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR, "", "" );
+
+               goto return_results;
+       }
+
+       /* id2entry index */
+       if ( bdb2i_id2entry_add( be, e ) != 0 ) {
+               Debug( LDAP_DEBUG_TRACE, "bdb2i_id2entry_add failed\n", 0,
+                   0, 0 );
+               (void) bdb2i_dn2id_delete( be, e->e_ndn );
+               send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR, "", "" );
+
+               goto return_results;
+       }
+
+       send_ldap_result( conn, op, LDAP_SUCCESS, "", "" );
+       rc = 0;
+
+return_results:;
+       if (p != NULL) {
+               /* free parent and writer lock */
+               bdb2i_cache_return_entry_w( &li->li_cache, p ); 
+
+       }
+
+       if ( rootlock ) {
+               /* release root lock */
+               ldap_pvt_thread_mutex_unlock(&li->li_root_mutex);
+       }
+
+       bdb2i_cache_set_state( &li->li_cache, e, 0 );
+
+       /* free entry and writer lock */
+       bdb2i_cache_return_entry_w( &li->li_cache, e ); 
+
+       return( rc );
+}
+
+
+int
+bdb2_back_add(
+    Backend    *be,
+    Connection *conn,
+    Operation  *op,
+    Entry      *e
+)
+{
+       DB_LOCK  lock;
+       struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+
+       struct   timeval  time1, time2;
+       char     *elapsed_time;
+       int      ret;
+
+       gettimeofday( &time1, NULL );
+
+       if ( bdb2i_enter_backend_w( &li->li_db_env, &lock ) != 0 ) {
+
+               send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR, "", "" );
+               return( -1 );
+
+       }
+
+       /*  check, if a new default attribute index will be created,
+               in which case we have to open the index file BEFORE TP  */
+       if ( bdb2i_with_dbenv )
+               bdb2i_check_default_attr_index_add( li, e );
+
+       ret = bdb2i_back_add_internal( be, conn, op, e );
+
+       (void) bdb2i_leave_backend( &li->li_db_env, lock );
+
+       if ( bdb2i_do_timing ) {
+
+               gettimeofday( &time2, NULL);
+               elapsed_time = bdb2i_elapsed( time1, time2 );
+               Debug( LDAP_DEBUG_ANY, "conn=%d op=%d ADD elapsed=%s\n",
+                               conn->c_connid, op->o_opid, elapsed_time );
+               free( elapsed_time );
+
+       }
+
+       return( ret );
+}
+
+
diff --git a/servers/slapd/back-bdb2/alias.c b/servers/slapd/back-bdb2/alias.c
new file mode 100644 (file)
index 0000000..984ca56
--- /dev/null
@@ -0,0 +1,320 @@
+/*
+ * Copyright (c) 1998 Will Ballantyne, ITSD, Government of BC
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to ITSD, Government of BC. The name of ITSD
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <ac/socket.h>         /* Get struct sockaddr for slap.h */
+#include "slap.h"
+#include "back-bdb2.h"
+#include "proto-back-bdb2.h"
+
+/*
+ * given an alias object, dereference it to its end point.
+ * Entry returned has reader lock or is NULL.  Starting entry is not released.
+ */
+Entry *bdb2i_derefAlias_r ( Backend     *be,
+                   Connection  *conn,
+                   Operation   *op,
+                   Entry       *e)
+{
+  /* to free cache entries */
+  struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+  Attribute *a;
+  int       depth;
+  char      **pastAliases;
+  char      *matched;
+  Entry     *origDN = e;
+
+  if (!e) return NULL;  /* be sure we have a starting entry */
+
+  Debug( LDAP_DEBUG_TRACE, "<= checking for alias for dn %s\n", e->e_dn, 0, 0 );
+
+  /*
+   * try to deref fully, up to a maximum depth.  If the max depth exceeded
+   * then send an error
+   */
+  for ( depth = 0;
+       ( ( a = attr_find( e->e_attrs, "aliasedobjectname" ) ) != NULL) &&
+         ( depth < be->be_maxDerefDepth );
+       ++depth) 
+  {
+
+    /* 
+     * make sure there is a defined aliasedobjectname.  
+     * can only have one value so just use first value (0) in the attr list. 
+     */            
+    if (a->a_vals[0] && a->a_vals[0]->bv_val) {
+      char *newDN, *oldDN;
+
+      Debug( LDAP_DEBUG_TRACE, "<= %s is an alias for %s\n", 
+            e->e_dn, a->a_vals[0]->bv_val, 0 );
+      newDN = ch_strdup (a->a_vals[0]->bv_val);
+      oldDN = ch_strdup (e->e_ndn);
+
+      /* 
+       * release past lock if not original
+       */
+      if ( (depth > 0) && e ) {
+          bdb2i_cache_return_entry_r(&li->li_cache, e);        
+      }
+
+      /* make sure new and old DN are not same to avoid loops */
+      dn_normalize_case (newDN);
+      if ( strcmp (newDN, oldDN) == 0 ) {
+       
+               Debug( LDAP_DEBUG_TRACE, 
+               "<= %s alias is same as current %s\n", 
+               oldDN, newDN, 0 );
+               send_ldap_result( conn, op, LDAP_ALIAS_PROBLEM, "",
+                               "Circular alias" );
+               free (newDN);
+               free (oldDN);
+               break;
+      }
+
+      /* make sure new and original are not same to avoid deadlocks */
+      if ( strcmp (newDN, origDN->e_ndn) == 0 ) {
+               Debug( LDAP_DEBUG_TRACE, 
+               "<= %s alias is same as original %s\n", 
+               oldDN, origDN->e_ndn, 0 );
+               send_ldap_result( conn, op, LDAP_ALIAS_PROBLEM, "",
+                               "Circular alias" );
+               free (newDN);
+               free (oldDN);
+               break;
+      }
+
+      /*
+       * ok, so what happens if there is an alias in the DN of a dereferenced
+       * alias object?  
+       */
+      if ( (e = bdb2i_dn2entry_r( be, newDN, &matched )) == NULL ) {
+
+               /* could not deref return error  */
+               Debug( LDAP_DEBUG_TRACE, 
+               "<= %s is a dangling alias to %s\n", 
+               oldDN, newDN, 0 );
+               send_ldap_result( conn, op, LDAP_ALIAS_DEREF_PROBLEM, "",
+                               "Dangling Alias" );
+
+                       if(matched != NULL) free(matched);
+       free (newDN);
+       free (oldDN);
+               break;
+      }
+
+      free (newDN);
+      free (oldDN);
+    }
+    else {
+      /*
+       * there was an aliasedobjectname defined but no data.
+       * this can't happen, right?
+       */
+               Debug( LDAP_DEBUG_TRACE, 
+               "<= %s has no data in aliasedobjectname attribute\n", 
+               (e && e->e_dn) ? e->e_dn : "(null)", 0, 0 );
+               send_ldap_result( conn, op, LDAP_ALIAS_PROBLEM, "",
+                               "Alias missing aliasedobjectname" );
+               break;
+    }
+  }
+
+  /*
+   * warn if we pulled out due to exceeding the maximum deref depth
+   */
+  if ( depth >= be->be_maxDerefDepth ) {
+    Debug( LDAP_DEBUG_TRACE, 
+          "<= deref(\"%s\") exceeded maximum deref depth (%d) at \"%s\"\n", 
+          origDN->e_dn ? origDN->e_dn : "(null)",
+               be->be_maxDerefDepth,
+               (e && e->e_ndn) ? e->e_ndn : "(null)");
+    send_ldap_result( conn, op, LDAP_ALIAS_DEREF_PROBLEM, "",
+                       "Maximum alias dereference depth exceeded" );
+  }
+
+  return e;
+}
+
+/*
+ * given a DN fully deref it and return the real DN or original DN if it fails
+ * This involves finding the last matched part then reconstructing forward
+ * e.g. 
+ * ou=MyOU,o=MyAliasedOrg,c=MyCountry where o=MyAliasedOrg is an alias for o=MyOrg
+ * loop starts with newDN = ou=MyOU,o=MyAliasedOrg,c=MyCountry
+ *   dn2entry_r on newDN gives null entry and o=MyAliasedOrg,c=MyCountry matched
+ *   dn2entry_r on matched gives o=MyAliasedOrg,c=MyCountry entry
+ *   remainder is ou=MyOU
+ *   dereferencing o=MyAliasedOrg,c=MyCountry yields entry o=MyOrg,c=MyCountry
+ *   release lock on o=MyAliasedOrg,c=MyCountry entry
+ *   reconstructed dn is ou=MyOU,o=MyOrg,c=MyCountry
+ *   release lock on o=MyOrg,c=MyCountry entry
+ */
+char *bdb2i_derefDN ( Backend     *be,
+                Connection  *conn,
+                Operation   *op,
+                char        *dn
+)
+{
+  struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+  char         *matched = 0;
+  char         *newDN = NULL;
+  int  depth, i;
+  Entry        *eMatched;
+  Entry        *eDeref;
+  Entry         *eNew;
+  
+  if (!dn) return NULL;
+
+  Debug( LDAP_DEBUG_TRACE, 
+        "<= dereferencing dn: \"%s\"\n", 
+        dn, 0, 0 );
+  
+  newDN = ch_strdup ( dn );
+
+  /* while we don't have a matched dn, deref the DN */
+  for ( depth = 0;
+       ( (eMatched = bdb2i_dn2entry_r( be, newDN, &matched )) == NULL) &&
+         (depth < be->be_maxDerefDepth);
+       ++depth ) {
+    
+    if ((matched != NULL) && *matched) {       
+      char *submatch;
+      
+      /* 
+       * make sure there actually is an entry for the matched part 
+       */
+      if ( (eMatched = bdb2i_dn2entry_r( be, matched, &submatch )) != NULL) {
+       char  *remainder; /* part before the aliased part */
+       int  rlen = strlen(newDN) - strlen(matched);
+       
+       Debug( LDAP_DEBUG_TRACE, "<= matched %s\n", matched, 0, 0 );
+       
+       remainder = ch_malloc (rlen + 1);
+       strncpy ( remainder, newDN, rlen );
+       remainder[rlen] = '\0';
+       
+       Debug( LDAP_DEBUG_TRACE, "<= remainder %s\n", remainder, 0, 0 );
+       
+       if ((eNew = bdb2i_derefAlias_r( be, conn, op, eMatched )) == NULL) {
+         free (matched);
+         matched = NULL;
+         free (newDN);
+         newDN = NULL;
+         free (remainder);
+         remainder = NULL;
+
+               bdb2i_cache_return_entry_r(&li->li_cache, eMatched);
+               eMatched = NULL;
+         break; /*  no associated entry, dont deref */
+       }
+       else {
+
+         Debug( LDAP_DEBUG_TRACE, "<= l&g we have %s vs %s \n", matched, eNew->e_dn, 0 );
+
+         i = strcasecmp (matched, eNew->e_dn);
+          /* free reader lock */
+          bdb2i_cache_return_entry_r(&li->li_cache, eNew);
+
+               free (matched);
+               matched = NULL;
+
+         if (! i) {
+           /* newDN same as old so not an alias, no need to go further */
+           free (newDN);
+           newDN = NULL;
+           free (remainder);
+
+               bdb2i_cache_return_entry_r(&li->li_cache, eMatched);
+               eMatched = NULL;
+           break;
+         }
+
+         /* 
+          * we have dereferenced the aliased part so put
+          * the new dn together
+          */
+         free (newDN);
+         newDN = ch_malloc (strlen(eMatched->e_dn) + rlen + 1);
+         strcpy (newDN, remainder);
+         strcat (newDN, eMatched->e_dn);
+         Debug( LDAP_DEBUG_TRACE, "<= expanded to %s\n", newDN, 0, 0 );
+
+         free (remainder);
+       }
+        /* free reader lock */
+        bdb2i_cache_return_entry_r(&li->li_cache, eMatched);
+      }
+      else {
+       if(submatch != NULL) free(submatch);
+       break; /* there was no entry for the matched part */
+      }
+    }
+    else {
+      break; /* there was no matched part */
+    }
+  }
+  
+       /* release lock if a match terminated the loop, there should be no
+        * outstanding locks at this point
+        */
+  if(eMatched != NULL) {
+    /* free reader lock */
+    bdb2i_cache_return_entry_r(&li->li_cache, eMatched);
+  }
+
+  /*
+   * the final part of the DN might be an alias so try to dereference it.
+   * e.g. if we had started with dn = o=MyAliasedOrg,c=MyCountry the dn would match
+   * and the above loop complete but we would still be left with an aliased DN.
+   */
+  if ( (eNew = bdb2i_dn2entry_r( be, newDN, &matched )) != NULL) {
+    if ((eDeref = bdb2i_derefAlias_r( be, conn, op, eNew )) != NULL) {
+      free (newDN);
+      newDN = ch_strdup (eDeref->e_dn);
+      /* free reader lock */
+      bdb2i_cache_return_entry_r(&li->li_cache, eDeref);
+    }
+    /* free reader lock */
+    bdb2i_cache_return_entry_r(&li->li_cache, eNew);
+  }
+       if (matched != NULL) free(matched);
+  
+  /*
+   * warn if we exceeded the max depth as the resulting DN may not be dereferenced
+   */
+  if (depth >= be->be_maxDerefDepth) {
+       if (newDN) {
+       Debug( LDAP_DEBUG_TRACE, 
+               "<= max deref depth exceeded in derefDN for \"%s\", result \"%s\"\n", 
+               dn, newDN, 0 );
+               free (newDN);
+               newDN = NULL;
+       } else {
+               Debug( LDAP_DEBUG_TRACE,
+                       "<= max deref depth exceeded in derefDN for \"%s\", result NULL\n",
+                       dn, 0, 0 );
+       }
+    send_ldap_result( conn, op, LDAP_ALIAS_DEREF_PROBLEM, "",
+                     "Maximum alias dereference depth exceeded for base" );
+  }
+
+  if (newDN == NULL) {
+    newDN = ch_strdup ( dn );
+  }
+  
+  Debug( LDAP_DEBUG_TRACE, "<= returning deref DN of \"%s\"\n", newDN, 0, 0 ); 
+
+  return newDN;
+}
diff --git a/servers/slapd/back-bdb2/attr.c b/servers/slapd/back-bdb2/attr.c
new file mode 100644 (file)
index 0000000..729ed9e
--- /dev/null
@@ -0,0 +1,171 @@
+/* attr.c - backend routines for dealing with attributes */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "back-bdb2.h"
+
+static int
+ainfo_type_cmp(
+    char               *type,
+    struct attrinfo    *a
+)
+{
+       return( strcasecmp( type, a->ai_type ) );
+}
+
+static int
+ainfo_cmp(
+    struct attrinfo    *a,
+    struct attrinfo    *b
+)
+{
+       return( strcasecmp( a->ai_type, b->ai_type ) );
+}
+
+/*
+ * Called when a duplicate "index" line is encountered.
+ *
+ * returns 1 => original from init code, indexmask updated
+ *        2 => original not from init code, warn the user
+ */
+
+static int
+ainfo_dup(
+    struct attrinfo    *a,
+    struct attrinfo    *b
+)
+{
+       /*
+        * if the duplicate definition is because we initialized the attr,
+        * just add what came from the config file. otherwise, complain.
+        */
+       if ( a->ai_indexmask & INDEX_FROMINIT ) {
+               a->ai_indexmask |= b->ai_indexmask;
+
+               return( 1 );
+       }
+
+       return( 2 );
+}
+
+void
+bdb2i_attr_masks(
+    struct ldbminfo    *li,
+    char               *type,
+    int                        *indexmask,
+    int                        *syntaxmask
+)
+{
+       struct attrinfo *a;
+
+       *indexmask = 0;
+       *syntaxmask = 0;
+       if ( (a = (struct attrinfo *) avl_find( li->li_attrs, type,
+           ainfo_type_cmp )) == NULL ) {
+               if ( (a = (struct attrinfo *) avl_find( li->li_attrs, "default",
+                   ainfo_type_cmp )) == NULL ) {
+                       return;
+               }
+       }
+       *indexmask = a->ai_indexmask;
+       if ( strcasecmp( a->ai_type, "default" ) == 0 ) {
+               *syntaxmask = attr_syntax( type );
+       } else {
+               *syntaxmask = a->ai_syntaxmask;
+       }
+}
+
+
+/*  BDB2 changed  */
+void
+bdb2i_attr_index_config(
+    struct ldbminfo    *li,
+    char               *fname,
+    int                        lineno,
+    int                        argc,
+    char               **argv,
+    int                        init
+)
+{
+       int             i, j;
+       char            **attrs, **indexes;
+       struct attrinfo *a;
+
+       attrs = str2charray( argv[0], "," );
+       if ( argc > 1 ) {
+               indexes = str2charray( argv[1], "," );
+       }
+       for ( i = 0; attrs[i] != NULL; i++ ) {
+               a = (struct attrinfo *) ch_malloc( sizeof(struct attrinfo) );
+               a->ai_type = ch_strdup( attrs[i] );
+               a->ai_syntaxmask = attr_syntax( a->ai_type );
+               if ( argc == 1 ) {
+                       a->ai_indexmask = (INDEX_PRESENCE | INDEX_EQUALITY |
+                           INDEX_APPROX | INDEX_SUB);
+               } else {
+                       a->ai_indexmask = 0;
+                       for ( j = 0; indexes[j] != NULL; j++ ) {
+                               if ( strncasecmp( indexes[j], "pres", 4 )
+                                   == 0 ) {
+                                       a->ai_indexmask |= INDEX_PRESENCE;
+                               } else if ( strncasecmp( indexes[j], "eq", 2 )
+                                   == 0 ) {
+                                       a->ai_indexmask |= INDEX_EQUALITY;
+                               } else if ( strncasecmp( indexes[j], "approx",
+                                   6 ) == 0 ) {
+                                       a->ai_indexmask |= INDEX_APPROX;
+                               } else if ( strncasecmp( indexes[j], "sub", 3 )
+                                   == 0 ) {
+                                       a->ai_indexmask |= INDEX_SUB;
+                               } else if ( strncasecmp( indexes[j], "none", 4 )
+                                   == 0 ) {
+                                       if ( a->ai_indexmask != 0 ) {
+                                               fprintf( stderr,
+"%s: line %d: index type \"none\" cannot be combined with other types\n",
+                                                   fname, lineno );
+                                       }
+                                       a->ai_indexmask = 0;
+                               } else {
+                                       fprintf( stderr,
+                       "%s: line %d: unknown index type \"%s\" (ignored)\n",
+                                           fname, lineno, indexes[j] );
+                                       fprintf( stderr,
+       "valid index types are \"pres\", \"eq\", \"approx\", or \"sub\"\n" );
+                               }
+                       }
+               }
+               if ( init ) {
+                       a->ai_indexmask |= INDEX_FROMINIT;
+               } else {
+                       if ( a->ai_indexmask )
+                               bdb2i_txn_attr_config( li, a->ai_type, 0 );
+               }
+
+               switch (avl_insert( &li->li_attrs, (caddr_t) a, ainfo_cmp, ainfo_dup )) {
+               case 1:         /* duplicate - updating init version */
+                       free( a->ai_type );
+                       free( (char *) a );
+                       break;
+
+               case 2:         /* user duplicate - ignore and warn */
+                       fprintf( stderr,
+    "%s: line %d: duplicate index definition for attr \"%s\" (ignored)\n",
+                           fname, lineno, a->ai_type );
+                       free( a->ai_type );
+                       free( (char *) a );
+                       break;
+
+               default:;       /* inserted ok */
+                       /* FALL */
+               }
+       }
+       charray_free( attrs );
+       if ( argc > 1 )
+               charray_free( indexes );
+}
diff --git a/servers/slapd/back-bdb2/back-bdb2.h b/servers/slapd/back-bdb2/back-bdb2.h
new file mode 100644 (file)
index 0000000..64f9c26
--- /dev/null
@@ -0,0 +1,185 @@
+/* back-bdb2.h - ldap bdb2 back-end header file */
+
+#ifndef _BACK_BDB2_H_
+#define _BACK_BDB2_H_
+
+#include "ldbm.h"
+#include "db.h"
+
+LDAP_BEGIN_DECL
+
+#define DEFAULT_CACHE_SIZE     1000
+
+/*  since DEFAULT_DB_PAGE_SIZE is 1K, we have 128K,
+       which is suggested by Sleepycat  */
+#define DEFAULT_DBCACHE_SIZE (128 * DEFAULT_DB_PAGE_SIZE)
+
+#define DEFAULT_DB_DIRECTORY   "/usr/tmp"
+#define DEFAULT_MODE           0600
+
+#define SUBLEN                 3
+
+/*
+ * there is a single index for each attribute.  these prefixes insure
+ * that there is no collision among keys.
+ */
+#define EQ_PREFIX      '='     /* prefix for equality keys     */
+#define APPROX_PREFIX  '~'     /* prefix for approx keys       */
+#define SUB_PREFIX     '*'     /* prefix for substring keys    */
+#define CONT_PREFIX    '\\'    /* prefix for continuation keys */
+
+#define UNKNOWN_PREFIX '?'     /* prefix for unknown keys    */
+
+#define DEFAULT_BLOCKSIZE      8192
+
+/*
+ * This structure represents an id block on disk and an id list
+ * in core.
+ *
+ * The fields have the following meanings:
+ *
+ *     b_nmax  maximum number of ids in this block. if this is == ALLIDSBLOCK,
+ *             then this block represents all ids.
+ *     b_nids  current number of ids in use in this block.  if this
+ *             is == INDBLOCK, then this block is an indirect block
+ *             containing a list of other blocks containing actual ids.
+ *             the list is terminated by an id of NOID.
+ *     b_ids   a list of the actual ids themselves
+ */
+
+typedef ID ID_BLOCK;
+
+#define ID_BLOCK_NMAX_OFFSET   0
+#define ID_BLOCK_NIDS_OFFSET   1
+#define ID_BLOCK_IDS_OFFSET            2
+
+/* all ID_BLOCK macros operate on a pointer to a ID_BLOCK */
+
+#define ID_BLOCK_NMAX(b)               ((b)[ID_BLOCK_NMAX_OFFSET])
+#define ID_BLOCK_NIDS(b)               ((b)[ID_BLOCK_NIDS_OFFSET])
+#define ID_BLOCK_ID(b, n)              ((b)[ID_BLOCK_IDS_OFFSET+(n)])
+
+#define ID_BLOCK_NOID(b, n)            (ID_BLOCK_ID((b),(n)) == NOID)
+
+#define ID_BLOCK_ALLIDS_VALUE  0
+#define ID_BLOCK_ALLIDS(b)             (ID_BLOCK_NMAX(b) == ID_BLOCK_ALLIDS_VALUE)
+
+#define ID_BLOCK_INDIRECT_VALUE        0
+#define ID_BLOCK_INDIRECT(b)   (ID_BLOCK_NIDS(b) == ID_BLOCK_INDIRECT_VALUE)
+
+/* for the in-core cache of entries */
+struct cache {
+       int             c_maxsize;
+       int             c_cursize;
+       Avlnode         *c_dntree;
+       Avlnode         *c_idtree;
+       Entry           *c_lruhead;     /* lru - add accessed entries here */
+       Entry           *c_lrutail;     /* lru - rem lru entries from here */
+       ldap_pvt_thread_mutex_t c_mutex;
+};
+
+/* for the cache of open index files (re-used for txn) */
+struct dbcache {
+       int                     dbc_refcnt;
+       int                     dbc_maxids;
+       int                     dbc_maxindirect;
+       time_t          dbc_lastref;
+       long            dbc_blksize;
+       char            *dbc_name;
+       LDBM            dbc_db;
+
+       struct dbcache   *next;
+};
+
+typedef  struct dbcache  BDB2_TXN_FILES;
+
+
+/* for the cache of attribute information (which are indexed, etc.) */
+struct attrinfo {
+       char    *ai_type;       /* type name (cn, sn, ...)      */
+       int     ai_indexmask;   /* how the attr is indexed      */
+#define INDEX_PRESENCE 0x01
+#define INDEX_EQUALITY 0x02
+#define INDEX_APPROX   0x04
+#define INDEX_SUB      0x08
+#define INDEX_UNKNOWN  0x10
+#define INDEX_FROMINIT 0x20
+       int     ai_syntaxmask;  /* what kind of syntax          */
+/* ...from slap.h...
+#define SYNTAX_CIS      0x01
+#define SYNTAX_CES      0x02
+#define SYNTAX_BIN      0x04
+   ... etc. ...
+*/
+};
+
+#define MAXDBCACHE     10
+
+/* this could be made an option */
+#ifndef SLAPD_NEXTID_CHUNK
+#define SLAPD_NEXTID_CHUNK     32
+#endif
+
+
+/*  TP stuff  */
+
+typedef  struct _bdb2_txn_head {
+
+       /*  counter and timer to control checkpoints  */
+       size_t           txn_cnt;
+       time_t           txn_chkp;
+
+       /*  a list of all DB files in use  */
+       BDB2_TXN_FILES   *dbFiles;
+
+       /*  for performance reasons we have pointers to fixed descriptors  */
+       BDB2_TXN_FILES   *dbFileHandle[4];
+#define  BDB2_DB_DN_FILE            0
+#define  BDB2_DB_DN2ID_FILE         1
+#define  BDB2_DB_ID2ENTRY_FILE      2
+#define  BDB2_DB_ID2CHILDREN_FILE   3
+#define  BDB2_DB_OC_IDX_FILE        4
+
+       /*  is the default attribute index set to non-none  */
+       int              withDefIDX;
+#define  BDB2_WITH_DEF_IDX          1
+
+} BDB2_TXN_HEAD;
+
+
+/*  end of TP stuff  */
+
+struct ldbminfo {
+       ID                      li_nextid;
+#if SLAPD_NEXTID_CHUNK > 1
+       ID                      li_nextid_wrote;
+#endif
+       char            *li_nextid_file;
+       ldap_pvt_thread_mutex_t         li_root_mutex;
+       ldap_pvt_thread_mutex_t         li_add_mutex;
+       ldap_pvt_thread_mutex_t         li_nextid_mutex;
+       int                     li_mode;
+       char                    *li_directory;
+       struct cache            li_cache;
+       Avlnode                 *li_attrs;
+       int                     li_dbcachesize;
+       int                     li_dbcachewsync;
+       struct dbcache          li_dbcache[MAXDBCACHE];
+       ldap_pvt_thread_mutex_t         li_dbcache_mutex;
+       ldap_pvt_thread_cond_t          li_dbcache_cv;
+
+       /*  Berkeley DB2 Environment  */
+       DB_ENV                  li_db_env;
+       char                    *li_dbhome;
+       BDB2_TXN_HEAD           li_txn_head;
+
+};
+
+
+extern  int bdb2i_with_dbenv;
+
+#include "proto-back-bdb2.h"
+
+LDAP_END_DECL
+
+#endif /* _back_bdb2_h_ */
diff --git a/servers/slapd/back-bdb2/bind.c b/servers/slapd/back-bdb2/bind.c
new file mode 100644 (file)
index 0000000..264c216
--- /dev/null
@@ -0,0 +1,262 @@
+/* bind.c - bdb2 backend bind and unbind routines */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/krb.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+
+#include "slap.h"
+#include "back-bdb2.h"
+#include "proto-back-bdb2.h"
+
+#include <lutil.h>
+
+#ifdef HAVE_KERBEROS
+extern int     bdb2i_krbv4_ldap_auth();
+#endif
+
+static int
+crypted_value_find(
+       struct berval       **vals,
+       struct berval       *v,
+       int                 syntax,
+       int                 normalize,
+       struct berval           *cred
+)
+{
+       int     i;
+       for ( i = 0; vals[i] != NULL; i++ ) {
+               if ( syntax != SYNTAX_BIN ) {
+                       int result;
+
+#ifdef SLAPD_CRYPT
+                       ldap_pvt_thread_mutex_lock( &crypt_mutex );
+#endif
+
+                       result = lutil_passwd(
+                               (char*) cred->bv_val,
+                               (char*) vals[i]->bv_val);
+
+#ifdef SLAPD_CRYPT
+                       ldap_pvt_thread_mutex_unlock( &crypt_mutex );
+#endif
+
+                       return result;
+
+               } else {
+                if ( value_cmp( vals[i], v, syntax, normalize ) == 0 ) {
+                        return( 0 );
+                }
+        }
+       }
+
+       return( 1 );
+}
+
+static int
+bdb2i_back_bind_internal(
+    Backend            *be,
+    Connection         *conn,
+    Operation          *op,
+    char               *dn,
+    int                        method,
+    struct berval      *cred,
+       char**  edn
+)
+{
+       struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+       Entry           *e;
+       Attribute       *a;
+       int             rc;
+       char            *matched;
+#ifdef HAVE_KERBEROS
+       char            krbname[MAX_K_NAME_SZ + 1];
+       AUTH_DAT        ad;
+#endif
+
+       Debug(LDAP_DEBUG_ARGS, "==> bdb2_back_bind: dn: %s\n", dn, 0, 0);
+
+       *edn = NULL;
+
+       /* get entry with reader lock */
+       if ( (e = bdb2i_dn2entry_r( be, dn, &matched )) == NULL ) {
+               /* allow noauth binds */
+               if ( method == LDAP_AUTH_SIMPLE && cred->bv_len == 0 ) {
+                       /*
+                        * bind successful, but return 1 so we don't
+                        * authorize based on noauth credentials
+                        */
+                       send_ldap_result( conn, op, LDAP_SUCCESS, NULL, NULL );
+                       rc = 1;
+               } else if ( be_isroot_pw( be, dn, cred ) ) {
+                       /* front end will send result */
+                       *edn = ch_strdup( be_root_dn( be ) );
+                       rc = 0;
+               } else {
+                       send_ldap_result( conn, op, LDAP_NO_SUCH_OBJECT, matched, NULL );
+                       rc = 1;
+               }
+               if ( matched != NULL ) {
+                       free( matched );
+               }
+               return( rc );
+       }
+
+       *edn = ch_strdup( e->e_dn );
+
+       /* check for deleted */
+
+       switch ( method ) {
+       case LDAP_AUTH_SIMPLE:
+               if ( cred->bv_len == 0 ) {
+                       send_ldap_result( conn, op, LDAP_SUCCESS, NULL, NULL );
+
+                       /* stop front end from sending result */
+                       rc = 1;
+                       goto return_results;
+               } else if ( be_isroot_pw( be, dn, cred ) ) {
+                       /* front end will send result */
+                       *edn = ch_strdup( be_root_dn( be ) );
+                       rc = 0;
+                       goto return_results;
+               }
+
+               if ( (a = attr_find( e->e_attrs, "userpassword" )) == NULL ) {
+                       if ( be_isroot_pw( be, dn, cred ) ) {
+                               /* front end will send result */
+                               *edn = ch_strdup( be_root_dn( be ) );
+                               rc = 0;
+                               goto return_results;
+                       }
+                       send_ldap_result( conn, op, LDAP_INAPPROPRIATE_AUTH,
+                           NULL, NULL );
+                       rc = 1;
+                       goto return_results;
+               }
+
+               if ( crypted_value_find( a->a_vals, cred, a->a_syntax, 0, cred ) != 0 )
+               {
+                       if ( be_isroot_pw( be, dn, cred ) ) {
+                               /* front end will send result */
+                               *edn = ch_strdup( be_root_dn( be ) );
+                               rc = 0;
+                               goto return_results;
+                       }
+                       send_ldap_result( conn, op, LDAP_INVALID_CREDENTIALS,
+                               NULL, NULL );
+                       rc = 1;
+                       goto return_results;
+               }
+               rc = 0;
+               break;
+
+#ifdef HAVE_KERBEROS
+       case LDAP_AUTH_KRBV41:
+               if ( bdb2i_krbv4_ldap_auth( be, cred, &ad ) != LDAP_SUCCESS ) {
+                       send_ldap_result( conn, op, LDAP_INVALID_CREDENTIALS,
+                           NULL, NULL );
+                       rc = 0;
+                       goto return_results;
+               }
+               sprintf( krbname, "%s%s%s@%s", ad.pname, *ad.pinst ? "."
+                   : "", ad.pinst, ad.prealm );
+               if ( (a = attr_find( e->e_attrs, "krbname" )) == NULL ) {
+                       /*
+                        * no krbName values present:  check against DN
+                        */
+                       if ( strcasecmp( dn, krbname ) == 0 ) {
+                               rc = 0; /* XXX wild ass guess */
+                               break;
+                       }
+                       send_ldap_result( conn, op, LDAP_INAPPROPRIATE_AUTH,
+                           NULL, NULL );
+                       rc = 1;
+                       goto return_results;
+               } else {        /* look for krbName match */
+                       struct berval   krbval;
+
+                       krbval.bv_val = krbname;
+                       krbval.bv_len = strlen( krbname );
+
+                       if ( value_find( a->a_vals, &krbval, a->a_syntax, 3 ) != 0 ) {
+                               send_ldap_result( conn, op,
+                                   LDAP_INVALID_CREDENTIALS, NULL, NULL );
+                               rc = 1;
+                               goto return_results;
+                       }
+               }
+               rc = 0;
+               break;
+
+       case LDAP_AUTH_KRBV42:
+               send_ldap_result( conn, op, LDAP_SUCCESS, NULL, NULL );
+               /* stop front end from sending result */
+               rc = 1;
+               goto return_results;
+#endif
+
+       default:
+               send_ldap_result( conn, op, LDAP_STRONG_AUTH_NOT_SUPPORTED,
+                   NULL, "auth method not supported" );
+               rc = 1;
+               goto return_results;
+       }
+
+return_results:;
+       /* free entry and reader lock */
+       bdb2i_cache_return_entry_r( &li->li_cache, e );
+
+       /* front end with send result on success (rc==0) */
+       return( rc );
+}
+
+
+int
+bdb2_back_bind(
+    Backend            *be,
+    Connection         *conn,
+    Operation          *op,
+    char               *dn,
+    int                        method,
+    struct berval      *cred,
+       char**  edn
+)
+{
+       DB_LOCK  lock;
+       struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+
+       struct timeval  time1, time2;
+       char   *elapsed_time;
+       int    ret;
+
+       gettimeofday( &time1, NULL );
+
+       if ( bdb2i_enter_backend_r( &li->li_db_env, &lock ) != 0 ) {
+
+               send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR, "", "" );
+               return( 1 );
+
+       }
+
+       ret = bdb2i_back_bind_internal( be, conn, op, dn, method, cred, edn );
+
+       (void) bdb2i_leave_backend( &li->li_db_env, lock );
+
+       if ( bdb2i_do_timing ) {
+
+               gettimeofday( &time2, NULL);
+               elapsed_time = bdb2i_elapsed( time1, time2 );
+               Debug( LDAP_DEBUG_ANY, "conn=%d op=%d BIND elapsed=%s\n",
+                               conn->c_connid, op->o_opid, elapsed_time );
+               free( elapsed_time );
+
+       }
+
+       return( ret );
+}
+
+
diff --git a/servers/slapd/back-bdb2/cache.c b/servers/slapd/back-bdb2/cache.c
new file mode 100644 (file)
index 0000000..8e696e1
--- /dev/null
@@ -0,0 +1,462 @@
+/* cache.c - routines to maintain an in-core cache of entries */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/errno.h>
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+
+#include "back-bdb2.h"
+
+static int     cache_delete_entry_internal(struct cache *cache, Entry *e);
+#ifdef LDAP_DEBUG
+static void    lru_print(struct cache *cache);
+#endif
+
+/*
+ * the cache has three entry points (ways to find things):
+ *
+ *     by entry        e.g., if you already have an entry from the cache
+ *                     and want to delete it. (really by entry ptr)
+ *     by dn           e.g., when looking for the base object of a search
+ *     by id           e.g., for search candidates
+ *
+ * these correspond to three different avl trees that are maintained.
+ */
+
+static int
+cache_entry_cmp( Entry *e1, Entry *e2 )
+{
+       return( e1 < e2 ? -1 : (e1 > e2 ? 1 : 0) );
+}
+
+static int
+cache_entrydn_cmp( Entry *e1, Entry *e2 )
+{
+       /* compare their normalized UPPERCASED dn's */
+       return( strcmp( e1->e_ndn, e2->e_ndn ) );
+}
+
+static int
+cache_entryid_cmp( Entry *e1, Entry *e2 )
+{
+       return( e1->e_id < e2->e_id ? -1 : (e1->e_id > e2->e_id ? 1 : 0) );
+}
+
+void
+bdb2i_cache_set_state( struct cache *cache, Entry *e, int state )
+{
+       /* set cache mutex */
+       ldap_pvt_thread_mutex_lock( &cache->c_mutex );
+
+       e->e_state = state;
+
+       /* free cache mutex */
+       ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
+}
+
+#ifdef not_used
+static void
+cache_return_entry( struct cache *cache, Entry *e )
+{
+       /* set cache mutex */
+       ldap_pvt_thread_mutex_lock( &cache->c_mutex );
+
+       if ( --e->e_refcnt == 0 && e->e_state == ENTRY_STATE_DELETED ) {
+               entry_free( e );
+       }
+
+       /* free cache mutex */
+       ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
+}
+#endif
+
+static void
+cache_return_entry_rw( struct cache *cache, Entry *e, int rw )
+{
+       Debug( LDAP_DEBUG_TRACE, "====> cache_return_entry_%s\n",
+               rw ? "w" : "r", 0, 0);
+
+       /* set cache mutex */
+       ldap_pvt_thread_mutex_lock( &cache->c_mutex );
+
+       entry_rdwr_unlock(e, rw);;
+
+       if ( --e->e_refcnt == 0 && e->e_state == ENTRY_STATE_DELETED ) {
+               entry_free( e );
+       }
+
+       /* free cache mutex */
+       ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
+}
+
+void
+bdb2i_cache_return_entry_r( struct cache *cache, Entry *e )
+{
+       cache_return_entry_rw(cache, e, 0);
+}
+
+void
+bdb2i_cache_return_entry_w( struct cache *cache, Entry *e )
+{
+       cache_return_entry_rw(cache, e, 1);
+}
+
+
+#define LRU_DELETE( cache, e ) { \
+       if ( e->e_lruprev != NULL ) { \
+               e->e_lruprev->e_lrunext = e->e_lrunext; \
+       } else { \
+               cache->c_lruhead = e->e_lrunext; \
+       } \
+       if ( e->e_lrunext != NULL ) { \
+               e->e_lrunext->e_lruprev = e->e_lruprev; \
+       } else { \
+               cache->c_lrutail = e->e_lruprev; \
+       } \
+}
+
+#define LRU_ADD( cache, e ) { \
+       e->e_lrunext = cache->c_lruhead; \
+       if ( e->e_lrunext != NULL ) { \
+               e->e_lrunext->e_lruprev = e; \
+       } \
+       cache->c_lruhead = e; \
+       e->e_lruprev = NULL; \
+       if ( cache->c_lrutail == NULL ) { \
+               cache->c_lrutail = e; \
+       } \
+}
+
+/*
+ * cache_create_entry_lock - create an entry in the cache, and lock it.
+ * returns:    0       entry has been created and locked
+ *             1       entry already existed
+ *             -1      something bad happened
+ */
+int
+bdb2i_cache_add_entry_lock(
+    struct cache       *cache,
+    Entry              *e,
+    int                        state
+)
+{
+       int     i, rc;
+       Entry   *ee;
+
+       /* set cache mutex */
+       ldap_pvt_thread_mutex_lock( &cache->c_mutex );
+
+       if ( avl_insert( &cache->c_dntree, (caddr_t) e,
+               cache_entrydn_cmp, avl_dup_error ) != 0 )
+       {
+               Debug( LDAP_DEBUG_TRACE,
+                       "====> cache_add_entry lock: entry %20s id %lu already in dn cache\n",
+                   e->e_dn, e->e_id, 0 );
+
+               /* free cache mutex */
+               ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
+               return( 1 );
+       }
+
+       /* id tree */
+       if ( avl_insert( &cache->c_idtree, (caddr_t) e,
+               cache_entryid_cmp, avl_dup_error ) != 0 )
+       {
+               Debug( LDAP_DEBUG_ANY,
+                       "====> entry %20s id %lu already in id cache\n",
+                   e->e_dn, e->e_id, 0 );
+
+               /* delete from dn tree inserted above */
+               if ( avl_delete( &cache->c_dntree, (caddr_t) e,
+                       cache_entrydn_cmp ) == NULL )
+               {
+                       Debug( LDAP_DEBUG_ANY, "====> can't delete from dn cache\n",
+                           0, 0, 0 );
+               }
+
+               /* free cache mutex */
+               ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
+               return( -1 );
+       }
+
+       e->e_state = state;
+       e->e_refcnt = 1;
+
+       /* lru */
+       LRU_ADD( cache, e );
+       if ( ++cache->c_cursize > cache->c_maxsize ) {
+               /*
+                * find the lru entry not currently in use and delete it.
+                * in case a lot of entries are in use, only look at the
+                * first 10 on the tail of the list.
+                */
+               i = 0;
+               while ( cache->c_lrutail != NULL && cache->c_lrutail->e_refcnt
+                   != 0 && i < 10 ) {
+                       /* move this in-use entry to the front of the q */
+                       ee = cache->c_lrutail;
+                       LRU_DELETE( cache, ee );
+                       LRU_ADD( cache, ee );
+                       i++;
+               }
+
+               /*
+                * found at least one to delete - try to get back under
+                * the max cache size.
+                */
+               while ( cache->c_lrutail != NULL && cache->c_lrutail->e_refcnt
+                    == 0 && cache->c_cursize > cache->c_maxsize ) {
+                       e = cache->c_lrutail;
+
+               /* XXX check for writer lock - should also check no readers pending */
+#ifdef LDAP_DEBUG
+                       assert(!ldap_pvt_thread_rdwr_active(&e->e_rdwr));
+#endif
+
+                       /* delete from cache and lru q */
+                       rc = cache_delete_entry_internal( cache, e );
+
+                       entry_free( e );
+               }
+       }
+
+       /* free cache mutex */
+       ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
+       return( 0 );
+}
+
+/*
+ * cache_find_entry_dn2id - find an entry in the cache, given dn
+ */
+
+ID
+bdb2i_cache_find_entry_dn2id(
+       Backend         *be,
+    struct cache       *cache,
+    char               *dn
+)
+{
+       struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+       Entry           e, *ep;
+       ID                      id;
+
+       /* set cache mutex */
+       ldap_pvt_thread_mutex_lock( &cache->c_mutex );
+
+       e.e_dn = dn;
+       e.e_ndn = dn_normalize_case( ch_strdup( dn ) );
+
+       if ( (ep = (Entry *) avl_find( cache->c_dntree, (caddr_t) &e,
+               cache_entrydn_cmp )) != NULL )
+       {
+               /*
+                * ep now points to an unlocked entry
+                * we do not need to lock the entry if we only
+                * check the state, refcnt, LRU, and id.
+                */
+               free(e.e_ndn);
+
+               Debug(LDAP_DEBUG_TRACE, "====> cache_find_entry_dn2id: found dn: %s\n",
+                       dn, 0, 0);
+
+               /*
+                * entry is deleted or not fully created yet
+                */
+               if ( ep->e_state == ENTRY_STATE_DELETED ||
+                       ep->e_state == ENTRY_STATE_CREATING )
+               {
+                       /* free cache mutex */
+                       ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
+                       return( NOID );
+               }
+
+               /* lru */
+               LRU_DELETE( cache, ep );
+               LRU_ADD( cache, ep );
+
+               /* save id */
+               id = ep->e_id;
+
+               /* free cache mutex */
+               ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
+
+               return( id );
+       }
+
+       free(e.e_ndn);
+
+       /* free cache mutex */
+       ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
+
+       return( NOID );
+}
+
+/*
+ * cache_find_entry_id - find an entry in the cache, given id
+ */
+
+Entry *
+bdb2i_cache_find_entry_id(
+       struct cache    *cache,
+       ID                              id,
+       int                             rw
+)
+{
+       Entry   e;
+       Entry   *ep;
+
+       e.e_id = id;
+
+try_again:
+       /* set cache mutex */
+       ldap_pvt_thread_mutex_lock( &cache->c_mutex );
+
+       if ( (ep = (Entry *) avl_find( cache->c_idtree, (caddr_t) &e,
+               cache_entryid_cmp )) != NULL )
+       {
+               Debug(LDAP_DEBUG_TRACE,
+                       "====> cache_find_entry_dn2id: found id: %ld rw: %d\n",
+                       id, rw, 0);
+
+               /*
+                * entry is deleted or not fully created yet
+                */
+               if ( ep->e_state == ENTRY_STATE_DELETED ||
+                       ep->e_state == ENTRY_STATE_CREATING )
+               {
+                       /* free cache mutex */
+                       ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
+                       return( NULL );
+               }
+
+               /* acquire reader lock */
+               if ( entry_rdwr_trylock(ep, rw) == LDAP_PVT_THREAD_EBUSY ) {
+                       /* could not acquire entry lock...
+                        * owner cannot free as we have the cache locked.
+                        * so, unlock the cache, yield, and try again.
+                        */
+
+                       /* free cache mutex */
+                       ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
+                       ldap_pvt_thread_yield();
+                       goto try_again;
+               }
+
+               /* lru */
+               LRU_DELETE( cache, ep );
+               LRU_ADD( cache, ep );
+                
+               ep->e_refcnt++;
+
+               /* free cache mutex */
+               ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
+
+               return( ep );
+       }
+
+       /* free cache mutex */
+       ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
+
+       return( NULL );
+}
+
+/*
+ * cache_delete_entry - delete the entry e from the cache.  the caller
+ * should have obtained e (increasing its ref count) via a call to one
+ * of the cache_find_* routines.  the caller should *not* call the
+ * cache_return_entry() routine prior to calling cache_delete_entry().
+ * it performs this function.
+ *
+ * returns:    0       e was deleted ok
+ *             1       e was not in the cache
+ *             -1      something bad happened
+ */
+int
+bdb2i_cache_delete_entry(
+    struct cache       *cache,
+    Entry              *e
+)
+{
+       int     rc;
+
+       Debug( LDAP_DEBUG_TRACE, "====> cache_delete_entry:\n", 0, 0, 0 );
+
+       /* XXX check for writer lock - should also check no readers pending */
+#ifdef LDAP_DEBUG
+       assert(ldap_pvt_thread_rdwr_writers(&e->e_rdwr));
+#endif
+
+       /* set cache mutex */
+       ldap_pvt_thread_mutex_lock( &cache->c_mutex );
+
+       rc = cache_delete_entry_internal( cache, e );
+
+       /* free cache mutex */
+       ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
+       return( rc );
+}
+
+static int
+cache_delete_entry_internal(
+    struct cache       *cache,
+    Entry              *e
+)
+{
+       int rc = 0;     /* return code */
+
+       /* dn tree */
+       if ( avl_delete( &cache->c_dntree, (caddr_t) e, cache_entrydn_cmp )
+               == NULL )
+       {
+               rc = -1;
+       }
+
+       /* id tree */
+       if ( avl_delete( &cache->c_idtree, (caddr_t) e, cache_entryid_cmp )
+               == NULL )
+       {
+               rc = -1;
+       }
+
+       if (rc != 0) {
+               return rc;
+       }
+
+       /* lru */
+       LRU_DELETE( cache, e );
+       cache->c_cursize--;
+
+       /*
+        * flag entry to be freed later by a call to cache_return_entry()
+        */
+       e->e_state = ENTRY_STATE_DELETED;
+
+       return( 0 );
+}
+
+#ifdef LDAP_DEBUG
+
+static void
+lru_print( struct cache *cache )
+{
+       Entry   *e;
+
+       fprintf( stderr, "LRU queue (head to tail):\n" );
+       for ( e = cache->c_lruhead; e != NULL; e = e->e_lrunext ) {
+               fprintf( stderr, "\tdn %20s id %lu refcnt %d\n", e->e_dn,
+                   e->e_id, e->e_refcnt );
+       }
+       fprintf( stderr, "LRU queue (tail to head):\n" );
+       for ( e = cache->c_lrutail; e != NULL; e = e->e_lruprev ) {
+               fprintf( stderr, "\tdn %20s id %lu refcnt %d\n", e->e_dn,
+                   e->e_id, e->e_refcnt );
+       }
+}
+
+#endif
+
diff --git a/servers/slapd/back-bdb2/close.c b/servers/slapd/back-bdb2/close.c
new file mode 100644 (file)
index 0000000..90dda95
--- /dev/null
@@ -0,0 +1,52 @@
+/* close.c - close bdb2 backend */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "back-bdb2.h"
+
+static int
+bdb2i_back_db_close_internal( Backend *be )
+{
+       Debug( LDAP_DEBUG_TRACE, "bdb2 backend saving nextid\n", 0, 0, 0 );
+       if ( bdb2i_next_id_save( be ) < 0 ) {
+               Debug( LDAP_DEBUG_ANY, "bdb2 backend nextid save failed!\n", 0, 0, 0 );
+       }
+
+       Debug( LDAP_DEBUG_TRACE, "bdb2 backend syncing\n", 0, 0, 0 );
+       bdb2i_cache_flush_all( be );
+       Debug( LDAP_DEBUG_TRACE, "bdb2 backend done syncing\n", 0, 0, 0 );
+
+       return 0;
+}
+
+
+int
+bdb2_back_db_close( Backend *be )
+{
+       struct timeval  time1, time2;
+       char   *elapsed_time;
+       int    ret;
+
+       gettimeofday( &time1, NULL );
+
+       ret = bdb2i_back_db_close_internal( be );
+
+       if ( bdb2i_do_timing ) {
+
+               gettimeofday( &time2, NULL);
+               elapsed_time = bdb2i_elapsed( time1, time2 );
+               Debug( LDAP_DEBUG_ANY, "CLOSE elapsed=%s\n",
+                               elapsed_time, 0, 0 );
+               free( elapsed_time );
+
+       }
+
+       return( ret );
+}
+
+
diff --git a/servers/slapd/back-bdb2/compare.c b/servers/slapd/back-bdb2/compare.c
new file mode 100644 (file)
index 0000000..df38525
--- /dev/null
@@ -0,0 +1,107 @@
+/* compare.c - bdb2 backend compare routine */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "back-bdb2.h"
+#include "proto-back-bdb2.h"
+
+static int
+bdb2i_back_compare_internal(
+    Backend    *be,
+    Connection *conn,
+    Operation  *op,
+    char       *dn,
+    Ava                *ava
+)
+{
+       struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+       char            *matched;
+       Entry           *e;
+       Attribute       *a;
+       int             rc;
+
+       /* get entry with reader lock */
+       if ( (e = bdb2i_dn2entry_r( be, dn, &matched )) == NULL ) {
+               send_ldap_result( conn, op, LDAP_NO_SUCH_OBJECT, matched, "" );
+
+               if(matched == NULL) free(matched);
+               return( 1 );
+       }
+
+       /* check for deleted */
+       if ( ! access_allowed( be, conn, op, e,
+               ava->ava_type, &ava->ava_value, ACL_COMPARE ) )
+       {
+               send_ldap_result( conn, op, LDAP_INSUFFICIENT_ACCESS, "", "" );
+               rc = 1;
+               goto return_results;
+       }
+
+       if ( (a = attr_find( e->e_attrs, ava->ava_type )) == NULL ) {
+               send_ldap_result( conn, op, LDAP_NO_SUCH_ATTRIBUTE, "", "" );
+               rc = 1;
+               goto return_results;
+       }
+
+       if ( value_find( a->a_vals, &ava->ava_value, a->a_syntax, 1 ) == 0 ) 
+               send_ldap_result( conn, op, LDAP_COMPARE_TRUE, "", "" );
+       else
+               send_ldap_result( conn, op, LDAP_COMPARE_FALSE, "", "" );
+
+       rc = 0;
+
+return_results:;
+       bdb2i_cache_return_entry_r( &li->li_cache, e );
+       return( rc );
+}
+
+
+int
+bdb2_back_compare(
+    Backend    *be,
+    Connection *conn,
+    Operation  *op,
+    char       *dn,
+    Ava                *ava
+)
+{
+       DB_LOCK  lock;
+       struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+
+       struct timeval  time1, time2;
+       char   *elapsed_time;
+       int    ret;
+
+       gettimeofday( &time1, NULL );
+
+       if ( bdb2i_enter_backend_r( &li->li_db_env, &lock ) != 0 ) {
+
+               send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR, "", "" );
+               return( 1 );
+
+       }
+
+       ret = bdb2i_back_compare_internal( be, conn, op, dn, ava );
+
+       (void) bdb2i_leave_backend( &li->li_db_env, lock );
+
+       if ( bdb2i_do_timing ) {
+
+               gettimeofday( &time2, NULL);
+               elapsed_time = bdb2i_elapsed( time1, time2 );
+               Debug( LDAP_DEBUG_ANY, "conn=%d op=%d CMP elapsed=%s\n",
+                               conn->c_connid, op->o_opid, elapsed_time );
+               free( elapsed_time );
+
+       }
+
+       return( ret );
+}
+
+
diff --git a/servers/slapd/back-bdb2/config.c b/servers/slapd/back-bdb2/config.c
new file mode 100644 (file)
index 0000000..92d2e46
--- /dev/null
@@ -0,0 +1,138 @@
+/* config.c - bdb2 backend configuration file routine */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "back-bdb2.h"
+
+static int
+bdb2i_back_db_config_internal(
+    Backend    *be,
+    char       *fname,
+    int                lineno,
+    int                argc,
+    char       **argv
+)
+{
+       struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+
+       if ( li == NULL ) {
+               fprintf( stderr, "%s: line %d: ldbm backend info is null!\n",
+                   fname, lineno );
+               return( 1 );
+       }
+
+       /* directory where database files live */
+       if ( strcasecmp( argv[0], "directory" ) == 0 ) {
+               if ( argc < 2 ) {
+                       fprintf( stderr,
+               "%s: line %d: missing dir in \"directory <dir>\" line\n",
+                           fname, lineno );
+                       return( 1 );
+               }
+               li->li_directory = ch_strdup( argv[1] );
+
+               li->li_nextid_file =
+                       ch_malloc( strlen(li->li_directory) + sizeof("/NEXTID") );
+
+               strcpy(li->li_nextid_file, li->li_directory);
+               strcat(li->li_nextid_file, "/NEXTID");
+
+       /* mode with which to create new database files */
+       } else if ( strcasecmp( argv[0], "mode" ) == 0 ) {
+               if ( argc < 2 ) {
+                       fprintf( stderr,
+                       "%s: line %d: missing mode in \"mode <mode>\" line\n",
+                           fname, lineno );
+                       return( 1 );
+               }
+               li->li_mode = strtol( argv[1], NULL, 0 );
+
+       /* attribute to index */
+       } else if ( strcasecmp( argv[0], "index" ) == 0 ) {
+               if ( argc < 2 ) {
+                       fprintf( stderr,
+"%s: line %d: missing attr in \"index <attr> [pres,eq,approx,sub]\" line\n",
+                           fname, lineno );
+                       return( 1 );
+               } else if ( argc > 3 ) {
+                       fprintf( stderr,
+"%s: line %d: extra junk after \"index <attr> [pres,eq,approx,sub]\" line (ignored)\n",
+                           fname, lineno );
+               }
+               bdb2i_attr_index_config( li, fname, lineno, argc - 1, &argv[1], 0 );
+
+       /* size of the cache in entries */
+       } else if ( strcasecmp( argv[0], "cachesize" ) == 0 ) {
+               if ( argc < 2 ) {
+                       fprintf( stderr,
+               "%s: line %d: missing size in \"cachesize <size>\" line\n",
+                           fname, lineno );
+                       return( 1 );
+               }
+               li->li_cache.c_maxsize = atoi( argv[1] );
+
+       /* size of each dbcache in bytes */
+       } else if ( strcasecmp( argv[0], "dbcachesize" ) == 0 ) {
+               if ( argc < 2 ) {
+                       fprintf( stderr,
+               "%s: line %d: missing size in \"dbcachesize <size>\" line\n",
+                           fname, lineno );
+                       return( 1 );
+               }
+               li->li_dbcachesize = atoi( argv[1] );
+               /*  we should at least have the suggested 128k  */
+               if ( li->li_dbcachesize < DEFAULT_DBCACHE_SIZE )
+                       li->li_dbcachesize = DEFAULT_DBCACHE_SIZE;
+
+       /* no write sync */
+       } else if ( strcasecmp( argv[0], "dbcachenowsync" ) == 0 ) {
+               li->li_dbcachewsync = 0;
+
+       /* anything else */
+       } else {
+               fprintf( stderr,
+"%s: line %d: unknown directive \"%s\" in ldbm database definition (ignored)\n",
+                   fname, lineno, argv[0] );
+       }
+
+       return 0;
+}
+
+
+int
+bdb2_back_db_config(
+    Backend    *be,
+    char       *fname,
+    int                lineno,
+    int                argc,
+    char       **argv
+)
+{
+       struct timeval  time1, time2;
+       char   *elapsed_time;
+       int    ret;
+
+       gettimeofday( &time1, NULL );
+
+       ret = bdb2i_back_db_config_internal( be, fname, lineno, argc, argv );
+
+       if ( bdb2i_do_timing ) {
+
+               gettimeofday( &time2, NULL);
+               elapsed_time = bdb2i_elapsed( time1, time2 );
+               Debug( LDAP_DEBUG_ANY, "CONFIG elapsed=%s\n",
+                               elapsed_time, 0, 0 );
+               free( elapsed_time );
+
+       }
+
+       return( ret );
+}
+
+
diff --git a/servers/slapd/back-bdb2/dbcache.c b/servers/slapd/back-bdb2/dbcache.c
new file mode 100644 (file)
index 0000000..259245a
--- /dev/null
@@ -0,0 +1,252 @@
+/* ldbmcache.c - maintain a cache of open bdb2 files */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/errno.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include <sys/stat.h>
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#include "ldapconfig.h"
+#include "slap.h"
+#include "back-bdb2.h"
+
+#define  ldbm_open_env( buf, flags, mode, dbcachesize, env ) \
+                       ldbm_open( (buf), (flags), (mode), (dbcachesize) )
+
+struct dbcache *
+bdb2i_cache_open(
+    Backend    *be,
+    char       *name,
+    char       *suffix,
+    int                flags
+)
+{
+       struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+       int             i, lru;
+       time_t          oldtime, curtime;
+       char            buf[MAXPATHLEN];
+       LDBM            db;
+       struct stat     st;
+       int                     dbcachesize;
+
+       /* sprintf( buf, "%s%s%s%s", li->li_directory, DEFAULT_DIRSEP, name, suffix ); */
+       sprintf( buf, "%s%s", name, suffix );
+
+       /*  if in slapd, all files are open, so return handle from file cache  */
+       if (  bdb2i_with_dbenv )
+               return( bdb2i_get_db_file_cache( li, buf ));
+
+       Debug( LDAP_DEBUG_TRACE, "=> bdb2i_cache_open( \"%s\", %d, %o )\n", buf,
+           flags, li->li_mode );
+
+       lru = 0;
+       ldap_pvt_thread_mutex_lock( &currenttime_mutex );
+       curtime = currenttime;
+       ldap_pvt_thread_mutex_unlock( &currenttime_mutex );
+       oldtime = curtime;
+
+       ldap_pvt_thread_mutex_lock( &li->li_dbcache_mutex );
+       for ( i = 0; i < MAXDBCACHE && li->li_dbcache[i].dbc_name != NULL;
+           i++ ) {
+               /* already open - return it */
+               if ( strcmp( li->li_dbcache[i].dbc_name, buf ) == 0 ) {
+                       li->li_dbcache[i].dbc_refcnt++;
+                       Debug( LDAP_DEBUG_TRACE,
+                           "<= bdb2i_cache_open (cache %d)\n", i, 0, 0 );
+                       ldap_pvt_thread_mutex_unlock( &li->li_dbcache_mutex );
+                       return( &li->li_dbcache[i] );
+               }
+
+               /* keep track of lru db */
+               if ( li->li_dbcache[i].dbc_lastref < oldtime &&
+                   li->li_dbcache[i].dbc_refcnt == 0 ) {
+                       lru = i;
+                       oldtime = li->li_dbcache[i].dbc_lastref;
+               }
+       }
+
+       /* no empty slots, not already open - close lru and use that slot */
+       if ( i == MAXDBCACHE ) {
+               i = lru;
+               if ( li->li_dbcache[i].dbc_refcnt != 0 ) {
+                       Debug( LDAP_DEBUG_ANY,
+                           "bdb2i_cache_open no unused db to close - waiting\n",
+                           0, 0, 0 );
+                       lru = -1;
+                       while ( lru == -1 ) {
+                               ldap_pvt_thread_cond_wait( &li->li_dbcache_cv,
+                                   &li->li_dbcache_mutex );
+                               for ( i = 0; i < MAXDBCACHE; i++ ) {
+                                       if ( li->li_dbcache[i].dbc_refcnt
+                                           == 0 ) {
+                                               lru = i;
+                                               break;
+                                       }
+                               }
+                       }
+                       i = lru;
+               }
+               ldbm_close( li->li_dbcache[i].dbc_db );
+               free( li->li_dbcache[i].dbc_name );
+               li->li_dbcache[i].dbc_name = NULL;
+       }
+
+       dbcachesize = li->li_dbcachesize;
+
+       if (  bdb2i_with_dbenv ) dbcachesize = 0;
+       if ( (li->li_dbcache[i].dbc_db = ldbm_open_env( buf, flags, li->li_mode,
+           dbcachesize, &li->li_db_env )) == NULL ) {
+
+               Debug( LDAP_DEBUG_TRACE,
+                   "<= bdb2i_cache_open NULL \"%s\" errno %d reason \"%s\")\n",
+                   buf, errno, errno > -1 && errno < sys_nerr ?
+                   sys_errlist[errno] : "unknown" );
+               ldap_pvt_thread_mutex_unlock( &li->li_dbcache_mutex );
+               return( NULL );
+       }
+       li->li_dbcache[i].dbc_name = ch_strdup( buf );
+       li->li_dbcache[i].dbc_refcnt = 1;
+       li->li_dbcache[i].dbc_lastref = curtime;
+       if ( stat( buf, &st ) == 0 ) {
+               li->li_dbcache[i].dbc_blksize = st.st_blksize;
+       } else {
+               li->li_dbcache[i].dbc_blksize = DEFAULT_BLOCKSIZE;
+       }
+       li->li_dbcache[i].dbc_maxids = (li->li_dbcache[i].dbc_blksize /
+           sizeof(ID)) - ID_BLOCK_IDS_OFFSET;
+       li->li_dbcache[i].dbc_maxindirect = (SLAPD_LDBM_MIN_MAXIDS /
+           li->li_dbcache[i].dbc_maxids) + 1;
+
+       Debug( LDAP_DEBUG_ARGS,
+           "bdb2i_cache_open (blksize %ld) (maxids %d) (maxindirect %d)\n",
+           li->li_dbcache[i].dbc_blksize, li->li_dbcache[i].dbc_maxids,
+           li->li_dbcache[i].dbc_maxindirect );
+       Debug( LDAP_DEBUG_TRACE, "<= bdb2i_cache_open (opened %d)\n", i, 0, 0 );
+       ldap_pvt_thread_mutex_unlock( &li->li_dbcache_mutex );
+       return( &li->li_dbcache[i] );
+}
+
+void
+bdb2i_cache_close( Backend *be, struct dbcache *db )
+{
+       struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+
+       /*  if in slapd, all files stay open and we have only
+               readers or one writer  */
+       if (  bdb2i_with_dbenv ) return;
+
+       ldap_pvt_thread_mutex_lock( &li->li_dbcache_mutex );
+       if ( --db->dbc_refcnt == 0 ) {
+               ldap_pvt_thread_cond_signal( &li->li_dbcache_cv );
+       }
+       ldap_pvt_thread_mutex_unlock( &li->li_dbcache_mutex );
+}
+
+void
+bdb2i_cache_really_close( Backend *be, struct dbcache *db )
+{
+       struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+
+       /*  if in slapd, all files stay open and we have only
+               readers or one writer  */
+       if (  bdb2i_with_dbenv ) return;
+
+       ldap_pvt_thread_mutex_lock( &li->li_dbcache_mutex );
+       if ( --db->dbc_refcnt == 0 ) {
+               ldap_pvt_thread_cond_signal( &li->li_dbcache_cv );
+               ldbm_close( db->dbc_db );
+               free( db->dbc_name );
+               db->dbc_name = NULL;
+       }
+       ldap_pvt_thread_mutex_unlock( &li->li_dbcache_mutex );
+}
+
+void
+bdb2i_cache_flush_all( Backend *be )
+{
+       struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+       int             i;
+
+       /*  if in slapd, syncing is done by TP  */
+       if (  bdb2i_with_dbenv ) return;
+
+       ldap_pvt_thread_mutex_lock( &li->li_dbcache_mutex );
+       for ( i = 0; i < MAXDBCACHE; i++ ) {
+               if ( li->li_dbcache[i].dbc_name != NULL ) {
+                       Debug( LDAP_DEBUG_TRACE, "ldbm flushing db (%s)\n",
+                           li->li_dbcache[i].dbc_name, 0, 0 );
+                       ldbm_sync( li->li_dbcache[i].dbc_db );
+               }
+       }
+       ldap_pvt_thread_mutex_unlock( &li->li_dbcache_mutex );
+}
+
+Datum
+bdb2i_cache_fetch(
+    struct dbcache     *db,
+    Datum              key
+)
+{
+       Datum   data;
+
+       ldbm_datum_init( data );
+
+       data = ldbm_fetch( db->dbc_db, key );
+
+       return( data );
+}
+
+int
+bdb2i_cache_store(
+    struct dbcache     *db,
+    Datum              key,
+    Datum              data,
+    int                        flags
+)
+{
+       int     rc;
+
+#ifdef LDBM_DEBUG
+       Statslog( LDAP_DEBUG_STATS,
+               "=> bdb2i_cache_store(): key.dptr=%s, key.dsize=%d\n",
+               key.dptr, key.dsize, 0, 0, 0 );
+
+       Statslog( LDAP_DEBUG_STATS,
+               "=> bdb2i_cache_store(): key.dptr=0x%08x, data.dptr=0x%0 8x\n",
+               key.dptr, data.dptr, 0, 0, 0 );
+
+       Statslog( LDAP_DEBUG_STATS,
+               "=> bdb2i_cache_store(): data.dptr=%s, data.dsize=%d\n",
+               data.dptr, data.dsize, 0, 0, 0 );
+
+       Statslog( LDAP_DEBUG_STATS,
+               "=> bdb2i_cache_store(): flags=0x%08x\n",
+               flags, 0, 0, 0, 0 );
+#endif /* LDBM_DEBUG */
+
+       rc = ldbm_store( db->dbc_db, key, data, flags );
+
+       return( rc );
+}
+
+int
+bdb2i_cache_delete(
+    struct dbcache     *db,
+    Datum              key
+)
+{
+       int     rc;
+
+       rc = ldbm_delete( db->dbc_db, key );
+
+       return( rc );
+}
diff --git a/servers/slapd/back-bdb2/delete.c b/servers/slapd/back-bdb2/delete.c
new file mode 100644 (file)
index 0000000..241734c
--- /dev/null
@@ -0,0 +1,194 @@
+/* delete.c - bdb2 backend delete routine */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "back-bdb2.h"
+#include "proto-back-bdb2.h"
+
+static int
+bdb2i_back_delete_internal(
+    Backend    *be,
+    Connection *conn,
+    Operation  *op,
+    char       *dn
+)
+{
+       struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+       char    *matched = NULL;
+       char    *pdn = NULL;
+       Entry   *e, *p = NULL;
+       int rootlock = 0;
+       int     rc = -1;
+
+       Debug(LDAP_DEBUG_ARGS, "==> bdb2i_back_delete: %s\n", dn, 0, 0);
+
+       /* get entry with writer lock */
+       if ( (e = bdb2i_dn2entry_w( be, dn, &matched )) == NULL ) {
+               Debug(LDAP_DEBUG_ARGS, "<=- bdb2i_back_delete: no such object %s\n",
+                       dn, 0, 0);
+               send_ldap_result( conn, op, LDAP_NO_SUCH_OBJECT, matched, "" );
+               if ( matched != NULL ) {
+                       free( matched );
+               }
+               return( -1 );
+       }
+
+       /* check for deleted */
+
+       if ( bdb2i_has_children( be, e ) ) {
+               Debug(LDAP_DEBUG_ARGS, "<=- bdb2i_back_delete: non leaf %s\n",
+                       dn, 0, 0);
+               send_ldap_result( conn, op, LDAP_NOT_ALLOWED_ON_NONLEAF, "",
+                   "" );
+               goto return_results;
+       }
+
+#ifdef SLAPD_CHILD_MODIFICATION_WITH_ENTRY_ACL
+       if ( ! access_allowed( be, conn, op, e,
+               "entry", NULL, ACL_WRITE ) )
+       {
+               Debug(LDAP_DEBUG_ARGS,
+                       "<=- bdb2i_back_delete: insufficient access %s\n",
+                       dn, 0, 0);
+               send_ldap_result( conn, op, LDAP_INSUFFICIENT_ACCESS, "", "" );
+               goto return_results;
+       }
+#endif
+
+       /* delete from parent's id2children entry */
+       if( (pdn = dn_parent( be, dn )) != NULL ) {
+               if( (p = bdb2i_dn2entry_w( be, pdn, &matched )) == NULL) {
+                       Debug( LDAP_DEBUG_TRACE,
+                               "<=- bdb2i_back_delete: parent does not exist\n", 0, 0, 0);
+                       send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR,
+                               "", "");
+                       goto return_results;
+               }
+
+#ifndef SLAPD_CHILD_MODIFICATION_WITH_ENTRY_ACL
+               /* check parent for "children" acl */
+               if ( ! access_allowed( be, conn, op, p,
+                       "children", NULL, ACL_WRITE ) )
+               {
+                       Debug( LDAP_DEBUG_TRACE,
+                               "<=- bdb2i_back_delete: no access to parent\n", 0, 0, 0 );
+                       send_ldap_result( conn, op, LDAP_INSUFFICIENT_ACCESS,
+                               "", "" );
+                       goto return_results;
+               }
+#endif
+
+       } else {
+               /* no parent, must be root to delete */
+               if( ! be_isroot( be, op->o_ndn ) ) {
+                       Debug( LDAP_DEBUG_TRACE,
+                               "<=- bdb2i_back_delete: no parent & not root\n", 0, 0, 0);
+                       send_ldap_result( conn, op, LDAP_INSUFFICIENT_ACCESS,
+                               "", "");
+                       goto return_results;
+               }
+
+               ldap_pvt_thread_mutex_lock(&li->li_root_mutex);
+               rootlock = 1;
+       }
+
+       if ( bdb2i_id2children_remove( be, p, e ) != 0 ) {
+               Debug(LDAP_DEBUG_ARGS,
+                       "<=- bdb2i_back_delete: operations error %s\n",
+                       dn, 0, 0);
+               send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR, "","" );
+               goto return_results;
+       }
+
+       /* delete from dn2id mapping */
+       if ( bdb2i_dn2id_delete( be, e->e_dn ) != 0 ) {
+               Debug(LDAP_DEBUG_ARGS,
+                       "<=- bdb2i_back_delete: operations error %s\n",
+                       dn, 0, 0);
+               send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR, "", "" );
+               goto return_results;
+       }
+
+       /* delete from disk and cache */
+       if ( bdb2i_id2entry_delete( be, e ) != 0 ) {
+               Debug(LDAP_DEBUG_ARGS,
+                       "<=- bdb2i_back_delete: operations error %s\n",
+                       dn, 0, 0);
+               send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR, "", "" );
+               goto return_results;
+       }
+
+       send_ldap_result( conn, op, LDAP_SUCCESS, "", "" );
+       rc = 0;
+
+return_results:;
+       if ( pdn != NULL ) free(pdn);
+
+       if( p != NULL ) {
+               /* free parent and writer lock */
+               bdb2i_cache_return_entry_w( &li->li_cache, p );
+
+       }
+
+       if ( rootlock ) {
+               /* release root lock */
+               ldap_pvt_thread_mutex_unlock(&li->li_root_mutex);
+       }
+
+       /* free entry and writer lock */
+       bdb2i_cache_return_entry_w( &li->li_cache, e );
+
+       if ( matched != NULL ) free(matched);
+
+       return rc;
+}
+
+
+int
+bdb2_back_delete(
+    Backend    *be,
+    Connection *conn,
+    Operation  *op,
+    char       *dn
+)
+{
+       DB_LOCK  lock;
+       struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+
+       struct timeval  time1, time2;
+       char   *elapsed_time;
+       int    ret;
+
+       gettimeofday( &time1, NULL );
+
+       if ( bdb2i_enter_backend_w( &li->li_db_env, &lock ) != 0 ) {
+
+               send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR, "", "" );
+               return( -1 );
+
+       }
+
+       ret = bdb2i_back_delete_internal( be, conn, op, dn );
+
+       (void) bdb2i_leave_backend( &li->li_db_env, lock );
+
+       if ( bdb2i_do_timing ) {
+
+               gettimeofday( &time2, NULL);
+               elapsed_time = bdb2i_elapsed( time1, time2 );
+               Debug( LDAP_DEBUG_ANY, "conn=%d op=%d DEL elapsed=%s\n",
+                               conn->c_connid, op->o_opid, elapsed_time );
+               free( elapsed_time );
+
+       }
+
+       return( ret );
+}
+
+
diff --git a/servers/slapd/back-bdb2/dn2id.c b/servers/slapd/back-bdb2/dn2id.c
new file mode 100644 (file)
index 0000000..5d960e5
--- /dev/null
@@ -0,0 +1,232 @@
+/* dn2id.c - routines to deal with the dn2id index */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "back-bdb2.h"
+#include "proto-back-bdb2.h"
+
+int
+bdb2i_dn2id_add(
+    Backend    *be,
+    char       *dn,
+    ID         id
+)
+{
+       int             rc, flags;
+       struct dbcache  *db;
+       Datum           key, data;
+       struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+
+       ldbm_datum_init( key );
+       ldbm_datum_init( data );
+
+       Debug( LDAP_DEBUG_TRACE, "=> bdb2i_dn2id_add( \"%s\", %ld )\n", dn, id, 0 );
+
+       if ( (db = bdb2i_cache_open( be, "dn2id", LDBM_SUFFIX, LDBM_WRCREAT ))
+           == NULL ) {
+               Debug( LDAP_DEBUG_ANY, "Could not open/create dn2id%s\n",
+                   LDBM_SUFFIX, 0, 0 );
+               return( -1 );
+       }
+
+       dn = ch_strdup( dn );
+       dn_normalize_case( dn );
+
+       key.dptr = dn;
+       key.dsize = strlen( dn ) + 1;
+       data.dptr = (char *) &id;
+       data.dsize = sizeof(ID);
+
+       flags = LDBM_INSERT;
+       if ( li->li_dbcachewsync ) flags |= LDBM_SYNC;
+
+       rc = bdb2i_cache_store( db, key, data, flags );
+
+       free( dn );
+       bdb2i_cache_close( be, db );
+
+       Debug( LDAP_DEBUG_TRACE, "<= bdb2i_dn2id_add %d\n", rc, 0, 0 );
+       return( rc );
+}
+
+ID
+bdb2i_dn2id(
+    Backend    *be,
+    char       *dn
+)
+{
+       struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+       struct dbcache  *db;
+       ID              id;
+       Datum           key, data;
+
+       ldbm_datum_init( key );
+       ldbm_datum_init( data );
+
+       dn = ch_strdup( dn );
+       Debug( LDAP_DEBUG_TRACE, "=> bdb2i_dn2id( \"%s\" )\n", dn, 0, 0 );
+       dn_normalize_case( dn );
+
+       /* first check the cache */
+       if ( (id = bdb2i_cache_find_entry_dn2id( be, &li->li_cache, dn )) != NOID ) {
+               free( dn );
+               Debug( LDAP_DEBUG_TRACE, "<= bdb2i_dn2id %lu (in cache)\n", id,
+                       0, 0 );
+               return( id );
+       }
+
+       if ( (db = bdb2i_cache_open( be, "dn2id", LDBM_SUFFIX, LDBM_WRCREAT ))
+               == NULL ) {
+               free( dn );
+               Debug( LDAP_DEBUG_ANY, "<= bdb2i_dn2id could not open dn2id%s\n",
+                       LDBM_SUFFIX, 0, 0 );
+               return( NOID );
+       }
+
+       key.dptr = dn;
+       key.dsize = strlen( dn ) + 1;
+
+       data = bdb2i_cache_fetch( db, key );
+
+       bdb2i_cache_close( be, db );
+       free( dn );
+
+       if ( data.dptr == NULL ) {
+               Debug( LDAP_DEBUG_TRACE, "<= bdb2i_dn2id NOID\n", 0, 0, 0 );
+               return( NOID );
+       }
+
+       (void) memcpy( (char *) &id, data.dptr, sizeof(ID) );
+
+       ldbm_datum_free( db->dbc_db, data );
+
+       Debug( LDAP_DEBUG_TRACE, "<= bdb2i_dn2id %lu\n", id, 0, 0 );
+       return( id );
+}
+
+int
+bdb2i_dn2id_delete(
+    Backend    *be,
+    char       *dn
+)
+{
+       struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+       struct dbcache  *db;
+       Datum           key;
+       int             rc;
+
+       ldbm_datum_init( key );
+
+       Debug( LDAP_DEBUG_TRACE, "=> bdb2i_dn2id_delete( \"%s\" )\n", dn, 0, 0 );
+
+       if ( (db = bdb2i_cache_open( be, "dn2id", LDBM_SUFFIX, LDBM_WRCREAT ))
+           == NULL ) {
+               Debug( LDAP_DEBUG_ANY,
+                   "<= bdb2i_dn2id_delete could not open dn2id%s\n", LDBM_SUFFIX,
+                   0, 0 );
+               return( -1 );
+       }
+
+       dn = ch_strdup( dn );
+       dn_normalize_case( dn );
+       key.dptr = dn;
+       key.dsize = strlen( dn ) + 1;
+
+       rc = bdb2i_cache_delete( db, key );
+
+       free( dn );
+
+       bdb2i_cache_close( be, db );
+
+       Debug( LDAP_DEBUG_TRACE, "<= bdb2i_dn2id_delete %d\n", rc, 0, 0 );
+       return( rc );
+}
+
+/*
+ * dn2entry - look up dn in the cache/indexes and return the corresponding
+ * entry.
+ */
+
+static Entry *
+dn2entry(
+    Backend    *be,
+    char       *dn,
+    char       **matched,
+    int         rw
+)
+{
+       struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+       ID              id;
+       Entry           *e = NULL;
+       char            *pdn;
+
+       Debug(LDAP_DEBUG_TRACE, "dn2entry_%s: dn: \"%s\"\n",
+               rw ? "w" : "r", dn, 0);
+
+       *matched = NULL;
+
+       if ( (id = bdb2i_dn2id( be, dn )) != NOID &&
+               (e = bdb2i_id2entry( be, id, rw )) != NULL )
+       {
+               return( e );
+       }
+
+       if ( id != NOID ) {
+               Debug(LDAP_DEBUG_ANY,
+                       "dn2entry_%s: no entry for valid id (%lu), dn \"%s\"\n",
+                       rw ? "w" : "r", id, dn);
+               /* must have been deleted from underneath us */
+               /* treat as if NOID was found */
+       }
+
+       /* stop when we get to the suffix */
+       if ( be_issuffix( be, dn ) ) {
+               return( NULL );
+       }
+
+       /* entry does not exist - see how much of the dn does exist */
+       if ( (pdn = dn_parent( be, dn )) != NULL ) {
+               /* get entry with reader lock */
+               if ( (e = bdb2i_dn2entry_r( be, pdn, matched )) != NULL ) {
+                       if(*matched != NULL) {
+                               free(*matched);
+                       }
+                       *matched = pdn;
+                       /* free entry with reader lock */
+                       bdb2i_cache_return_entry_r( &li->li_cache, e );
+               } else {
+                       free( pdn );
+               }
+       }
+
+       return( NULL );
+}
+
+Entry *
+bdb2i_dn2entry_r(
+       Backend *be,
+       char    *dn,
+       char    **matched
+)
+{
+       return( dn2entry( be, dn, matched, 0 ) );
+}
+
+Entry *
+bdb2i_dn2entry_w(
+       Backend *be,
+       char    *dn,
+       char    **matched
+)
+{
+       return( dn2entry( be, dn, matched, 1 ) );
+}
+
+
+
diff --git a/servers/slapd/back-bdb2/external.h b/servers/slapd/back-bdb2/external.h
new file mode 100644 (file)
index 0000000..0617d48
--- /dev/null
@@ -0,0 +1,59 @@
+#ifndef _BDB2_EXTERNAL_H
+#define _BDB2_EXTERNAL_H
+
+LDAP_BEGIN_DECL
+
+extern int     bdb2_back_initialize LDAP_P(( BackendInfo *bi ));
+extern int     bdb2_back_open LDAP_P(( BackendInfo *bi ));
+extern int     bdb2_back_close LDAP_P(( BackendInfo *bi ));
+extern int     bdb2_back_destroy LDAP_P(( BackendInfo *bi ));
+
+extern int     bdb2_back_db_init LDAP_P(( BackendDB *bd ));
+extern int     bdb2_back_db_open LDAP_P(( BackendDB *bd ));
+extern int     bdb2_back_db_close LDAP_P(( BackendDB *bd ));
+extern int     bdb2_back_db_destroy LDAP_P(( BackendDB *bd ));
+
+extern int     bdb2_back_db_config LDAP_P(( BackendDB *bd,
+       char *fname, int lineno, int argc, char **argv ));
+
+extern int bdb2_back_bind LDAP_P(( BackendDB *bd,
+       Connection *conn, Operation *op,
+       char *dn, int method, struct berval *cred, char** edn ));
+
+extern int     bdb2_back_unbind LDAP_P(( BackendDB *bd,
+       Connection *conn, Operation *op ));
+
+extern int     bdb2_back_search LDAP_P(( BackendDB *bd,
+       Connection *conn, Operation *op,
+       char *base, int scope, int deref, int sizelimit, int timelimit,
+       Filter *filter, char *filterstr, char **attrs, int attrsonly ));
+
+extern int     bdb2_back_compare LDAP_P((BackendDB *bd,
+       Connection *conn, Operation *op,
+       char *dn, Ava   *ava ));
+
+extern int     bdb2_back_modify LDAP_P(( BackendDB *bd,
+       Connection *conn, Operation *op,
+       char *dn, LDAPModList *ml ));
+
+extern int     bdb2_back_modrdn LDAP_P(( BackendDB *bd,
+       Connection *conn, Operation *op,
+       char *dn, char*newrdn, int deleteoldrdn ));
+
+extern int     bdb2_back_add LDAP_P(( BackendDB *bd,
+       Connection *conn, Operation *op, Entry *e ));
+
+extern int     bdb2_back_delete LDAP_P(( BackendDB *bd,
+       Connection *conn, Operation *op, char *dn ));
+
+extern int     bdb2_back_abandon LDAP_P(( BackendDB *bd,
+       Connection *conn, Operation *op, int msgid ));
+
+extern int     bdb2_back_group LDAP_P(( BackendDB *bd,
+       Entry *target, char* gr_ndn, char* op_ndn,
+       char* objectclassValue, char* groupattrName));
+
+LDAP_END_DECL
+
+#endif /* _BDB2_EXTERNAL_H */
+
diff --git a/servers/slapd/back-bdb2/filterindex.c b/servers/slapd/back-bdb2/filterindex.c
new file mode 100644 (file)
index 0000000..71b3556
--- /dev/null
@@ -0,0 +1,353 @@
+/* filterindex.c - generate the list of candidate entries from a filter */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "back-bdb2.h"
+
+static ID_BLOCK        *ava_candidates( Backend *be, Ava *ava, int type );
+static ID_BLOCK        *presence_candidates( Backend *be, char *type );
+static ID_BLOCK        *approx_candidates( Backend *be, Ava *ava );
+static ID_BLOCK        *list_candidates( Backend *be, Filter *flist, int ftype );
+static ID_BLOCK        *substring_candidates( Backend *be, Filter *f );
+static ID_BLOCK        *substring_comp_candidates( Backend *be, char *type, char *val, int prepost );
+
+/*
+ * test_filter - test a filter against a single entry.
+ * returns     0       filter matched
+ *             -1      filter did not match
+ *             >0      an ldap error code
+ */
+
+ID_BLOCK *
+bdb2i_filter_candidates(
+    Backend    *be,
+    Filter     *f
+)
+{
+       ID_BLOCK        *result, *tmp1, *tmp2;
+
+       Debug( LDAP_DEBUG_TRACE, "=> bdb2i_filter_candidates\n", 0, 0, 0 );
+
+       result = NULL;
+       switch ( f->f_choice ) {
+       case LDAP_FILTER_EQUALITY:
+               Debug( LDAP_DEBUG_FILTER, "\tEQUALITY\n", 0, 0, 0 );
+               result = ava_candidates( be, &f->f_ava, LDAP_FILTER_EQUALITY );
+               break;
+
+       case LDAP_FILTER_SUBSTRINGS:
+               Debug( LDAP_DEBUG_FILTER, "\tSUBSTRINGS\n", 0, 0, 0 );
+               result = substring_candidates( be, f );
+               break;
+
+       case LDAP_FILTER_GE:
+               Debug( LDAP_DEBUG_FILTER, "\tGE\n", 0, 0, 0 );
+               result = ava_candidates( be, &f->f_ava, LDAP_FILTER_GE );
+               break;
+
+       case LDAP_FILTER_LE:
+               Debug( LDAP_DEBUG_FILTER, "\tLE\n", 0, 0, 0 );
+               result = ava_candidates( be, &f->f_ava, LDAP_FILTER_LE );
+               break;
+
+       case LDAP_FILTER_PRESENT:
+               Debug( LDAP_DEBUG_FILTER, "\tPRESENT\n", 0, 0, 0 );
+               result = presence_candidates( be, f->f_type );
+               break;
+
+       case LDAP_FILTER_APPROX:
+               Debug( LDAP_DEBUG_FILTER, "\tAPPROX\n", 0, 0, 0 );
+               result = approx_candidates( be, &f->f_ava );
+               break;
+
+       case LDAP_FILTER_AND:
+               Debug( LDAP_DEBUG_FILTER, "\tAND\n", 0, 0, 0 );
+               result = list_candidates( be, f->f_and, LDAP_FILTER_AND );
+               break;
+
+       case LDAP_FILTER_OR:
+               Debug( LDAP_DEBUG_FILTER, "\tOR\n", 0, 0, 0 );
+               result = list_candidates( be, f->f_or, LDAP_FILTER_OR );
+               break;
+
+       case LDAP_FILTER_NOT:
+               Debug( LDAP_DEBUG_FILTER, "\tNOT\n", 0, 0, 0 );
+               tmp1 = bdb2i_idl_allids( be );
+               tmp2 = bdb2i_filter_candidates( be, f->f_not );
+               result = bdb2i_idl_notin( be, tmp1, tmp2 );
+               bdb2i_idl_free( tmp2 );
+               bdb2i_idl_free( tmp1 );
+               break;
+       }
+
+       Debug( LDAP_DEBUG_TRACE, "<= bdb2i_filter_candidates %lu\n",
+           result ? ID_BLOCK_NIDS(result) : 0, 0, 0 );
+       return( result );
+}
+
+static ID_BLOCK *
+ava_candidates(
+    Backend    *be,
+    Ava                *ava,
+    int                type
+)
+{
+       ID_BLOCK        *idl;
+
+       Debug( LDAP_DEBUG_TRACE, "=> ava_candidates 0x%x\n", type, 0, 0 );
+
+       switch ( type ) {
+       case LDAP_FILTER_EQUALITY:
+               idl = bdb2i_index_read( be, ava->ava_type, INDEX_EQUALITY,
+                   ava->ava_value.bv_val );
+               break;
+
+       case LDAP_FILTER_GE:
+               idl = bdb2i_idl_allids( be );
+               break;
+
+       case LDAP_FILTER_LE:
+               idl = bdb2i_idl_allids( be );
+               break;
+       }
+
+       Debug( LDAP_DEBUG_TRACE, "<= ava_candidates %lu\n",
+           idl ? ID_BLOCK_NIDS(idl) : 0, 0, 0 );
+       return( idl );
+}
+
+static ID_BLOCK *
+presence_candidates(
+    Backend    *be,
+    char       *type
+)
+{
+       ID_BLOCK        *idl;
+
+       Debug( LDAP_DEBUG_TRACE, "=> presence_candidates\n", 0, 0, 0 );
+
+       idl = bdb2i_index_read( be, type, 0, "*" );
+
+       Debug( LDAP_DEBUG_TRACE, "<= presence_candidates %lu\n",
+           idl ? ID_BLOCK_NIDS(idl) : 0, 0, 0 );
+       return( idl );
+}
+
+static ID_BLOCK *
+approx_candidates(
+    Backend    *be,
+    Ava                *ava
+)
+{
+       char    *w, *c;
+       ID_BLOCK        *idl, *tmp;
+
+       Debug( LDAP_DEBUG_TRACE, "=> approx_candidates\n", 0, 0, 0 );
+
+       idl = NULL;
+       for ( w = first_word( ava->ava_value.bv_val ); w != NULL;
+           w = next_word( w ) ) {
+               c = phonetic( w );
+               if ( (tmp = bdb2i_index_read( be, ava->ava_type, INDEX_APPROX, c ))
+                   == NULL ) {
+                       free( c );
+                       bdb2i_idl_free( idl );
+                       Debug( LDAP_DEBUG_TRACE, "<= approx_candidates NULL\n",
+                           0, 0, 0 );
+                       return( NULL );
+               }
+               free( c );
+
+               if ( idl == NULL ) {
+                       idl = tmp;
+               } else {
+                       idl = bdb2i_idl_intersection( be, idl, tmp );
+               }
+       }
+
+       Debug( LDAP_DEBUG_TRACE, "<= approx_candidates %lu\n",
+           idl ? ID_BLOCK_NIDS(idl) : 0, 0, 0 );
+       return( idl );
+}
+
+static ID_BLOCK *
+list_candidates(
+    Backend    *be,
+    Filter     *flist,
+    int                ftype
+)
+{
+       ID_BLOCK        *idl, *tmp, *tmp2;
+       Filter  *f;
+
+       Debug( LDAP_DEBUG_TRACE, "=> list_candidates 0x%x\n", ftype, 0, 0 );
+
+       idl = NULL;
+       for ( f = flist; f != NULL; f = f->f_next ) {
+               if ( (tmp = bdb2i_filter_candidates( be, f )) == NULL &&
+                   ftype == LDAP_FILTER_AND ) {
+                               Debug( LDAP_DEBUG_TRACE,
+                                   "<= list_candidates NULL\n", 0, 0, 0 );
+                               bdb2i_idl_free( idl );
+                               return( NULL );
+               }
+
+               tmp2 = idl;
+               if ( idl == NULL ) {
+                       idl = tmp;
+               } else if ( ftype == LDAP_FILTER_AND ) {
+                       idl = bdb2i_idl_intersection( be, idl, tmp );
+                       bdb2i_idl_free( tmp );
+                       bdb2i_idl_free( tmp2 );
+               } else {
+                       idl = bdb2i_idl_union( be, idl, tmp );
+                       bdb2i_idl_free( tmp );
+                       bdb2i_idl_free( tmp2 );
+               }
+       }
+
+       Debug( LDAP_DEBUG_TRACE, "<= list_candidates %lu\n",
+           idl ? ID_BLOCK_NIDS(idl) : 0, 0, 0 );
+       return( idl );
+}
+
+static ID_BLOCK *
+substring_candidates(
+    Backend    *be,
+    Filter     *f
+)
+{
+       int     i;
+       ID_BLOCK        *idl, *tmp, *tmp2;
+
+       Debug( LDAP_DEBUG_TRACE, "=> substring_candidates\n", 0, 0, 0 );
+
+       idl = NULL;
+
+       /* initial */
+       if ( f->f_sub_initial != NULL ) {
+               if ( (int) strlen( f->f_sub_initial ) < SUBLEN - 1 ) {
+                       idl = bdb2i_idl_allids( be );
+               } else if ( (idl = substring_comp_candidates( be, f->f_sub_type,
+                   f->f_sub_initial, '^' )) == NULL ) {
+                       return( NULL );
+               }
+       }
+
+       /* final */
+       if ( f->f_sub_final != NULL ) {
+               if ( (int) strlen( f->f_sub_final ) < SUBLEN - 1 ) {
+                       tmp = bdb2i_idl_allids( be );
+               } else if ( (tmp = substring_comp_candidates( be, f->f_sub_type,
+                   f->f_sub_final, '$' )) == NULL ) {
+                       bdb2i_idl_free( idl );
+                       return( NULL );
+               }
+
+               if ( idl == NULL ) {
+                       idl = tmp;
+               } else {
+                       tmp2 = idl;
+                       idl = bdb2i_idl_intersection( be, idl, tmp );
+                       bdb2i_idl_free( tmp );
+                       bdb2i_idl_free( tmp2 );
+               }
+       }
+
+       for ( i = 0; f->f_sub_any != NULL && f->f_sub_any[i] != NULL; i++ ) {
+               if ( (int) strlen( f->f_sub_any[i] ) < SUBLEN ) {
+                       tmp = bdb2i_idl_allids( be );
+               } else if ( (tmp = substring_comp_candidates( be, f->f_sub_type,
+                   f->f_sub_any[i], 0 )) == NULL ) {
+                       bdb2i_idl_free( idl );
+                       return( NULL );
+               }
+
+               if ( idl == NULL ) {
+                       idl = tmp;
+               } else {
+                       tmp2 = idl;
+                       idl = bdb2i_idl_intersection( be, idl, tmp );
+                       bdb2i_idl_free( tmp );
+                       bdb2i_idl_free( tmp2 );
+               }
+       }
+
+       Debug( LDAP_DEBUG_TRACE, "<= substring_candidates %lu\n",
+           idl ? ID_BLOCK_NIDS(idl) : 0, 0, 0 );
+       return( idl );
+}
+
+static ID_BLOCK *
+substring_comp_candidates(
+    Backend    *be,
+    char       *type,
+    char       *val,
+    int                prepost
+)
+{
+       int     i, len;
+       ID_BLOCK        *idl, *tmp, *tmp2;
+       char    *p;
+       char    buf[SUBLEN + 1];
+
+       Debug( LDAP_DEBUG_TRACE, "=> substring_comp_candidates\n", 0, 0, 0 );
+
+       len = strlen( val );
+       idl = NULL;
+
+       /* prepend ^ for initial substring */
+       if ( prepost == '^' ) {
+               buf[0] = '^';
+               for ( i = 0; i < SUBLEN - 1; i++ ) {
+                       buf[i + 1] = val[i];
+               }
+               buf[SUBLEN] = '\0';
+
+               if ( (idl = bdb2i_index_read( be, type, INDEX_SUB, buf )) == NULL ) {
+                       return( NULL );
+               }
+       } else if ( prepost == '$' ) {
+               p = val + len - SUBLEN + 1;
+               for ( i = 0; i < SUBLEN - 1; i++ ) {
+                       buf[i] = p[i];
+               }
+               buf[SUBLEN - 1] = '$';
+               buf[SUBLEN] = '\0';
+
+               if ( (idl = bdb2i_index_read( be, type, INDEX_SUB, buf )) == NULL ) {
+                       return( NULL );
+               }
+       }
+
+       for ( p = val; p < (val + len - SUBLEN + 1); p++ ) {
+               for ( i = 0; i < SUBLEN; i++ ) {
+                       buf[i] = p[i];
+               }
+               buf[SUBLEN] = '\0';
+
+               if ( (tmp = bdb2i_index_read( be, type, INDEX_SUB, buf )) == NULL ) {
+                       bdb2i_idl_free( idl );
+                       return( NULL );
+               }
+
+               if ( idl == NULL ) {
+                       idl = tmp;
+               } else {
+                       tmp2 = idl;
+                       idl = bdb2i_idl_intersection( be, idl, tmp );
+                       bdb2i_idl_free( tmp );
+                       bdb2i_idl_free( tmp2 );
+               }
+       }
+
+       Debug( LDAP_DEBUG_TRACE, "<= substring_comp_candidates %lu\n",
+           idl ? ID_BLOCK_NIDS(idl) : 0, 0, 0 );
+       return( idl );
+}
diff --git a/servers/slapd/back-bdb2/group.c b/servers/slapd/back-bdb2/group.c
new file mode 100644 (file)
index 0000000..959699a
--- /dev/null
@@ -0,0 +1,171 @@
+/* group.c - bdb2 backend acl group routine */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "back-bdb2.h"
+#include "proto-back-bdb2.h"
+
+
+#ifdef SLAPD_ACLGROUPS
+/* return 0 IFF op_dn is a value in member attribute
+ * of entry with gr_dn AND that entry has an objectClass
+ * value of groupOfNames
+ */
+static int
+bdb2i_back_group_internal(
+       Backend *be,
+       Entry   *target,
+       char    *gr_ndn,
+       char    *op_ndn,
+       char    *objectclassValue,
+       char    *groupattrName
+)
+{
+        struct ldbminfo *li = (struct ldbminfo *) be->be_private;    
+        Entry        *e;
+        char        *matched;
+        Attribute   *objectClass;
+        Attribute   *member;
+        int          rc;
+
+       Debug( LDAP_DEBUG_TRACE,
+               "=> bdb2i_back_group: gr dn: \"%s\"\n",
+               gr_ndn, 0, 0 ); 
+       Debug( LDAP_DEBUG_TRACE,
+               "=> bdb2i_back_group: op dn: \"%s\"\n",
+               op_ndn, 0, 0 ); 
+       Debug( LDAP_DEBUG_TRACE,
+               "=> bdb2i_back_group: objectClass: \"%s\" attrName: \"%s\"\n", 
+               objectclassValue, groupattrName, 0 ); 
+
+       Debug( LDAP_DEBUG_TRACE,
+               "=> bdb2i_back_group: tr dn: \"%s\"\n",
+               target->e_ndn, 0, 0 ); 
+
+       if (strcmp(target->e_ndn, gr_ndn) == 0) {
+               /* we already have a LOCKED copy of the entry */
+               e = target;
+               Debug( LDAP_DEBUG_ARGS,
+                       "=> bdb2i_back_group: target is group: \"%s\"\n",
+                       gr_ndn, 0, 0 ); 
+       } else {
+               /* can we find group entry with reader lock */
+               if ((e = bdb2i_dn2entry_r(be, gr_ndn, &matched )) == NULL) {
+                       Debug( LDAP_DEBUG_TRACE,
+                               "=> bdb2i_back_group: cannot find group: \"%s\" matched: \"%s\"\n",
+                                       gr_ndn, (matched ? matched : ""), 0 ); 
+                       if (matched != NULL)
+                               free(matched);
+                       return( 1 );
+               }
+               Debug( LDAP_DEBUG_ARGS,
+                       "=> bdb2i_back_group: found group: \"%s\"\n",
+                       gr_ndn, 0, 0 ); 
+        }
+
+
+        /* check for deleted */
+
+        /* find it's objectClass and member attribute values
+         * make sure this is a group entry
+         * finally test if we can find op_dn in the member attribute value list *
+         */
+        
+        rc = 1;
+        if ((objectClass = attr_find(e->e_attrs, "objectclass")) == NULL)  {
+            Debug( LDAP_DEBUG_TRACE, "<= bdb2i_back_group: failed to find objectClass\n", 0, 0, 0 ); 
+        }
+        else if ((member = attr_find(e->e_attrs, groupattrName)) == NULL) {
+            Debug( LDAP_DEBUG_TRACE, "<= bdb2i_back_group: failed to find %s\n", groupattrName, 0, 0 ); 
+        }
+        else {
+            struct berval bvObjectClass;
+            struct berval bvMembers;
+
+            Debug( LDAP_DEBUG_ARGS, "<= bdb2i_back_group: found objectClass and %s\n", groupattrName, 0, 0 ); 
+
+            bvObjectClass.bv_val = objectclassValue;
+            bvObjectClass.bv_len = strlen( bvObjectClass.bv_val );         
+
+            bvMembers.bv_val = op_ndn;
+            bvMembers.bv_len = strlen( op_ndn );         
+
+            if (value_find(objectClass->a_vals, &bvObjectClass, SYNTAX_CIS, 1) != 0) {
+                Debug( LDAP_DEBUG_TRACE,
+                                       "<= bdb2i_back_group: failed to find %s in objectClass\n", 
+                        objectclassValue, 0, 0 ); 
+            }
+            else if (value_find(member->a_vals, &bvMembers, SYNTAX_CIS, 1) != 0) {
+                Debug( LDAP_DEBUG_ACL,
+                                       "<= bdb2i_back_group: \"%s\" not in \"%s\": %s\n", 
+                                       op_ndn, gr_ndn, groupattrName ); 
+            }
+            else {
+                               Debug( LDAP_DEBUG_ACL,
+                                       "<= bdb2i_back_group: \"%s\" is in \"%s\": %s\n", 
+                                       op_ndn, gr_ndn, groupattrName ); 
+                rc = 0;
+            }
+        }
+
+       if( target != e ) {
+               /* free entry and reader lock */
+               bdb2i_cache_return_entry_r( &li->li_cache, e );                 
+       }
+
+       Debug( LDAP_DEBUG_ARGS, "bdb2i_back_group: rc: %d\n", rc, 0, 0 ); 
+       return(rc);
+}
+
+
+int
+bdb2_back_group(
+       Backend *be,
+       Entry   *target,
+       char    *gr_ndn,
+       char    *op_ndn,
+       char    *objectclassValue,
+       char    *groupattrName
+)
+{
+       DB_LOCK  lock;
+       struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+
+       struct timeval  time1, time2;
+       char   *elapsed_time;
+       int    ret;
+
+       gettimeofday( &time1, NULL );
+
+       if ( bdb2i_enter_backend_r( &li->li_db_env, &lock ) != 0 ) {
+
+               return( 1 );
+
+       }
+
+       ret = bdb2i_back_group_internal( be, target, gr_ndn, op_ndn,
+                                       objectclassValue, groupattrName );
+
+       (void) bdb2i_leave_backend( &li->li_db_env, lock );
+
+       if ( bdb2i_do_timing ) {
+
+               gettimeofday( &time2, NULL);
+               elapsed_time = bdb2i_elapsed( time1, time2 );
+               Debug( LDAP_DEBUG_ANY, "GRP elapsed=%s\n",
+                               elapsed_time, 0, 0 );
+               free( elapsed_time );
+
+       }
+
+       return( ret );
+}
+
+#endif /* SLAPD_ACLGROUPS */
+
diff --git a/servers/slapd/back-bdb2/id2children.c b/servers/slapd/back-bdb2/id2children.c
new file mode 100644 (file)
index 0000000..d404e39
--- /dev/null
@@ -0,0 +1,139 @@
+/* id2children.c - routines to deal with the id2children index */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "back-bdb2.h"
+
+int
+bdb2i_id2children_add(
+    Backend    *be,
+    Entry      *p,
+    Entry      *e
+)
+{
+       struct dbcache  *db;
+       Datum           key;
+       int             len, rc;
+       ID_BLOCK                *idl;
+       char            buf[20];
+
+       ldbm_datum_init( key );
+
+       Debug( LDAP_DEBUG_TRACE, "=> bdb2i_id2children_add( %lu, %lu )\n",
+              p ? p->e_id : 0, e->e_id, 0 );
+
+       if ( (db = bdb2i_cache_open( be, "id2children", LDBM_SUFFIX,
+           LDBM_WRCREAT )) == NULL ) {
+               Debug( LDAP_DEBUG_ANY,
+                   "<= bdb2i_id2children_add -1 could not open \"id2children%s\"\n",
+                   LDBM_SUFFIX, 0, 0 );
+               return( -1 );
+       }
+
+       sprintf( buf, "%c%ld", EQ_PREFIX, p ? p->e_id : 0 );
+       key.dptr = buf;
+       key.dsize = strlen( buf ) + 1;
+
+       if ( bdb2i_idl_insert_key( be, db, key, e->e_id ) != 0 ) {
+               Debug( LDAP_DEBUG_TRACE, "<= bdb2i_id2children_add -1 (idl_insert)\n",
+                   0, 0, 0 );
+               bdb2i_cache_close( be, db );
+               return( -1 );
+       }
+
+       bdb2i_cache_close( be, db );
+
+       Debug( LDAP_DEBUG_TRACE, "<= bdb2i_id2children_add 0\n", 0, 0, 0 );
+       return( 0 );
+}
+
+
+int
+bdb2i_id2children_remove(
+    Backend    *be,
+    Entry      *p,
+    Entry      *e
+)
+{
+       struct dbcache  *db;
+       Datum           key;
+       int             len, rc;
+       ID_BLOCK                *idl;
+       char            buf[20];
+
+       Debug( LDAP_DEBUG_TRACE, "=> bdb2i_id2children_remove( %lu, %lu )\n",
+               p ? p->e_id : 0, e->e_id, 0 );
+
+       if ( (db = bdb2i_cache_open( be, "id2children", LDBM_SUFFIX,
+           LDBM_WRCREAT )) == NULL ) {
+               Debug( LDAP_DEBUG_ANY,
+                   "<= bdb2i_id2children_remove -1 could not open \"id2children%s\"\n",
+                   LDBM_SUFFIX, 0, 0 );
+               return( -1 );
+       }
+
+       ldbm_datum_init( key );
+       sprintf( buf, "%c%ld", EQ_PREFIX, p ? p->e_id : 0 );
+       key.dptr = buf;
+       key.dsize = strlen( buf ) + 1;
+
+       if ( bdb2i_idl_delete_key( be, db, key, e->e_id ) != 0 ) {
+               Debug( LDAP_DEBUG_TRACE, "<= bdb2i_id2children_remove -1 (idl_delete)\n",
+                   0, 0, 0 );
+               bdb2i_cache_close( be, db );
+               return( -1 );
+       }
+
+       bdb2i_cache_close( be, db );
+
+       Debug( LDAP_DEBUG_TRACE, "<= bdb2i_id2children_remove 0\n", 0, 0, 0 );
+       return( 0 );
+}
+
+int
+bdb2i_has_children(
+    Backend    *be,
+    Entry      *p
+)
+{
+       struct dbcache  *db;
+       Datum           key;
+       int             rc = 0;
+       ID_BLOCK                *idl;
+       char            buf[20];
+
+       ldbm_datum_init( key );
+
+       Debug( LDAP_DEBUG_TRACE, "=> bdb2i_has_children( %lu )\n", p->e_id , 0, 0 );
+
+       if ( (db = bdb2i_cache_open( be, "id2children", LDBM_SUFFIX,
+           LDBM_WRCREAT )) == NULL ) {
+               Debug( LDAP_DEBUG_ANY,
+                   "<= bdb2i_has_children -1 could not open \"id2children%s\"\n",
+                   LDBM_SUFFIX, 0, 0 );
+               return( 0 );
+       }
+
+       sprintf( buf, "%c%ld", EQ_PREFIX, p->e_id );
+       key.dptr = buf;
+       key.dsize = strlen( buf ) + 1;
+
+       idl = bdb2i_idl_fetch( be, db, key );
+
+       bdb2i_cache_close( be, db );
+
+       if( idl != NULL ) {
+               bdb2i_idl_free( idl );
+               rc = 1;
+       }
+
+       Debug( LDAP_DEBUG_TRACE, "<= bdb2i_has_children( %lu ): %s\n",
+                                       p->e_id, rc ? "yes" : "no", 0 );
+       return( rc );
+}
diff --git a/servers/slapd/back-bdb2/id2entry.c b/servers/slapd/back-bdb2/id2entry.c
new file mode 100644 (file)
index 0000000..d999c54
--- /dev/null
@@ -0,0 +1,174 @@
+/* id2entry.c - routines to deal with the id2entry index */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "back-bdb2.h"
+
+int
+bdb2i_id2entry_add( Backend *be, Entry *e )
+{
+       struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+       struct dbcache  *db;
+       Datum           key, data;
+       int             len, rc, flags;
+
+       ldbm_datum_init( key );
+       ldbm_datum_init( data );
+
+       Debug( LDAP_DEBUG_TRACE, "=> bdb2i_id2entry_add( %lu, \"%s\" )\n", e->e_id,
+           e->e_dn, 0 );
+
+       if ( (db = bdb2i_cache_open( be, "id2entry", LDBM_SUFFIX, LDBM_WRCREAT ))
+           == NULL ) {
+               Debug( LDAP_DEBUG_ANY, "Could not open/create id2entry%s\n",
+                   LDBM_SUFFIX, 0, 0 );
+               return( -1 );
+       }
+
+       key.dptr = (char *) &e->e_id;
+       key.dsize = sizeof(ID);
+
+       ldap_pvt_thread_mutex_lock( &entry2str_mutex );
+       data.dptr = entry2str( e, &len, 1 );
+       data.dsize = len + 1;
+
+       /* store it */
+       flags = LDBM_REPLACE;
+       if ( li->li_dbcachewsync ) flags |= LDBM_SYNC;
+       rc = bdb2i_cache_store( db, key, data, flags );
+
+       ldap_pvt_thread_mutex_unlock( &entry2str_mutex );
+
+       bdb2i_cache_close( be, db );
+       (void) bdb2i_cache_add_entry_lock( &li->li_cache, e, 0 );
+
+       Debug( LDAP_DEBUG_TRACE, "<= bdb2i_id2entry_add %d\n", rc, 0, 0 );
+
+       /* XXX should entries be born locked, i.e. apply writer lock here? */
+       return( rc );
+}
+
+int
+bdb2i_id2entry_delete( Backend *be, Entry *e )
+{
+       struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+       struct dbcache  *db;
+       Datum           key;
+       int             rc;
+
+       Debug(LDAP_DEBUG_TRACE, "=> bdb2i_id2entry_delete( %lu, \"%s\" )\n", e->e_id,
+           e->e_dn, 0 );
+
+#ifdef LDAP_DEBUG
+       /* check for writer lock */
+       assert(ldap_pvt_thread_rdwr_writers(&e->e_rdwr));
+#endif
+
+       ldbm_datum_init( key );
+
+       if ( (db = bdb2i_cache_open( be, "id2entry", LDBM_SUFFIX, LDBM_WRCREAT ))
+               == NULL ) {
+               Debug( LDAP_DEBUG_ANY, "Could not open/create id2entry%s\n",
+                   LDBM_SUFFIX, 0, 0 );
+               return( -1 );
+       }
+
+       if ( bdb2i_cache_delete_entry( &li->li_cache, e ) != 0 ) {
+               Debug(LDAP_DEBUG_ANY, "could not delete %lu (%s) from cache\n",
+                   e->e_id, e->e_dn, 0 );
+       }
+
+       key.dptr = (char *) &e->e_id;
+       key.dsize = sizeof(ID);
+
+       rc = bdb2i_cache_delete( db, key );
+
+       bdb2i_cache_close( be, db );
+
+       Debug( LDAP_DEBUG_TRACE, "<= bdb2i_id2entry_delete %d\n", rc, 0, 0 );
+       return( rc );
+}
+
+/* XXX returns entry with reader/writer lock */
+Entry *
+bdb2i_id2entry( Backend *be, ID id, int rw )
+{
+       struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+       struct dbcache  *db;
+       Datum           key, data;
+       Entry           *e;
+
+       ldbm_datum_init( key );
+       ldbm_datum_init( data );
+
+       Debug( LDAP_DEBUG_TRACE, "=> bdb2i_id2entry_%s( %ld )\n",
+               rw ? "w" : "r", id, 0 );
+
+       if ( (e = bdb2i_cache_find_entry_id( &li->li_cache, id, rw )) != NULL ) {
+               Debug( LDAP_DEBUG_TRACE, "<= bdb2i_id2entry_%s 0x%lx (cache)\n",
+                       rw ? "w" : "r", (unsigned long)e, 0 );
+               return( e );
+       }
+
+       if ( (db = bdb2i_cache_open( be, "id2entry", LDBM_SUFFIX, LDBM_WRCREAT ))
+               == NULL ) {
+               Debug( LDAP_DEBUG_ANY, "Could not open id2entry%s\n",
+                   LDBM_SUFFIX, 0, 0 );
+               return( NULL );
+       }
+
+       key.dptr = (char *) &id;
+       key.dsize = sizeof(ID);
+
+       data = bdb2i_cache_fetch( db, key );
+
+       if ( data.dptr == NULL ) {
+               Debug( LDAP_DEBUG_TRACE, "<= bdb2i_id2entry_%s( %ld ) not found\n",
+                       rw ? "w" : "r", id, 0 );
+               bdb2i_cache_close( be, db );
+               return( NULL );
+       }
+
+       e = str2entry( data.dptr );
+
+       ldbm_datum_free( db->dbc_db, data );
+       bdb2i_cache_close( be, db );
+
+       if ( e == NULL ) {
+               Debug( LDAP_DEBUG_TRACE, "<= bdb2i_id2entry_%s( %ld )  (failed)\n",
+                       rw ? "w" : "r", id, 0 );
+               return( NULL );
+       }
+
+       /* acquire required reader/writer lock */
+       if (entry_rdwr_lock(e, rw)) {
+               /* XXX set DELETE flag?? */
+               entry_free(e);
+               return(NULL);
+       }
+
+       e->e_id = id;
+       (void) bdb2i_cache_add_entry_lock( &li->li_cache, e, 0 );
+
+       Debug( LDAP_DEBUG_TRACE, "<= bdb2i_id2entry_%s( %ld ) (disk)\n",
+               rw ? "w" : "r", id, 0 );
+       return( e );
+}
+
+Entry *
+bdb2i_id2entry_r( Backend *be, ID id )
+{
+       return( bdb2i_id2entry( be, id, 0 ) );
+}
+
+Entry *
+bdb2i_id2entry_w( Backend *be, ID id )
+{
+       return( bdb2i_id2entry( be, id, 1 ) );
+}
+
diff --git a/servers/slapd/back-bdb2/idl.c b/servers/slapd/back-bdb2/idl.c
new file mode 100644 (file)
index 0000000..d9be424
--- /dev/null
@@ -0,0 +1,1032 @@
+/* idl.c - ldap id list handling routines */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "ldapconfig.h"
+#include "slap.h"
+#include "back-bdb2.h"
+
+static ID_BLOCK* idl_dup( ID_BLOCK *idl );
+
+/* Allocate an ID_BLOCK with room for nids ids */
+ID_BLOCK *
+bdb2i_idl_alloc( int nids )
+{
+       ID_BLOCK        *new;
+
+       /* nmax + nids + space for the ids */
+       new = (ID_BLOCK *) ch_calloc( (ID_BLOCK_IDS_OFFSET + nids), sizeof(ID) );
+       ID_BLOCK_NMAX(new) = nids;
+       ID_BLOCK_NIDS(new) = 0;
+
+       return( new );
+}
+
+
+/* Allocate an empty ALLIDS ID_BLOCK */
+ID_BLOCK       *
+bdb2i_idl_allids( Backend *be )
+{
+       ID_BLOCK        *idl;
+
+       idl = bdb2i_idl_alloc( 0 );
+       ID_BLOCK_NMAX(idl) = ID_BLOCK_ALLIDS_VALUE;
+       ID_BLOCK_NIDS(idl) = bdb2i_next_id_get( be );
+
+       return( idl );
+}
+
+
+/* Free an ID_BLOCK */
+void
+bdb2i_idl_free( ID_BLOCK *idl )
+{
+       if ( idl == NULL ) {
+               Debug( LDAP_DEBUG_TRACE,
+                       "bdb2i_idl_free: called with NULL pointer\n",
+                       0, 0, 0 );
+               return;
+       }
+
+       free( (char *) idl );
+}
+
+
+/* Fetch an single ID_BLOCK from the cache */
+static ID_BLOCK *
+idl_fetch_one(
+    Backend            *be,
+    struct dbcache     *db,
+    Datum              key
+)
+{
+       Datum   data;
+       ID_BLOCK        *idl;
+
+       ldbm_datum_init( data );
+
+       /* Debug( LDAP_DEBUG_TRACE, "=> idl_fetch_one\n", 0, 0, 0 ); */
+
+       data = bdb2i_cache_fetch( db, key );
+
+       if( data.dptr == NULL ) {
+               return NULL;
+       }
+
+       idl = idl_dup( (ID_BLOCK *) data.dptr);
+       ldbm_datum_free( db->dbc_db, data );
+
+       return( idl );
+}
+
+
+/* Fetch a set of ID_BLOCKs from the cache
+ *     if not INDIRECT
+ *             if block return is an ALLIDS block,
+ *                     return an new ALLIDS block
+ *             otherwise
+ *                     return block
+ *     construct super block from all blocks referenced by INDIRECT block
+ *     return super block
+ */
+ID_BLOCK *
+bdb2i_idl_fetch(
+    Backend            *be,
+    struct dbcache     *db,
+    Datum              key
+)
+{
+       Datum   data;
+       ID_BLOCK        *idl;
+       ID_BLOCK        **tmp;
+       char    *kstr;
+       int     i, nids;
+
+       idl = idl_fetch_one( be, db, key );
+
+       if ( idl == NULL ) { 
+               return NULL;
+       }
+
+       if ( ID_BLOCK_ALLIDS(idl) ) {
+               /* all ids block */
+               /* make sure we have the current value of highest id */
+               bdb2i_idl_free( idl );
+               idl = bdb2i_idl_allids( be );
+
+               return( idl );
+       }
+
+       if ( ! ID_BLOCK_INDIRECT( idl ) ) {
+               /* regular block */
+               return( idl );
+       }
+
+       /*
+        * this is an indirect block which points to other blocks.
+        * we need to read in all the blocks it points to and construct
+        * a big id list containing all the ids, which we will return.
+        */
+
+       /* count the number of blocks & allocate space for pointers to them */
+       for ( i = 0; !ID_BLOCK_NOID(idl, i); i++ )
+               ;       /* NULL */
+       tmp = (ID_BLOCK **) ch_malloc( (i + 1) * sizeof(ID_BLOCK *) );
+
+       /* read in all the blocks */
+       kstr = (char *) ch_malloc( key.dsize + 20 );
+       nids = 0;
+       for ( i = 0; !ID_BLOCK_NOID(idl, i); i++ ) {
+               ldbm_datum_init( data );
+
+               sprintf( kstr, "%c%s%ld", CONT_PREFIX, key.dptr, ID_BLOCK_ID(idl, i) );
+               data.dptr = kstr;
+               data.dsize = strlen( kstr ) + 1;
+
+               if ( (tmp[i] = idl_fetch_one( be, db, data )) == NULL ) {
+                       Debug( LDAP_DEBUG_ANY,
+                           "idl_fetch of (%s) returns NULL\n", data.dptr, 0, 0 );
+                       continue;
+               }
+
+               nids += ID_BLOCK_NIDS(tmp[i]);
+       }
+       tmp[i] = NULL;
+       free( kstr );
+       bdb2i_idl_free( idl );
+
+       /* allocate space for the big block */
+       idl = bdb2i_idl_alloc( nids );
+       ID_BLOCK_NIDS(idl) = nids;
+       nids = 0;
+
+       /* copy in all the ids from the component blocks */
+       for ( i = 0; tmp[i] != NULL; i++ ) {
+               if ( tmp[i] == NULL ) {
+                       continue;
+               }
+
+               SAFEMEMCPY(
+                       (char *) &ID_BLOCK_ID(idl, nids),
+                       (char *) &ID_BLOCK_ID(tmp[i], 0),
+                   ID_BLOCK_NIDS(tmp[i]) * sizeof(ID) );
+               nids += ID_BLOCK_NIDS(tmp[i]);
+
+               bdb2i_idl_free( tmp[i] );
+       }
+       free( (char *) tmp );
+
+       Debug( LDAP_DEBUG_TRACE, "<= bdb2i_idl_fetch %lu ids (%lu max)\n",
+              ID_BLOCK_NIDS(idl), ID_BLOCK_NMAX(idl), 0 );
+       return( idl );
+}
+
+
+/* store a single block */
+static int
+idl_store(
+    Backend            *be,
+    struct dbcache     *db,
+    Datum              key, 
+    ID_BLOCK           *idl
+)
+{
+       int     rc, flags;
+       Datum   data;
+       struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+
+       ldbm_datum_init( data );
+
+       /* Debug( LDAP_DEBUG_TRACE, "=> idl_store\n", 0, 0, 0 ); */
+
+       data.dptr = (char *) idl;
+       data.dsize = (ID_BLOCK_IDS_OFFSET + ID_BLOCK_NMAX(idl)) * sizeof(ID);
+       
+#ifdef LDBM_DEBUG
+       Statslog( LDAP_DEBUG_STATS, "<= idl_store(): rc=%d\n",
+               rc, 0, 0, 0, 0 );
+#endif
+
+       flags = LDBM_REPLACE;
+       if( li->li_dbcachewsync ) flags |= LDBM_SYNC;
+       rc = bdb2i_cache_store( db, key, data, flags );
+
+       /* Debug( LDAP_DEBUG_TRACE, "<= idl_store %d\n", rc, 0, 0 ); */
+       return( rc );
+}
+
+
+/* split the block at id
+ *    locate ID greater than or equal to id.
+ */
+static void
+idl_split_block(
+    ID_BLOCK   *b,
+    ID         id,
+    ID_BLOCK   **right,
+    ID_BLOCK   **left
+)
+{
+       unsigned int    nr, nl;
+
+       /* find where to split the block *//* XXX linear search XXX */
+       for ( nr = 0; nr < ID_BLOCK_NIDS(b) && id > ID_BLOCK_ID(b, nr); nr++ )
+               ;       /* NULL */
+
+       nl = ID_BLOCK_NIDS(b) - nr;
+
+       *right = bdb2i_idl_alloc( nr == 0 ? 1 : nr );
+       *left = bdb2i_idl_alloc( nl + (nr == 0 ? 0 : 1));
+
+       /*
+        * everything before the id being inserted in the first block
+        * unless there is nothing, in which case the id being inserted
+        * goes there.
+        */
+       if ( nr == 0 ) {
+               ID_BLOCK_NIDS(*right) = 1;
+               ID_BLOCK_ID(*right, 0) = id;
+       } else {
+               SAFEMEMCPY(
+                       (char *) &ID_BLOCK_ID(*right, 0),
+                       (char *) &ID_BLOCK_ID(b, 0),
+                       nr * sizeof(ID) );
+               ID_BLOCK_NIDS(*right) = nr;
+               ID_BLOCK_ID(*left, 0) = id;
+       }
+
+       /* the id being inserted & everything after in the second block */
+       SAFEMEMCPY(
+               (char *) &ID_BLOCK_ID(*left, (nr == 0 ? 0 : 1)),
+           (char *) &ID_BLOCK_ID(b, nr),
+               nl * sizeof(ID) );
+       ID_BLOCK_NIDS(*left) = nl + (nr == 0 ? 0 : 1);
+}
+
+
+/*
+ * idl_change_first - called when an indirect block's first key has
+ * changed, meaning it needs to be stored under a new key, and the
+ * header block pointing to it needs updating.
+ */
+static int
+idl_change_first(
+    Backend            *be,
+    struct dbcache     *db,
+    Datum              hkey,           /* header block key     */
+    ID_BLOCK           *h,             /* header block         */
+    int                        pos,            /* pos in h to update   */
+    Datum              bkey,           /* data block key       */
+    ID_BLOCK           *b              /* data block           */
+)
+{
+       int     rc;
+
+       /* Debug( LDAP_DEBUG_TRACE, "=> idl_change_first\n", 0, 0, 0 ); */
+
+       /* delete old key block */
+       if ( (rc = bdb2i_cache_delete( db, bkey )) != 0 ) {
+               Debug( LDAP_DEBUG_ANY,
+                   "ldbm_delete of (%s) returns %d\n", bkey.dptr, rc,
+                   0 );
+               return( rc );
+       }
+
+       /* write block with new key */
+       sprintf( bkey.dptr, "%c%s%ld", CONT_PREFIX, hkey.dptr, ID_BLOCK_ID(b, 0) );
+       bkey.dsize = strlen( bkey.dptr ) + 1;
+       if ( (rc = idl_store( be, db, bkey, b )) != 0 ) {
+               Debug( LDAP_DEBUG_ANY,
+                   "idl_store of (%s) returns %d\n", bkey.dptr, rc, 0 );
+               return( rc );
+       }
+
+       /* update + write indirect header block */
+       ID_BLOCK_ID(h, pos) = ID_BLOCK_ID(b, 0);
+       if ( (rc = idl_store( be, db, hkey, h )) != 0 ) {
+               Debug( LDAP_DEBUG_ANY,
+                   "idl_store of (%s) returns %d\n", hkey.dptr, rc, 0 );
+               return( rc );
+       }
+
+       return( 0 );
+}
+
+
+int
+bdb2i_idl_insert_key(
+    Backend            *be,
+    struct dbcache     *db,
+    Datum              key,
+    ID                 id
+)
+{
+       int     i, j, first, rc;
+       ID_BLOCK        *idl, *tmp, *tmp2, *tmp3;
+       char    *kstr;
+       Datum   k2;
+
+       ldbm_datum_init( k2 );
+
+       if ( (idl = idl_fetch_one( be, db, key )) == NULL ) {
+#ifdef LDBM_DEBUG
+               Statslog( LDAP_DEBUG_STATS, "=> bdb2i_idl_insert_key(): no key yet\n",
+                       0, 0, 0, 0, 0 );
+#endif
+
+               idl = bdb2i_idl_alloc( 1 );
+               ID_BLOCK_ID(idl, ID_BLOCK_NIDS(idl)++) = id;
+               rc = idl_store( be, db, key, idl );
+
+               bdb2i_idl_free( idl );
+               return( rc );
+       }
+
+       if ( ID_BLOCK_ALLIDS( idl ) ) {
+               /* ALLIDS */
+               bdb2i_idl_free( idl );
+               return 0;
+       }
+
+       if ( ! ID_BLOCK_INDIRECT( idl ) ) {
+               /* regular block */
+               switch ( bdb2i_idl_insert( &idl, id, db->dbc_maxids ) ) {
+               case 0:         /* id inserted - store the updated block */
+               case 1:
+                       rc = idl_store( be, db, key, idl );
+                       break;
+
+               case 2:         /* id already there - nothing to do */
+                       rc = 0;
+                       break;
+
+               case 3:         /* id not inserted - block must be split */
+                       /* check threshold for marking this an all-id block */
+                       if ( db->dbc_maxindirect < 2 ) {
+                               bdb2i_idl_free( idl );
+                               idl = bdb2i_idl_allids( be );
+                               rc = idl_store( be, db, key, idl );
+                               bdb2i_idl_free( idl );
+
+                               return( rc );
+                       }
+
+                       idl_split_block( idl, id, &tmp, &tmp2 );
+                       bdb2i_idl_free( idl );
+
+                       /* create the header indirect block */
+                       idl = bdb2i_idl_alloc( 3 );
+                       ID_BLOCK_NMAX(idl) = 3;
+                       ID_BLOCK_NIDS(idl) = ID_BLOCK_INDIRECT_VALUE;
+                       ID_BLOCK_ID(idl, 0) = ID_BLOCK_ID(tmp, 0);
+                       ID_BLOCK_ID(idl, 1) = ID_BLOCK_ID(tmp2, 0);
+                       ID_BLOCK_ID(idl, 2) = NOID;
+
+                       /* store it */
+                       rc = idl_store( be, db, key, idl );
+
+                       /* store the first id block */
+                       kstr = (char *) ch_malloc( key.dsize + 20 );
+                       sprintf( kstr, "%c%s%ld", CONT_PREFIX, key.dptr,
+                           ID_BLOCK_ID(tmp, 0) );
+                       k2.dptr = kstr;
+                       k2.dsize = strlen( kstr ) + 1;
+                       rc = idl_store( be, db, k2, tmp );
+
+                       /* store the second id block */
+                       sprintf( kstr, "%c%s%ld", CONT_PREFIX, key.dptr,
+                           ID_BLOCK_ID(tmp2, 0) );
+                       k2.dptr = kstr;
+                       k2.dsize = strlen( kstr ) + 1;
+                       rc = idl_store( be, db, k2, tmp2 );
+
+                       free( kstr );
+                       bdb2i_idl_free( tmp );
+                       bdb2i_idl_free( tmp2 );
+                       break;
+               }
+
+               bdb2i_idl_free( idl );
+               return( rc );
+       }
+
+       /*
+        * this is an indirect block which points to other blocks.
+        * we need to read in the block into which the id should be
+        * inserted, then insert the id and store the block.  we might
+        * have to split the block if it is full, which means we also
+        * need to write a new "header" block.
+        */
+
+       /* select the block to try inserting into *//* XXX linear search XXX */
+       for ( i = 0; !ID_BLOCK_NOID(idl, i) && id > ID_BLOCK_ID(idl, i); i++ )
+               ;       /* NULL */
+       if ( i != 0 ) {
+               i--;
+               first = 0;
+       } else {
+               first = 1;
+       }
+
+       /* get the block */
+       kstr = (char *) ch_malloc( key.dsize + 20 );
+       sprintf( kstr, "%c%s%ld", CONT_PREFIX, key.dptr, ID_BLOCK_ID(idl, i) );
+       k2.dptr = kstr;
+       k2.dsize = strlen( kstr ) + 1;
+       if ( (tmp = idl_fetch_one( be, db, k2 )) == NULL ) {
+               Debug( LDAP_DEBUG_ANY, "nonexistent continuation block (%s)\n",
+                   k2.dptr, 0, 0 );
+               free( kstr );
+               return( -1 );
+       }
+
+       /* insert the id */
+       switch ( bdb2i_idl_insert( &tmp, id, db->dbc_maxids ) ) {
+       case 0:         /* id inserted ok */
+               if ( (rc = idl_store( be, db, k2, tmp )) != 0 ) {
+                       Debug( LDAP_DEBUG_ANY,
+                           "idl_store of (%s) returns %d\n", k2.dptr, rc, 0 );
+               }
+               break;
+
+       case 1:         /* id inserted - first id in block has changed */
+               /*
+                * key for this block has changed, so we have to
+                * write the block under the new key, delete the
+                * old key block + update and write the indirect
+                * header block.
+                */
+
+               rc = idl_change_first( be, db, key, idl, i, k2, tmp );
+               break;
+
+       case 2:         /* id not inserted - already there */
+               break;
+
+       case 3:         /* id not inserted - block is full */
+               /*
+                * first, see if it will fit in the next block,
+                * without splitting, unless we're trying to insert
+                * into the beginning of the first block.
+                */
+
+               /* is there a next block? */
+               if ( !first && !ID_BLOCK_NOID(idl, i + 1) ) {
+                       /* read it in */
+                       sprintf( kstr, "%c%s%ld", CONT_PREFIX, key.dptr,
+                           ID_BLOCK_ID(idl, i + 1) );
+                       k2.dptr = kstr;
+                       k2.dsize = strlen( kstr ) + 1;
+                       if ( (tmp2 = idl_fetch_one( be, db, k2 )) == NULL ) {
+                               Debug( LDAP_DEBUG_ANY,
+                                   "idl_fetch_one (%s) returns NULL\n",
+                                   k2.dptr, 0, 0 );
+                               break;
+                       }
+
+                       switch ( (rc = bdb2i_idl_insert( &tmp2, id,
+                           db->dbc_maxids )) ) {
+                       case 1:         /* id inserted first in block */
+                               rc = idl_change_first( be, db, key, idl,
+                                   i + 1, k2, tmp2 );
+                               /* FALL */
+
+                       case 2:         /* id already there - how? */
+                       case 0:         /* id inserted */
+                               if ( rc == 2 ) {
+                                       Debug( LDAP_DEBUG_ANY,
+                                           "id %lu already in next block\n",
+                                           id, 0, 0 );
+                               }
+                               free( kstr );
+                               bdb2i_idl_free( tmp );
+                               bdb2i_idl_free( tmp2 );
+                               bdb2i_idl_free( idl );
+                               return( 0 );
+
+                       case 3:         /* split the original block */
+                               bdb2i_idl_free( tmp2 );
+                               break;
+                       }
+
+               }
+
+               /*
+                * must split the block, write both new blocks + update
+                * and write the indirect header block.
+                */
+
+               /* count how many indirect blocks *//* XXX linear count XXX */
+               for ( j = 0; !ID_BLOCK_NOID(idl, j); j++ )
+                       ;       /* NULL */
+
+               /* check it against all-id thresholed */
+               if ( j + 1 > db->dbc_maxindirect ) {
+                       /*
+                        * we've passed the all-id threshold, meaning
+                        * that this set of blocks should be replaced
+                        * by a single "all-id" block.  our job: delete
+                        * all the indirect blocks, and replace the header
+                        * block by an all-id block.
+                        */
+
+                       /* delete all indirect blocks */
+                       for ( j = 0; !ID_BLOCK_NOID(idl, j); j++ ) {
+                               sprintf( kstr, "%c%s%ld", CONT_PREFIX, key.dptr,
+                                   ID_BLOCK_ID(idl, j) );
+                               k2.dptr = kstr;
+                               k2.dsize = strlen( kstr ) + 1;
+
+                               rc = bdb2i_cache_delete( db, k2 );
+                       }
+
+                       /* store allid block in place of header block */
+                       bdb2i_idl_free( idl );
+                       idl = bdb2i_idl_allids( be );
+                       rc = idl_store( be, db, key, idl );
+
+                       free( kstr );
+                       bdb2i_idl_free( idl );
+                       bdb2i_idl_free( tmp );
+                       return( rc );
+               }
+
+               idl_split_block( tmp, id, &tmp2, &tmp3 );
+               bdb2i_idl_free( tmp );
+
+               /* create a new updated indirect header block */
+               tmp = bdb2i_idl_alloc( ID_BLOCK_NMAX(idl) + 1 );
+               ID_BLOCK_NIDS(tmp) = ID_BLOCK_INDIRECT_VALUE;
+               /* everything up to the split block */
+               SAFEMEMCPY(
+                       (char *) &ID_BLOCK_ID(tmp, 0),
+                       (char *) &ID_BLOCK_ID(idl, 0),
+                   i * sizeof(ID) );
+               /* the two new blocks */
+               ID_BLOCK_ID(tmp, i) = ID_BLOCK_ID(tmp2, 0);
+               ID_BLOCK_ID(tmp, i + 1) = ID_BLOCK_ID(tmp3, 0);
+               /* everything after the split block */
+               SAFEMEMCPY(
+                       (char *) &ID_BLOCK_ID(tmp, i + 2),
+                       (char *) &ID_BLOCK_ID(idl, i + 1),
+                   (ID_BLOCK_NMAX(idl) - i - 1) * sizeof(ID) );
+
+               /* store the header block */
+               rc = idl_store( be, db, key, tmp );
+
+               /* store the first id block */
+               sprintf( kstr, "%c%s%ld", CONT_PREFIX, key.dptr,
+                   ID_BLOCK_ID(tmp2, 0) );
+               k2.dptr = kstr;
+               k2.dsize = strlen( kstr ) + 1;
+               rc = idl_store( be, db, k2, tmp2 );
+
+               /* store the second id block */
+               sprintf( kstr, "%c%s%ld", CONT_PREFIX, key.dptr,
+                   ID_BLOCK_ID(tmp3, 0) );
+               k2.dptr = kstr;
+               k2.dsize = strlen( kstr ) + 1;
+               rc = idl_store( be, db, k2, tmp3 );
+
+               bdb2i_idl_free( tmp2 );
+               bdb2i_idl_free( tmp3 );
+               break;
+       }
+
+       free( kstr );
+       bdb2i_idl_free( tmp );
+       bdb2i_idl_free( idl );
+       return( rc );
+}
+
+
+/*
+ * bdb2i_idl_insert - insert an id into an id list.
+ *
+ * returns
+ *             0       id inserted
+ *             1       id inserted, first id in block has changed
+ *             2       id not inserted, already there
+ *             3       id not inserted, block must be split
+ */
+int
+bdb2i_idl_insert( ID_BLOCK **idl, ID id, int maxids )
+{
+       unsigned int    i, j;
+
+       if ( ID_BLOCK_ALLIDS( *idl ) ) {
+               return( 2 );    /* already there */
+       }
+
+       /* is it already there? *//* XXX linear search XXX */
+       for ( i = 0; i < ID_BLOCK_NIDS(*idl) && id > ID_BLOCK_ID(*idl, i); i++ ) {
+               ;       /* NULL */
+       }
+       if ( i < ID_BLOCK_NIDS(*idl) && ID_BLOCK_ID(*idl, i) == id ) {
+               return( 2 );    /* already there */
+       }
+
+       /* do we need to make room for it? */
+       if ( ID_BLOCK_NIDS(*idl) == ID_BLOCK_NMAX(*idl) ) {
+               /* make room or indicate block needs splitting */
+               if ( ID_BLOCK_NMAX(*idl) >= maxids ) {
+                       return( 3 );    /* block needs splitting */
+               }
+
+               ID_BLOCK_NMAX(*idl) *= 2;
+               if ( ID_BLOCK_NMAX(*idl) > maxids ) {
+                       ID_BLOCK_NMAX(*idl) = maxids;
+               }
+               *idl = (ID_BLOCK *) ch_realloc( (char *) *idl,
+                   (ID_BLOCK_NMAX(*idl) + ID_BLOCK_IDS_OFFSET) * sizeof(ID) );
+       }
+
+       /* make a slot for the new id *//* XXX bubble move XXX */
+       for ( j = ID_BLOCK_NIDS(*idl); j != i; j-- ) {
+               ID_BLOCK_ID(*idl, j) = ID_BLOCK_ID(*idl, j-1);
+       }
+       ID_BLOCK_ID(*idl, i) = id;
+       ID_BLOCK_NIDS(*idl)++;
+       (void) memset(
+               (char *) &ID_BLOCK_ID((*idl), ID_BLOCK_NIDS(*idl)),
+               '\0',
+           (ID_BLOCK_NMAX(*idl) - ID_BLOCK_NIDS(*idl)) * sizeof(ID) );
+
+       return( i == 0 ? 1 : 0 );       /* inserted - first id changed or not */
+}
+
+
+int
+bdb2i_idl_delete_key (
+       Backend         *be,
+       struct dbcache  *db,
+       Datum           key,
+       ID              id
+)
+{
+       Datum  data;
+       ID_BLOCK *idl, *tmp;
+       unsigned i;
+       int j, nids;
+       char    *kstr;
+
+       if ( (idl = idl_fetch_one( be, db, key ) ) == NULL )
+       {
+               /* It wasn't found.  Hmm... */
+               return -1;
+       }
+
+       if ( ID_BLOCK_ALLIDS( idl ) ) {
+               bdb2i_idl_free( idl );
+               return 0;
+       }
+
+       if ( ! ID_BLOCK_INDIRECT( idl ) ) {
+               for ( i=0; i < ID_BLOCK_NIDS(idl); i++ ) {
+                       if ( ID_BLOCK_ID(idl, i) == id ) {
+                               if( --ID_BLOCK_NIDS(idl) == 0 ) {
+                                       bdb2i_cache_delete( db, key );
+
+                               } else {
+                                       SAFEMEMCPY (
+                                               &ID_BLOCK_ID(idl, i),
+                                               &ID_BLOCK_ID(idl, i+1),
+                                               (ID_BLOCK_NIDS(idl)-i) * sizeof(ID) );
+
+                                       ID_BLOCK_ID(idl, ID_BLOCK_NIDS(idl)) = NOID;
+
+                                       idl_store( be, db, key, idl );
+                               }
+
+                               return 0;
+                       }
+                       /*  We didn't find the ID.  Hmmm... */
+               }
+               return -1;
+       }
+       
+       /* We have to go through an indirect block and find the ID
+          in the list of IDL's
+          */
+       for ( nids = 0; !ID_BLOCK_NOID(idl, nids); nids++ )
+               ;       /* NULL */
+       kstr = (char *) ch_malloc( key.dsize + 20 );
+       for ( j = 0; !ID_BLOCK_NOID(idl, j); j++ )
+       {
+               ldbm_datum_init( data );
+               sprintf( kstr, "%c%s%ld", CONT_PREFIX, key.dptr, ID_BLOCK_ID(idl, j) );
+               data.dptr = kstr;
+               data.dsize = strlen( kstr ) + 1;
+
+               if ( (tmp = idl_fetch_one( be, db, data )) == NULL ) {
+                       Debug( LDAP_DEBUG_ANY,
+                           "bdb2i_idl_fetch of (%s) returns NULL\n", data.dptr, 0, 0 );
+                       continue;
+               }
+               /*
+                  Now try to find the ID in tmp
+               */
+               for ( i=0; i < ID_BLOCK_NIDS(tmp); i++ )
+               {
+                       if ( ID_BLOCK_ID(tmp, i) == id )
+                       {
+                               SAFEMEMCPY(
+                                       &ID_BLOCK_ID(tmp, i),
+                                       &ID_BLOCK_ID(tmp, i+1),
+                                       (ID_BLOCK_NIDS(tmp)-(i+1)) * sizeof(ID));
+                               ID_BLOCK_ID(tmp, ID_BLOCK_NIDS(tmp)-1 ) = NOID;
+                               ID_BLOCK_NIDS(tmp)--;
+
+                               if ( ID_BLOCK_NIDS(tmp) ) {
+                                       idl_store ( be, db, data, tmp );
+
+                               } else {
+                                       bdb2i_cache_delete( db, data );
+                                       SAFEMEMCPY(
+                                               &ID_BLOCK_ID(idl, j),
+                                               &ID_BLOCK_ID(idl, j+1),
+                                               (nids-(j+1)) * sizeof(ID));
+                                       ID_BLOCK_ID(idl, nids-1) = NOID;
+                                       nids--;
+                                       if ( ! nids )
+                                               bdb2i_cache_delete( db, key );
+                                       else
+                                               idl_store( be, db, key, idl );
+                               }
+                               free( kstr );
+                               return 0;
+                       }
+               }
+       }
+       free( kstr );
+       return -1;
+}
+
+
+/* return a duplicate of a single ID_BLOCK */
+static ID_BLOCK *
+idl_dup( ID_BLOCK *idl )
+{
+       ID_BLOCK        *new;
+
+       if ( idl == NULL ) {
+               return( NULL );
+       }
+
+       new = bdb2i_idl_alloc( ID_BLOCK_NMAX(idl) );
+       SAFEMEMCPY(
+               (char *) new,
+               (char *) idl,
+               (ID_BLOCK_NMAX(idl) + ID_BLOCK_IDS_OFFSET) * sizeof(ID) );
+
+       return( new );
+}
+
+
+/* return the smaller ID_BLOCK */
+static ID_BLOCK *
+idl_min( ID_BLOCK *a, ID_BLOCK *b )
+{
+       return( ID_BLOCK_NIDS(a) > ID_BLOCK_NIDS(b) ? b : a );
+}
+
+
+/*
+ * bdb2i_idl_intersection - return a intersection b
+ */
+ID_BLOCK *
+bdb2i_idl_intersection(
+    Backend    *be,
+    ID_BLOCK   *a,
+    ID_BLOCK   *b
+)
+{
+       unsigned int    ai, bi, ni;
+       ID_BLOCK                *n;
+
+       if ( a == NULL || b == NULL ) {
+               return( NULL );
+       }
+       if ( ID_BLOCK_ALLIDS( a ) ) {
+               return( idl_dup( b ) );
+       }
+       if ( ID_BLOCK_ALLIDS( b ) ) {
+               return( idl_dup( a ) );
+       }
+
+       n = idl_dup( idl_min( a, b ) );
+
+       for ( ni = 0, ai = 0, bi = 0; ai < ID_BLOCK_NIDS(a); ai++ ) {
+               for ( ;
+                       bi < ID_BLOCK_NIDS(b) && ID_BLOCK_ID(b, bi) < ID_BLOCK_ID(a, ai);
+                       bi++ )
+               {
+                       ;       /* NULL */
+               }
+
+               if ( bi == ID_BLOCK_NIDS(b) ) {
+                       break;
+               }
+
+               if ( ID_BLOCK_ID(b, bi) == ID_BLOCK_ID(a, ai) ) {
+                       ID_BLOCK_ID(n, ni++) = ID_BLOCK_ID(a, ai);
+               }
+       }
+
+       if ( ni == 0 ) {
+               bdb2i_idl_free( n );
+               return( NULL );
+       }
+       ID_BLOCK_NIDS(n) = ni;
+
+       return( n );
+}
+
+
+/*
+ * bdb2i_idl_union - return a union b
+ */
+ID_BLOCK *
+bdb2i_idl_union(
+    Backend    *be,
+    ID_BLOCK   *a,
+    ID_BLOCK   *b
+)
+{
+       unsigned int    ai, bi, ni;
+       ID_BLOCK                *n;
+
+       if ( a == NULL ) {
+               return( idl_dup( b ) );
+       }
+       if ( b == NULL ) {
+               return( idl_dup( a ) );
+       }
+       if ( ID_BLOCK_ALLIDS( a ) || ID_BLOCK_ALLIDS( b ) ) {
+               return( bdb2i_idl_allids( be ) );
+       }
+
+       if ( ID_BLOCK_NIDS(b) < ID_BLOCK_NIDS(a) ) {
+               n = a;
+               a = b;
+               b = n;
+       }
+
+       n = bdb2i_idl_alloc( ID_BLOCK_NIDS(a) + ID_BLOCK_NIDS(b) );
+
+       for ( ni = 0, ai = 0, bi = 0;
+               ai < ID_BLOCK_NIDS(a) && bi < ID_BLOCK_NIDS(b);
+               )
+       {
+               if ( ID_BLOCK_ID(a, ai) < ID_BLOCK_ID(b, bi) ) {
+                       ID_BLOCK_ID(n, ni++) = ID_BLOCK_ID(a, ai++);
+
+               } else if ( ID_BLOCK_ID(b, bi) < ID_BLOCK_ID(a, ai) ) {
+                       ID_BLOCK_ID(n, ni++) = ID_BLOCK_ID(b, bi++);
+
+               } else {
+                       ID_BLOCK_ID(n, ni++) = ID_BLOCK_ID(a, ai);
+                       ai++, bi++;
+               }
+       }
+
+       for ( ; ai < ID_BLOCK_NIDS(a); ai++ ) {
+               ID_BLOCK_ID(n, ni++) = ID_BLOCK_ID(a, ai);
+       }
+       for ( ; bi < ID_BLOCK_NIDS(b); bi++ ) {
+               ID_BLOCK_ID(n, ni++) = ID_BLOCK_ID(b, bi);
+       }
+       ID_BLOCK_NIDS(n) = ni;
+
+       return( n );
+}
+
+
+/*
+ * bdb2i_idl_notin - return a intersection ~b (or a minus b)
+ */
+ID_BLOCK *
+bdb2i_idl_notin(
+    Backend    *be,
+    ID_BLOCK   *a,
+    ID_BLOCK   *b
+)
+{
+       unsigned int    ni, ai, bi;
+       ID_BLOCK                *n;
+
+       if ( a == NULL ) {
+               return( NULL );
+       }
+       if ( b == NULL || ID_BLOCK_ALLIDS( b )) {
+               return( idl_dup( a ) );
+       }
+
+       if ( ID_BLOCK_ALLIDS( a ) ) {
+               n = bdb2i_idl_alloc( SLAPD_LDBM_MIN_MAXIDS );
+               ni = 0;
+
+               for ( ai = 1, bi = 0;
+                       ai < ID_BLOCK_NIDS(a) && ni < ID_BLOCK_NMAX(n) && bi < ID_BLOCK_NMAX(b);
+                       ai++ )
+               {
+                       if ( ID_BLOCK_ID(b, bi) == ai ) {
+                               bi++;
+                       } else {
+                               ID_BLOCK_ID(n, ni++) = ai;
+                       }
+               }
+
+               for ( ; ai < ID_BLOCK_NIDS(a) && ni < ID_BLOCK_NMAX(n); ai++ ) {
+                       ID_BLOCK_ID(n, ni++) = ai;
+               }
+
+               if ( ni == ID_BLOCK_NMAX(n) ) {
+                       bdb2i_idl_free( n );
+                       return( bdb2i_idl_allids( be ) );
+               } else {
+                       ID_BLOCK_NIDS(n) = ni;
+                       return( n );
+               }
+       }
+
+       n = idl_dup( a );
+
+       ni = 0;
+       for ( ai = 0, bi = 0; ai < ID_BLOCK_NIDS(a); ai++ ) {
+               for ( ;
+                       bi < ID_BLOCK_NIDS(b) && ID_BLOCK_ID(b, bi) < ID_BLOCK_ID(a, ai);
+                   bi++ )
+               {
+                       ;       /* NULL */
+               }
+
+               if ( bi == ID_BLOCK_NIDS(b) ) {
+                       break;
+               }
+
+               if ( ID_BLOCK_ID(b, bi) != ID_BLOCK_ID(a, ai) ) {
+                       ID_BLOCK_ID(n, ni++) = ID_BLOCK_ID(a, ai);
+               }
+       }
+
+       for ( ; ai < ID_BLOCK_NIDS(a); ai++ ) {
+               ID_BLOCK_ID(n, ni++) = ID_BLOCK_ID(a, ai);
+       }
+       ID_BLOCK_NIDS(n) = ni;
+
+       return( n );
+}
+
+
+/*     return the first ID in the block
+ *     if ALLIDS block
+ *             NIDS > 1 return 1
+ *             otherwise return NOID 
+ *     otherwise return first ID
+ */         
+ID
+bdb2i_idl_firstid( ID_BLOCK *idl )
+{
+       if ( idl == NULL || ID_BLOCK_NIDS(idl) == 0 ) {
+               return( NOID );
+       }
+
+       if ( ID_BLOCK_ALLIDS( idl ) ) { 
+               return( ID_BLOCK_NIDS(idl) > 1 ? 1 : NOID );
+       }
+
+       return( ID_BLOCK_ID(idl, 0) );
+}
+
+
+/*     return next ID after id
+ *     if ALLIDS block, increment id. 
+ *             if id < NIDS return id
+ *             otherwise NOID.
+ *     otherwise SEARCH for next id (ugh!)
+ */ 
+ID
+bdb2i_idl_nextid( ID_BLOCK *idl, ID id )
+{
+       unsigned int    i;
+
+       if ( ID_BLOCK_ALLIDS( idl ) ) {
+               return( ++id < ID_BLOCK_NIDS(idl) ? id : NOID );
+       }
+
+       for ( i = 0; i < ID_BLOCK_NIDS(idl) && ID_BLOCK_ID(idl, i) <= id; i++ ) {
+               ;       /* NULL */
+       }
+
+       if ( i >= ID_BLOCK_NIDS(idl) ) {
+               return( NOID );
+       } else {
+               return( ID_BLOCK_ID(idl, i) );
+       }
+}
diff --git a/servers/slapd/back-bdb2/index.c b/servers/slapd/back-bdb2/index.c
new file mode 100644 (file)
index 0000000..9d99a38
--- /dev/null
@@ -0,0 +1,362 @@
+/* index.c - routines for dealing with attribute indexes */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "back-bdb2.h"
+
+static int     add_value(Backend *be, struct dbcache *db, char *type, int indextype, char *val, ID id);
+static int     index2prefix(int indextype);
+
+int
+bdb2i_index_add_entry(
+    Backend    *be,
+    Entry      *e
+)
+{
+       Attribute       *ap;
+       char            *dnval;
+       struct berval   bv;
+       struct berval   *bvals[2];
+
+       Debug( LDAP_DEBUG_TRACE, "=> index_add( %ld, \"%s\" )\n", e->e_id,
+           e->e_dn, 0 );
+
+       /*
+        * dn index entry - make it look like an attribute so it works
+        * with bdb2i_index_add_values() call
+        */
+
+       bv.bv_val = ch_strdup( e->e_dn );
+       bv.bv_len = strlen( bv.bv_val );
+       (void) dn_normalize_case( bv.bv_val );
+       bvals[0] = &bv;
+       bvals[1] = NULL;
+
+       /* add the dn to the indexes */
+       bdb2i_index_add_values( be, "dn", bvals, e->e_id );
+
+       free( bv.bv_val );
+
+       /* add each attribute to the indexes */
+       for ( ap = e->e_attrs; ap != NULL; ap = ap->a_next ) {
+               bdb2i_index_add_values( be, ap->a_type, ap->a_vals, e->e_id );
+       }
+
+       Debug( LDAP_DEBUG_TRACE, "<= index_add( %ld, \"%s\" ) 0\n", e->e_id,
+           e->e_dn, 0 );
+       return( 0 );
+}
+
+int
+bdb2i_index_add_mods(
+    Backend    *be,
+    LDAPModList        *ml,
+    ID         id
+)
+{
+       int     rc;
+
+       for ( ; ml != NULL; ml = ml->ml_next ) {
+               LDAPMod *mod = &ml->ml_mod;
+
+               switch ( mod->mod_op & ~LDAP_MOD_BVALUES ) {
+               case LDAP_MOD_ADD:
+               case LDAP_MOD_REPLACE:
+                       rc = bdb2i_index_add_values( be, mod->mod_type,
+                           mod->mod_bvalues, id );
+                       break;
+
+               case LDAP_MOD_DELETE:
+                       rc = 0;
+                       break;
+               }
+
+               if ( rc != 0 ) {
+                       return( rc );
+               }
+       }
+
+       return( 0 );
+}
+
+ID_BLOCK *
+bdb2i_index_read(
+    Backend    *be,
+    char       *type,
+    int                indextype,
+    char       *val
+)
+{
+       struct dbcache  *db;
+       Datum           key;
+       ID_BLOCK                *idl;
+       int             indexmask, syntax;
+       char            prefix;
+       char            *realval, *tmpval;
+       char            buf[BUFSIZ];
+
+       ldbm_datum_init( key );
+
+       prefix = index2prefix( indextype );
+       Debug( LDAP_DEBUG_TRACE, "=> bdb2i_index_read( \"%s\" \"%c\" \"%s\" )\n",
+           type, prefix, val );
+
+       bdb2i_attr_masks( be->be_private, type, &indexmask, &syntax );
+       if ( ! (indextype & indexmask) ) {
+               idl =  bdb2i_idl_allids( be );
+               Debug( LDAP_DEBUG_TRACE,
+                   "<= bdb2i_index_read %lu candidates (allids - not indexed)\n",
+                   idl ? ID_BLOCK_NIDS(idl) : 0, 0, 0 );
+               return( idl );
+       }
+
+       attr_normalize( type );
+       if ( (db = bdb2i_cache_open( be, type, LDBM_SUFFIX, LDBM_WRCREAT ))
+           == NULL ) {
+               Debug( LDAP_DEBUG_ANY,
+                   "<= bdb2i_index_read NULL (could not open %s%s)\n", type,
+                   LDBM_SUFFIX, 0 );
+               return( NULL );
+       }
+
+       realval = val;
+       tmpval = NULL;
+       if ( prefix != UNKNOWN_PREFIX ) {
+              unsigned int     len = strlen( val );
+
+              if ( (len + 2) < sizeof(buf) ) {
+                       realval = buf;
+               } else {
+                       /* value + prefix + null */
+                       tmpval = (char *) ch_malloc( len + 2 );
+                       realval = tmpval;
+               }
+              realval[0] = prefix;
+              strcpy( &realval[1], val );
+       }
+
+       key.dptr = realval;
+       key.dsize = strlen( realval ) + 1;
+
+       idl = bdb2i_idl_fetch( be, db, key );
+      if ( tmpval != NULL ) {
+              free( tmpval );
+      }
+
+       bdb2i_cache_close( be, db );
+
+       Debug( LDAP_DEBUG_TRACE, "<= bdb2i_index_read %lu candidates\n",
+           idl ? ID_BLOCK_NIDS(idl) : 0, 0, 0 );
+       return( idl );
+}
+
+static int
+add_value(
+    Backend            *be,
+    struct dbcache     *db,
+    char               *type,
+    int                        indextype,
+    char               *val,
+    ID                 id
+)
+{
+       int     rc;
+       Datum   key;
+       ID_BLOCK        *idl;
+       char    prefix;
+       char    *realval, *tmpval, *s;
+       char    buf[BUFSIZ];
+
+       ldbm_datum_init( key );
+
+       prefix = index2prefix( indextype );
+       Debug( LDAP_DEBUG_TRACE, "=> add_value( \"%c%s\" )\n", prefix, val, 0 );
+
+       realval = val;
+       tmpval = NULL;
+       idl = NULL;
+       if ( prefix != UNKNOWN_PREFIX ) {
+              unsigned int     len = strlen( val );
+
+              if ( (len + 2) < sizeof(buf) ) {
+                       realval = buf;
+               } else {
+                       /* value + prefix + null */
+                       tmpval = (char *) ch_malloc( len + 2 );
+                       realval = tmpval;
+               }
+              realval[0] = prefix;
+              strcpy( &realval[1], val );
+       }
+
+       key.dptr = realval;
+       key.dsize = strlen( realval ) + 1;
+
+       rc = bdb2i_idl_insert_key( be, db, key, id );
+
+       if ( tmpval != NULL ) {
+               free( tmpval );
+       }
+       bdb2i_idl_free( idl );
+
+       ldap_pvt_thread_yield();
+
+       /* Debug( LDAP_DEBUG_TRACE, "<= add_value %d\n", rc, 0, 0 ); */
+       return( rc );
+}
+
+int
+bdb2i_index_add_values(
+    Backend            *be,
+    char               *type,
+    struct berval      **vals,
+    ID                 id
+)
+{
+       char            *val, *p, *code, *w;
+       unsigned        i, j, len;
+       int             indexmask, syntax;
+       char            buf[SUBLEN + 1];
+       char            vbuf[BUFSIZ];
+       char            *bigbuf;
+       struct dbcache  *db;
+
+       Debug( LDAP_DEBUG_TRACE, "=> bdb2i_index_add_values( \"%s\", %ld )\n", type,
+           id, 0 );
+
+       bdb2i_attr_masks( be->be_private, type, &indexmask, &syntax );
+       if ( indexmask == 0 ) {
+               return( 0 );
+       }
+
+       if ( (db = bdb2i_cache_open( be, type, LDBM_SUFFIX, LDBM_WRCREAT ))
+           == NULL ) {
+               Debug( LDAP_DEBUG_ANY,
+                   "<= bdb2i_index_add_values -1 (could not open/create %s%s)\n",
+                   type, LDBM_SUFFIX, 0 );
+               return( -1 );
+       }
+
+       for ( i = 0; vals[i] != NULL; i++ ) {
+               /*
+                * presence index entry
+                */
+               if ( indexmask & INDEX_PRESENCE ) {
+                       add_value( be, db, type, INDEX_PRESENCE, "*", id );
+               }
+
+               Debug( LDAP_DEBUG_TRACE, "*** bdb2i_index_add_values syntax 0x%x syntax bin 0x%x\n",
+                   syntax, SYNTAX_BIN, 0 );
+               if ( syntax & SYNTAX_BIN ) {
+                       bdb2i_cache_close( be, db );
+                       return( 0 );
+               }
+
+               bigbuf = NULL;
+               len = vals[i]->bv_len;
+
+               /* value + null */
+               if ( len + 2 > sizeof(vbuf) ) {
+                       bigbuf = (char *) ch_malloc( len + 1 );
+                       val = bigbuf;
+               } else {
+                       val = vbuf;
+               }
+               (void) memcpy( val, vals[i]->bv_val, len );
+               val[len] = '\0';
+
+               value_normalize( val, syntax );
+
+               /*
+                * equality index entry
+                */
+               if ( indexmask & INDEX_EQUALITY ) {
+                       add_value( be, db, type, INDEX_EQUALITY, val, id );
+               }
+
+               /*
+                * approximate index entry
+                */
+               if ( indexmask & INDEX_APPROX ) {
+                       for ( w = first_word( val ); w != NULL;
+                           w = next_word( w ) ) {
+                               if ( (code = phonetic( w )) != NULL ) {
+                                       add_value( be, db, type, INDEX_APPROX,
+                                           code, id );
+                                       free( code );
+                               }
+                       }
+               }
+
+               /*
+                * substrings index entry
+                */
+               if ( indexmask & INDEX_SUB ) {
+                       /* leading and trailing */
+                       if ( len > SUBLEN - 2 ) {
+                               buf[0] = '^';
+                               for ( j = 0; j < SUBLEN - 1; j++ ) {
+                                       buf[j + 1] = val[j];
+                               }
+                               buf[SUBLEN] = '\0';
+
+                               add_value( be, db, type, INDEX_SUB, buf, id );
+
+                               p = val + len - SUBLEN + 1;
+                               for ( j = 0; j < SUBLEN - 1; j++ ) {
+                                       buf[j] = p[j];
+                               }
+                               buf[SUBLEN - 1] = '$';
+                               buf[SUBLEN] = '\0';
+
+                               add_value( be, db, type, INDEX_SUB, buf, id );
+                       }
+
+                       /* any */
+                       for ( p = val; p < (val + len - SUBLEN + 1); p++ ) {
+                               for ( j = 0; j < SUBLEN; j++ ) {
+                                       buf[j] = p[j];
+                               }
+                               buf[SUBLEN] = '\0';
+
+                               add_value( be, db, type, INDEX_SUB, buf, id );
+                       }
+               }
+
+               if ( bigbuf != NULL ) {
+                       free( bigbuf );
+               }
+       }
+       bdb2i_cache_close( be, db );
+
+       return( 0 );
+}
+
+static int
+index2prefix( int indextype )
+{
+       int     prefix;
+
+       switch ( indextype ) {
+       case INDEX_EQUALITY:
+               prefix = EQ_PREFIX;
+               break;
+       case INDEX_APPROX:
+               prefix = APPROX_PREFIX;
+               break;
+       case INDEX_SUB:
+               prefix = SUB_PREFIX;
+               break;
+       default:
+               prefix = UNKNOWN_PREFIX;
+               break;
+       }
+
+       return( prefix );
+}
diff --git a/servers/slapd/back-bdb2/init.c b/servers/slapd/back-bdb2/init.c
new file mode 100644 (file)
index 0000000..6a3f55e
--- /dev/null
@@ -0,0 +1,196 @@
+/* init.c - initialize bdb2 backend */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "back-bdb2.h"
+
+
+int
+bdb2_back_initialize(
+    BackendInfo        *bi
+)
+{
+       bi->bi_open = bdb2_back_open;
+       bi->bi_config = NULL;
+       bi->bi_close = bdb2_back_close;
+       bi->bi_destroy = bdb2_back_destroy;
+
+       bi->bi_db_init = bdb2_back_db_init;
+       bi->bi_db_config = bdb2_back_db_config;
+       bi->bi_db_open = bdb2_back_db_open;
+       bi->bi_db_close = bdb2_back_db_close;
+       bi->bi_db_destroy = bdb2_back_db_destroy;
+
+       bi->bi_op_bind = bdb2_back_bind;
+       bi->bi_op_unbind = bdb2_back_unbind;
+       bi->bi_op_search = bdb2_back_search;
+       bi->bi_op_compare = bdb2_back_compare;
+       bi->bi_op_modify = bdb2_back_modify;
+       bi->bi_op_modrdn = bdb2_back_modrdn;
+       bi->bi_op_add = bdb2_back_add;
+       bi->bi_op_delete = bdb2_back_delete;
+       bi->bi_op_abandon = bdb2_back_abandon;
+
+       bi->bi_acl_group = bdb2_back_group;
+
+       return 0;
+}
+
+int
+bdb2_back_destroy(
+    BackendInfo        *bi
+)
+{
+       return 0;
+}
+
+int
+bdb2_back_open(
+    BackendInfo        *bi
+)
+{
+       int rc;
+
+       /* initialize the underlying database system */
+       rc = bdb2_initialize();
+
+       return rc;
+}
+
+int
+bdb2_back_close(
+    BackendInfo        *bi
+)
+{
+       /* close the underlying database system */
+       bdb2_shutdown();
+
+       return 0;
+}
+
+/*  BDB2 changed  */
+static int
+bdb2i_back_db_init_internal(
+    Backend    *be
+)
+{
+       struct ldbminfo *li;
+       char            *argv[ 4 ];
+       int             i;
+
+       /* allocate backend-specific stuff */
+       li = (struct ldbminfo *) ch_calloc( 1, sizeof(struct ldbminfo) );
+
+       /* arrange to read nextid later (on first request for it) */
+       li->li_nextid = NOID;
+#if    SLAPD_NEXTID_CHUNCK > 1
+       li->li_nextid_wrote = NOID
+#endif
+
+       /* default cache size */
+       li->li_cache.c_maxsize = DEFAULT_CACHE_SIZE;
+
+       /* default database cache size */
+       li->li_dbcachesize = DEFAULT_DBCACHE_SIZE;
+
+       /* default cache mode is sync on write */
+       li->li_dbcachewsync = 1;
+
+       /* default file creation mode */
+       li->li_mode = DEFAULT_MODE;
+
+       /* default database directory */
+       li->li_directory = DEFAULT_DB_DIRECTORY;
+
+       /* always index dn, id2children, objectclass (used in some searches) */
+       argv[ 0 ] = "dn";
+       argv[ 1 ] = "dn";
+       argv[ 2 ] = NULL;
+       attr_syntax_config( "ldbm dn initialization", 0, 2, argv );
+       argv[ 0 ] = "dn";
+       argv[ 1 ] = "sub";
+       argv[ 2 ] = "eq";
+       argv[ 3 ] = NULL;
+       bdb2i_attr_index_config( li, "ldbm dn initialization", 0, 3, argv, 1 );
+       argv[ 0 ] = "id2children";
+       argv[ 1 ] = "eq";
+       argv[ 2 ] = NULL;
+       bdb2i_attr_index_config( li, "ldbm id2children initialization", 0, 2, argv,
+           1 );
+       argv[ 0 ] = "objectclass";
+       argv[ 1 ] = ch_strdup( "pres,eq" );
+       argv[ 2 ] = NULL;
+       bdb2i_attr_index_config( li, "ldbm objectclass initialization", 0, 2, argv,
+           1 );
+       free( argv[ 1 ] );
+
+       /* initialize various mutex locks & condition variables */
+       ldap_pvt_thread_mutex_init( &li->li_root_mutex );
+       ldap_pvt_thread_mutex_init( &li->li_add_mutex );
+       ldap_pvt_thread_mutex_init( &li->li_cache.c_mutex );
+       ldap_pvt_thread_mutex_init( &li->li_nextid_mutex );
+       ldap_pvt_thread_mutex_init( &li->li_dbcache_mutex );
+       ldap_pvt_thread_cond_init( &li->li_dbcache_cv );
+
+       /*  initialize the TP file head  */
+       bdb2i_txn_head_init( &li->li_txn_head );
+
+       be->be_private = li;
+
+       return 0;
+}
+
+
+int
+bdb2_back_db_init(
+    Backend    *be
+)
+{
+       struct timeval  time1, time2;
+       char   *elapsed_time;
+       int    ret;
+
+       gettimeofday( &time1, NULL );
+
+       ret = bdb2i_back_db_init_internal( be );
+
+       if ( bdb2i_do_timing ) {
+
+               gettimeofday( &time2, NULL);
+               elapsed_time = bdb2i_elapsed( time1, time2 );
+               Debug( LDAP_DEBUG_ANY, "INIT elapsed=%s\n",
+                               elapsed_time, 0, 0 );
+               free( elapsed_time );
+
+       }
+
+       return( ret );
+}
+
+
+int
+bdb2_back_db_open(
+    BackendDB  *be
+)
+{
+       return 0;
+}
+
+int
+bdb2_back_db_destroy(
+    BackendDB  *be
+)
+{
+       /* should free/destroy every in be_private */
+       free( be->be_private );
+       be->be_private = NULL;
+       return 0;
+}
+
+
diff --git a/servers/slapd/back-bdb2/kerberos.c b/servers/slapd/back-bdb2/kerberos.c
new file mode 100644 (file)
index 0000000..c7a917a
--- /dev/null
@@ -0,0 +1,48 @@
+/* kerberos.c - bdb2 backend kerberos bind routines */
+
+#include "portable.h"
+
+#ifdef HAVE_KERBEROS
+
+#include <stdio.h>
+
+#include <ac/krb.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "back-bdb2.h"
+
+#define LDAP_KRB_PRINCIPAL     "ldapserver"
+
+extern char            *ldap_srvtab;
+extern Attribute       *attr_find();
+
+bdb2i_krbv4_ldap_auth(
+    Backend            *be,
+    struct berval      *cred,
+    AUTH_DAT           *ad
+)
+{
+       KTEXT_ST        k;
+       KTEXT           ktxt = &k;
+       char            instance[INST_SZ];
+       int             err;
+
+       Debug( LDAP_DEBUG_TRACE, "=> kerberosv4_ldap_auth\n", 0, 0, 0 );
+
+       SAFEMEMCPY( ktxt->dat, cred->bv_val, cred->bv_len );
+       ktxt->length = cred->bv_len;
+
+       strcpy( instance, "*" );
+       if ( (err = krb_rd_req( ktxt, LDAP_KRB_PRINCIPAL, instance, 0L, ad,
+           ldap_srvtab )) != KSUCCESS ) {
+               Debug( LDAP_DEBUG_ANY, "krb_rd_req failed (%s)\n",
+                   krb_err_txt[err], 0, 0 );
+               return( LDAP_INVALID_CREDENTIALS );
+       }
+
+       return( LDAP_SUCCESS );
+}
+
+#endif /* kerberos */
diff --git a/servers/slapd/back-bdb2/modify.c b/servers/slapd/back-bdb2/modify.c
new file mode 100644 (file)
index 0000000..f36ba6e
--- /dev/null
@@ -0,0 +1,264 @@
+/* modify.c - bdb2 backend modify routine */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "back-bdb2.h"
+#include "proto-back-bdb2.h"
+
+static int     add_values(Entry *e, LDAPMod *mod, char *dn);
+static int     delete_values(Entry *e, LDAPMod *mod, char *dn);
+static int     replace_values(Entry *e, LDAPMod *mod, char *dn);
+
+static int
+bdb2i_back_modify_internal(
+    Backend    *be,
+    Connection *conn,
+    Operation  *op,
+    char       *dn,
+    LDAPModList        *modlist
+)
+{
+       struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+       char            *matched;
+       LDAPModList     *ml;
+       Entry           *e;
+       int             i, err;
+
+       Debug(LDAP_DEBUG_ARGS, "bdb2i_back_modify:\n", 0, 0, 0);
+
+       if ( (e = bdb2i_dn2entry_w( be, dn, &matched )) == NULL ) {
+               send_ldap_result( conn, op, LDAP_NO_SUCH_OBJECT, matched,
+                   NULL );
+               if ( matched != NULL ) {
+                       free( matched );
+               }
+               return( -1 );
+       }
+
+       if ( (err = acl_check_modlist( be, conn, op, e, modlist )) != LDAP_SUCCESS ) {
+               send_ldap_result( conn, op, err, NULL, NULL );
+               goto error_return;
+       }
+
+       for ( ml = modlist; ml != NULL; ml = ml->ml_next ) {
+               LDAPMod *mod = &ml->ml_mod;
+
+               switch ( mod->mod_op & ~LDAP_MOD_BVALUES ) {
+               case LDAP_MOD_ADD:
+                       err = add_values( e, mod, op->o_ndn );
+                       break;
+
+               case LDAP_MOD_DELETE:
+                       err = delete_values( e, mod, op->o_ndn );
+                       break;
+
+               case LDAP_MOD_REPLACE:
+                       err = replace_values( e, mod, op->o_ndn );
+                       break;
+               }
+
+               if ( err != LDAP_SUCCESS ) {
+                       /* unlock entry, delete from cache */
+                       send_ldap_result( conn, op, err, NULL, NULL );
+                       goto error_return;
+               }
+       }
+
+       /* check that the entry still obeys the schema */
+       if ( global_schemacheck && oc_schema_check( e ) != 0 ) {
+               Debug( LDAP_DEBUG_ANY, "entry failed schema check\n", 0, 0, 0 );
+               send_ldap_result( conn, op, LDAP_OBJECT_CLASS_VIOLATION, NULL, NULL );
+               goto error_return;
+       }
+
+       /* check for abandon */
+       ldap_pvt_thread_mutex_lock( &op->o_abandonmutex );
+       if ( op->o_abandon ) {
+               ldap_pvt_thread_mutex_unlock( &op->o_abandonmutex );
+               goto error_return;
+       }
+       ldap_pvt_thread_mutex_unlock( &op->o_abandonmutex );
+
+       /* modify indexes */
+       if ( bdb2i_index_add_mods( be, modlist, e->e_id ) != 0 ) {
+               send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR, NULL, NULL );
+               goto error_return;
+       }
+
+       /* check for abandon */
+       ldap_pvt_thread_mutex_lock( &op->o_abandonmutex );
+       if ( op->o_abandon ) {
+               ldap_pvt_thread_mutex_unlock( &op->o_abandonmutex );
+               goto error_return;
+       }
+       ldap_pvt_thread_mutex_unlock( &op->o_abandonmutex );
+
+       /* change the entry itself */
+       if ( bdb2i_id2entry_add( be, e ) != 0 ) {
+               send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR, NULL, NULL );
+               goto error_return;
+       }
+
+       send_ldap_result( conn, op, LDAP_SUCCESS, NULL, NULL );
+       bdb2i_cache_return_entry_w( &li->li_cache, e );
+       return( 0 );
+
+error_return:;
+       bdb2i_cache_return_entry_w( &li->li_cache, e );
+       return( -1 );
+}
+
+
+int
+bdb2_back_modify(
+    Backend    *be,
+    Connection *conn,
+    Operation  *op,
+    char       *dn,
+    LDAPModList        *modlist
+)
+{
+       DB_LOCK  lock;
+       struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+
+       struct timeval  time1, time2;
+       char   *elapsed_time;
+       int    ret;
+
+       gettimeofday( &time1, NULL );
+
+       if ( bdb2i_enter_backend_w( &li->li_db_env, &lock ) != 0 ) {
+
+               send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR, "", "" );
+               return( -1 );
+
+       }
+
+       /*  check, if a new default attribute index will be created,
+               in which case we have to open the index file BEFORE TP  */
+       if ( bdb2i_with_dbenv )
+               bdb2i_check_default_attr_index_mod( li, modlist );
+
+        ret = bdb2i_back_modify_internal( be, conn, op, dn, modlist );
+
+       (void) bdb2i_leave_backend( &li->li_db_env, lock );
+
+       if ( bdb2i_do_timing ) {
+
+               gettimeofday( &time2, NULL);
+               elapsed_time = bdb2i_elapsed( time1, time2 );
+               Debug( LDAP_DEBUG_ANY, "conn=%d op=%d MOD elapsed=%s\n",
+                               conn->c_connid, op->o_opid, elapsed_time );
+               free( elapsed_time );
+
+       }
+
+       return( ret );
+}
+
+
+static int
+add_values(
+    Entry      *e,
+    LDAPMod    *mod,
+    char       *dn
+)
+{
+       int             i;
+       Attribute       *a;
+
+       /* check if the values we're adding already exist */
+       if ( (a = attr_find( e->e_attrs, mod->mod_type )) != NULL ) {
+               for ( i = 0; mod->mod_bvalues[i] != NULL; i++ ) {
+                       if ( value_find( a->a_vals, mod->mod_bvalues[i],
+                           a->a_syntax, 3 ) == 0 ) {
+                               return( LDAP_TYPE_OR_VALUE_EXISTS );
+                       }
+               }
+       }
+
+       /* no - add them */
+       if( attr_merge( e, mod->mod_type, mod->mod_bvalues ) != 0 ) {
+               return( LDAP_CONSTRAINT_VIOLATION );
+       }
+
+       return( LDAP_SUCCESS );
+}
+
+static int
+delete_values(
+    Entry      *e,
+    LDAPMod    *mod,
+    char       *dn
+)
+{
+       int             i, j, k, found;
+       Attribute       *a;
+
+       /* delete the entire attribute */
+       if ( mod->mod_bvalues == NULL ) {
+               Debug( LDAP_DEBUG_ARGS, "removing entire attribute %s\n",
+                   mod->mod_type, 0, 0 );
+               return( attr_delete( &e->e_attrs, mod->mod_type ) ?
+                   LDAP_NO_SUCH_ATTRIBUTE : LDAP_SUCCESS );
+       }
+
+       /* delete specific values - find the attribute first */
+       if ( (a = attr_find( e->e_attrs, mod->mod_type )) == NULL ) {
+               Debug( LDAP_DEBUG_ARGS, "could not find attribute %s\n",
+                   mod->mod_type, 0, 0 );
+               return( LDAP_NO_SUCH_ATTRIBUTE );
+       }
+
+       /* find each value to delete */
+       for ( i = 0; mod->mod_bvalues[i] != NULL; i++ ) {
+               found = 0;
+               for ( j = 0; a->a_vals[j] != NULL; j++ ) {
+                       if ( value_cmp( mod->mod_bvalues[i], a->a_vals[j],
+                           a->a_syntax, 3 ) != 0 ) {
+                               continue;
+                       }
+                       found = 1;
+
+                       /* found a matching value - delete it */
+                       ber_bvfree( a->a_vals[j] );
+                       for ( k = j + 1; a->a_vals[k] != NULL; k++ ) {
+                               a->a_vals[k - 1] = a->a_vals[k];
+                       }
+                       a->a_vals[k - 1] = NULL;
+                       break;
+               }
+
+               /* looked through them all w/o finding it */
+               if ( ! found ) {
+                       Debug( LDAP_DEBUG_ARGS,
+                           "could not find value for attr %s\n",
+                           mod->mod_type, 0, 0 );
+                       return( LDAP_NO_SUCH_ATTRIBUTE );
+               }
+       }
+
+       return( LDAP_SUCCESS );
+}
+
+static int
+replace_values(
+    Entry      *e,
+    LDAPMod    *mod,
+    char       *dn
+)
+{
+       (void) attr_delete( &e->e_attrs, mod->mod_type );
+
+       if ( attr_merge( e, mod->mod_type, mod->mod_bvalues ) != 0 ) {
+               return( LDAP_CONSTRAINT_VIOLATION );
+       }
+
+       return( LDAP_SUCCESS );
+}
diff --git a/servers/slapd/back-bdb2/modrdn.c b/servers/slapd/back-bdb2/modrdn.c
new file mode 100644 (file)
index 0000000..b79a429
--- /dev/null
@@ -0,0 +1,237 @@
+/* modrdn.c - bdb2 backend modrdn routine */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "back-bdb2.h"
+#include "proto-back-bdb2.h"
+
+static int
+bdb2i_back_modrdn_internal(
+    Backend    *be,
+    Connection *conn,
+    Operation  *op,
+    char       *dn,
+    char       *newrdn,
+    int                deleteoldrdn
+)
+{
+       struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+       char            *matched = NULL;
+       char            *p_dn = NULL, *p_ndn = NULL;
+       char            *new_dn = NULL, *new_ndn = NULL;
+       char            sep[2];
+       Entry           *e, *p = NULL;
+       int                     rootlock = 0;
+       int                     rc = -1;
+
+       /* get entry with writer lock */
+       if ( (e = bdb2i_dn2entry_w( be, dn, &matched )) == NULL ) {
+               send_ldap_result( conn, op, LDAP_NO_SUCH_OBJECT, matched, "" );
+               if ( matched != NULL ) {
+                       free( matched );
+               }
+               return( -1 );
+       }
+
+#ifdef SLAPD_CHILD_MODIFICATION_WITH_ENTRY_ACL
+               /* check parent for "children" acl */
+       if ( ! access_allowed( be, conn, op, e,
+               "entry", NULL, ACL_WRITE ) )
+       {
+               Debug( LDAP_DEBUG_TRACE, "no access to entry\n", 0,
+                       0, 0 );
+               send_ldap_result( conn, op, LDAP_INSUFFICIENT_ACCESS,
+                       "", "" );
+               goto return_results;
+       }
+#endif
+
+       if ( (p_ndn = dn_parent( be, e->e_ndn )) != NULL ) {
+               /* parent + rdn + separator(s) + null */
+               if( (p = bdb2i_dn2entry_w( be, p_ndn, &matched )) == NULL) {
+                       Debug( LDAP_DEBUG_TRACE, "parent does not exist\n",
+                               0, 0, 0);
+                       send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR,
+                               "", "");
+                       goto return_results;
+               }
+
+#ifndef SLAPD_CHILD_MODIFICATION_WITH_ENTRY_ACL
+               /* check parent for "children" acl */
+               if ( ! access_allowed( be, conn, op, p,
+                       "children", NULL, ACL_WRITE ) )
+               {
+                       Debug( LDAP_DEBUG_TRACE, "no access to parent\n", 0,
+                               0, 0 );
+                       send_ldap_result( conn, op, LDAP_INSUFFICIENT_ACCESS,
+                               "", "" );
+                       goto return_results;
+               }
+#endif
+
+               p_dn = dn_parent( be, e->e_dn );
+               new_dn = (char *) ch_malloc( strlen( p_dn ) + strlen( newrdn )
+                   + 3 );
+               if ( dn_type( e->e_dn ) == DN_X500 ) {
+                       strcpy( new_dn, newrdn );
+                       strcat( new_dn, ", " );
+                       strcat( new_dn, p_dn );
+               } else {
+                       char *s;
+                       strcpy( new_dn, newrdn );
+                       s = strchr( newrdn, '\0' );
+                       s--;
+                       if ( *s != '.' && *s != '@' ) {
+                               if ( (s = strpbrk( dn, ".@" )) != NULL ) {
+                                       sep[0] = *s;
+                                       sep[1] = '\0';
+                                       strcat( new_dn, sep );
+                               }
+                       }
+                       strcat( new_dn, p_dn );
+               }
+
+       } else {
+               /* no parent, modrdn entry directly under root */
+               if( ! be_isroot( be, op->o_ndn ) ) {
+                       Debug( LDAP_DEBUG_TRACE, "no parent & not root\n",
+                               0, 0, 0);
+                       send_ldap_result( conn, op, LDAP_INSUFFICIENT_ACCESS,
+                               "", "");
+                       goto return_results;
+               }
+
+               ldap_pvt_thread_mutex_lock(&li->li_root_mutex);
+               rootlock = 1;
+
+               new_dn = ch_strdup( newrdn );
+       }
+
+       new_ndn = dn_normalize_case( ch_strdup( new_dn ) );
+
+       if ( (bdb2i_dn2id ( be, new_ndn ) ) != NOID ) {
+               send_ldap_result( conn, op, LDAP_ALREADY_EXISTS, NULL, NULL );
+               goto return_results;
+       }
+
+       /* check for abandon */
+       ldap_pvt_thread_mutex_lock( &op->o_abandonmutex );
+       if ( op->o_abandon ) {
+               ldap_pvt_thread_mutex_unlock( &op->o_abandonmutex );
+               goto return_results;
+       }
+       ldap_pvt_thread_mutex_unlock( &op->o_abandonmutex );
+
+       /* add new one */
+       if ( bdb2i_dn2id_add( be, new_ndn, e->e_id ) != 0 ) {
+               send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR, NULL, NULL );
+               goto return_results;
+       }
+
+       /* delete old one */
+       if ( bdb2i_dn2id_delete( be, e->e_ndn ) != 0 ) {
+               send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR, NULL, NULL );
+               goto return_results;
+       }
+
+       (void) bdb2i_cache_delete_entry( &li->li_cache, e );
+       free( e->e_dn );
+       free( e->e_ndn );
+       e->e_dn = new_dn;
+       e->e_ndn = new_ndn;
+
+       /* XXX
+        * At some point here we need to update the attribute values in
+        * the entry itself that were effected by this RDN change
+        * (respecting the value of the deleteoldrdn parameter).
+        *
+        * Since the code to do this has not yet been written, treat this
+        * omission as a (documented) bug.
+        */
+
+       /* id2entry index */
+       if ( bdb2i_id2entry_add( be, e ) != 0 ) {
+               entry_free( e );
+               send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR, "", "" );
+               goto return_results;
+       }
+
+       send_ldap_result( conn, op, LDAP_SUCCESS, NULL, NULL );
+       rc = 0;
+
+return_results:
+       if( new_dn != NULL ) free( new_dn );
+       if( new_ndn != NULL ) free( new_ndn );
+       if( p_dn != NULL ) free( p_dn );
+       if( p_ndn != NULL ) free( p_ndn );
+
+       if( matched != NULL ) free( matched );
+
+       if( p != NULL ) {
+               /* free parent and writer lock */
+               bdb2i_cache_return_entry_w( &li->li_cache, p );
+
+       }
+
+       if ( rootlock ) {
+               /* release root writer lock */
+               ldap_pvt_thread_mutex_unlock(&li->li_root_mutex);
+       }
+
+       /* free entry and writer lock */
+       bdb2i_cache_return_entry_w( &li->li_cache, e );
+       return( rc );
+}
+
+
+int
+bdb2_back_modrdn(
+    Backend    *be,
+    Connection *conn,
+    Operation  *op,
+    char       *dn,
+    char       *newrdn,
+    int                deleteoldrdn
+)
+{
+       DB_LOCK  lock;
+       struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+
+       struct timeval  time1, time2;
+       char   *elapsed_time;
+       int    ret;
+
+       gettimeofday( &time1, NULL );
+
+       if ( bdb2i_enter_backend_w( &li->li_db_env, &lock ) != 0 ) {
+
+               send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR, "", "" );
+               return( -1 );
+
+       }
+
+       ret = bdb2i_back_modrdn_internal( be, conn, op, dn,
+                                       newrdn, deleteoldrdn );
+
+       (void) bdb2i_leave_backend( &li->li_db_env, lock );
+
+       if ( bdb2i_do_timing ) {
+
+               gettimeofday( &time2, NULL);
+               elapsed_time = bdb2i_elapsed( time1, time2 );
+               Debug( LDAP_DEBUG_ANY, "conn=%d op=%d MODRDN elapsed=%s\n",
+                               conn->c_connid, op->o_opid, elapsed_time );
+               free( elapsed_time );
+
+       }
+
+       return( ret );
+}
+
+
diff --git a/servers/slapd/back-bdb2/nextid.c b/servers/slapd/back-bdb2/nextid.c
new file mode 100644 (file)
index 0000000..c4d1eb4
--- /dev/null
@@ -0,0 +1,184 @@
+/* id.c - keep track of the next id to be given out */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#include "slap.h"
+#include "back-bdb2.h"
+
+static ID
+next_id_read( Backend *be )
+{
+       struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+       ID      id;
+       char    buf[20];
+       char*   file = li->li_nextid_file; 
+       FILE*   fp;
+
+       if ( (fp = fopen( file, "r" )) == NULL ) {
+               Debug( LDAP_DEBUG_ANY,
+                   "next_id_read: could not open \"%s\"\n",
+                   file, 0, 0 );
+               return NOID;
+       }
+
+       if ( fgets( buf, sizeof(buf), fp ) == NULL ) {
+               Debug( LDAP_DEBUG_ANY,
+                  "next_id_read: could not fgets nextid from \"%s\"\n",
+                   file, 0, 0 );
+               fclose( fp );
+               return NOID;
+       }
+
+       id = atol( buf );
+       fclose( fp );
+
+       if(id < 1) {
+               Debug( LDAP_DEBUG_ANY,
+                       "next_id_read %lu: atol(%s) return non-positive integer\n",
+                       id, buf, 0 );
+               return NOID;
+       }
+
+       return id;
+}
+
+static int
+next_id_write( Backend *be, ID id )
+{
+       struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+       char    buf[20];
+       char*   file = li->li_nextid_file; 
+       FILE*   fp;
+       int             rc;
+
+       if ( (fp = fopen( file, "w" )) == NULL ) {
+               Debug( LDAP_DEBUG_ANY, "next_id_write(%lu): could not open \"%s\"\n",
+                   id, file, 0 );
+               return -1;
+       } 
+
+       rc = 0;
+
+       if ( fprintf( fp, "%ld\n", id ) == EOF ) {
+               Debug( LDAP_DEBUG_ANY, "next_id_write(%lu): cannot fprintf\n",
+                   id, 0, 0 );
+               rc = -1;
+       }
+
+       if( fclose( fp ) != 0 ) {
+               Debug( LDAP_DEBUG_ANY, "next_id_write %lu: cannot fclose\n",
+                   id, 0, 0 );
+               rc = -1;
+       }
+
+       return rc;
+}
+
+int
+bdb2i_next_id_save( Backend *be )
+{
+       struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+       ID id = next_id_get( be );
+       int rc = next_id_write( be, id );
+
+       if (rc == 0) {
+               li->li_nextid_wrote = id;
+       }
+
+       return rc;
+}
+
+ID
+bdb2i_next_id( Backend *be )
+{
+       struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+       ID              id;
+
+       ldap_pvt_thread_mutex_lock( &li->li_nextid_mutex );
+
+       /* first time in here since startup - try to read the nexid */
+       if ( li->li_nextid == NOID ) {
+               li->li_nextid = next_id_read( be );
+
+               if ( li->li_nextid == NOID ) {
+                       li->li_nextid = 1;
+               }
+
+#if SLAPD_NEXTID_CHUNK > 1
+               li->li_nextid_wrote = li->li_nextid;
+#endif
+       }
+
+       id = li->li_nextid++;
+
+#if SLAPD_NEXTID_CHUNK > 1
+       if ( li->li_nextid > li->li_nextid_wrote ) {
+               li->li_nextid_wrote += SLAPD_NEXTID_CHUNK;
+               (void) next_id_write( be, li->li_nextid_wrote );
+       }
+#else
+       (void) next_id_write( be, li->li_nextid );
+#endif
+
+       ldap_pvt_thread_mutex_unlock( &li->li_nextid_mutex );
+       return( id );
+}
+
+void
+bdb2i_next_id_return( Backend *be, ID id )
+{
+#ifdef SLAPD_NEXTID_RETURN
+       struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+
+       ldap_pvt_thread_mutex_lock( &li->li_nextid_mutex );
+
+       if ( id != li->li_nextid - 1 ) {
+               ldap_pvt_thread_mutex_unlock( &li->li_nextid_mutex );
+               return;
+       }
+
+       li->li_nextid--;
+
+#if !( SLAPD_NEXTID_CHUCK > 1 )
+       (void) next_id_write( be, li->li_nextid );
+#endif
+
+       ldap_pvt_thread_mutex_unlock( &li->li_nextid_mutex );
+#endif
+}
+
+ID
+bdb2i_next_id_get( Backend *be )
+{
+       struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+       ID              id;
+
+       ldap_pvt_thread_mutex_lock( &li->li_nextid_mutex );
+
+       /* first time in here since startup - try to read the nexid */
+       if ( li->li_nextid == NOID ) {
+               li->li_nextid = next_id_read( be );
+
+               if ( li->li_nextid == NOID ) {
+                       li->li_nextid = 1;
+               }
+
+#if SLAPD_NEXTID_CHUNK > 1
+               li->li_nextid_wrote = li->li_nextid;
+#endif
+       }
+
+       id = li->li_nextid;
+
+       ldap_pvt_thread_mutex_unlock( &li->li_nextid_mutex );
+
+       return( id );
+}
diff --git a/servers/slapd/back-bdb2/porter.c b/servers/slapd/back-bdb2/porter.c
new file mode 100644 (file)
index 0000000..6047463
--- /dev/null
@@ -0,0 +1,127 @@
+/* porter.c - port functions of the bdb2 backend */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <errno.h>
+
+#include <ac/string.h>
+
+#include "slap.h"
+#include "back-bdb2.h"
+
+#define  PORTER_OBJ   "bdb2_backend"
+
+int  bdb2i_with_dbenv = 0;
+
+
+static int
+bdb2i_enter_backend( DB_ENV *dbEnv, DB_LOCK *lock, int writer )
+{
+       u_int32_t      locker;
+       db_lockmode_t  lock_type;
+       DBT            lock_dbt;
+       int            ret;
+
+       if ( !bdb2i_with_dbenv ) return( 0 );
+
+       if ( ( ret = lock_id( dbEnv->lk_info, &locker )) != 0 ) {
+
+               Debug( LDAP_DEBUG_ANY,
+                               "bdb2i_enter_backend(): unable to get locker id -- %s\n",
+                               strerror( ret ), 0, 0 );
+               return( ret );
+
+       }
+
+       lock_type     = writer ? DB_LOCK_WRITE : DB_LOCK_READ;
+       lock_dbt.data = PORTER_OBJ;
+       lock_dbt.size = strlen( PORTER_OBJ );
+
+       switch ( ( ret = lock_get( dbEnv->lk_info, locker, 0, &lock_dbt,
+                                       lock_type, lock ))) {
+
+               case 0:
+                       Debug( LDAP_DEBUG_ANY, "bdb2i_enter_backend() -- %s lock granted\n",
+                                               writer ? "write" : "read", 0, 0 );
+                       break;
+
+               case DB_LOCK_NOTGRANTED:
+                       Debug( LDAP_DEBUG_ANY,
+                                               "bdb2i_enter_backend() -- %s lock NOT granted\n",
+                                               writer ? "write" : "read", 0, 0 );
+                       break;
+
+               case DB_LOCK_DEADLOCK:
+                       Debug( LDAP_DEBUG_ANY,
+                                               "bdb2i_enter_backend() -- %s lock returned DEADLOCK\n",
+                                               writer ? "write" : "read", 0, 0 );
+                       break;
+
+               default:
+                       Debug( LDAP_DEBUG_ANY,
+                                               "bdb2i_enter_backend() -- %s lock returned ERROR: %s\n",
+                                               writer ? "write" : "read", strerror( errno ), 0 );
+                       ret = errno;
+                       break;
+                       
+       }
+
+       return( ret );
+}
+
+
+int
+bdb2i_enter_backend_r( DB_ENV *dbEnv, DB_LOCK *lock )
+{
+       return( bdb2i_enter_backend( dbEnv, lock, 0 ));
+}
+
+
+int
+bdb2i_enter_backend_w( DB_ENV *dbEnv, DB_LOCK *lock )
+{
+       return( bdb2i_enter_backend( dbEnv, lock, 1 ));
+}
+
+
+int
+bdb2i_leave_backend( DB_ENV *dbEnv, DB_LOCK lock )
+{
+       int   ret;
+
+       if ( !bdb2i_with_dbenv ) return( 0 );
+
+       switch( ( ret = lock_put( dbEnv->lk_info, lock ))) {
+
+               case 0:
+                       Debug( LDAP_DEBUG_ANY, "bdb2i_leave_backend() -- lock released\n",
+                                               0, 0, 0 );
+                       break;
+
+               case DB_LOCK_NOTHELD:
+                       Debug( LDAP_DEBUG_ANY,
+                                               "bdb2i_leave_backend() -- lock NOT held\n",
+                                               0, 0, 0 );
+                       break;
+
+               case DB_LOCK_DEADLOCK:
+                       Debug( LDAP_DEBUG_ANY,
+                                               "bdb2i_leave_backend() -- lock returned DEADLOCK\n",
+                                               0, 0, 0 );
+                       break;
+
+               default:
+                       Debug( LDAP_DEBUG_ANY,
+                                               "bdb2i_leave_backend() -- lock returned ERROR: %s\n",
+                                               strerror( errno ), 0, 0 );
+                       ret = errno;
+                       break;
+                       
+       }
+
+       return( ret );
+
+}
+
+
diff --git a/servers/slapd/back-bdb2/proto-back-bdb2.h b/servers/slapd/back-bdb2/proto-back-bdb2.h
new file mode 100644 (file)
index 0000000..f85fabc
--- /dev/null
@@ -0,0 +1,177 @@
+#ifndef _PROTO_BACK_BDB2
+#define _PROTO_BACK_BDB2
+
+#include <ldap_cdefs.h>
+
+#include "external.h"
+
+LDAP_BEGIN_DECL
+
+/*
+ * alias.c
+ */
+Entry *bdb2i_derefAlias_r LDAP_P((
+       Backend     *be,
+       Connection      *conn,
+       Operation       *op,
+       Entry       *e ));
+char *bdb2i_derefDN LDAP_P((
+       Backend     *be,
+       Connection  *conn,
+       Operation   *op,
+       char        *dn ));
+
+/*
+ * attr.c
+ */
+
+void bdb2i_attr_masks LDAP_P(( struct ldbminfo *li, char *type, int *indexmask,
+ int *syntaxmask ));
+void bdb2i_attr_index_config LDAP_P(( struct ldbminfo *li, char *fname,
+ int lineno, int argc, char **argv, int init ));
+
+/*
+ * cache.c
+ */
+
+void bdb2i_cache_set_state LDAP_P(( struct cache *cache, Entry *e, int state ));
+void bdb2i_cache_return_entry_r LDAP_P(( struct cache *cache, Entry *e ));
+void bdb2i_cache_return_entry_w LDAP_P(( struct cache *cache, Entry *e ));
+int bdb2i_cache_add_entry_lock LDAP_P(( struct cache *cache, Entry *e,
+ int state ));
+ID bdb2i_cache_find_entry_dn2id LDAP_P(( Backend *be, struct cache *cache,
+ char *dn ));
+Entry * bdb2i_cache_find_entry_id LDAP_P(( struct cache *cache, ID id, int rw ));
+int bdb2i_cache_delete_entry LDAP_P(( struct cache *cache, Entry *e ));
+
+/*
+ * dbcache.c
+ */
+
+struct dbcache * bdb2i_cache_open LDAP_P(( Backend *be, char *name, char *suffix,
+ int flags ));
+void bdb2i_cache_close LDAP_P(( Backend *be, struct dbcache *db ));
+void bdb2i_cache_really_close LDAP_P(( Backend *be, struct dbcache *db ));
+void bdb2i_cache_flush_all LDAP_P(( Backend *be ));
+Datum bdb2i_cache_fetch LDAP_P(( struct dbcache *db, Datum key ));
+int bdb2i_cache_store LDAP_P(( struct dbcache *db, Datum key, Datum data, int flags ));
+int bdb2i_cache_delete LDAP_P(( struct dbcache *db, Datum key ));
+
+/*
+ * dn2id.c
+ */
+
+int bdb2i_dn2id_add LDAP_P(( Backend *be, char *dn, ID id ));
+ID bdb2i_dn2id LDAP_P(( Backend *be, char *dn ));
+int bdb2i_dn2id_delete LDAP_P(( Backend *be, char *dn ));
+Entry * bdb2i_dn2entry_r LDAP_P(( Backend *be, char *dn, char **matched ));
+Entry * bdb2i_dn2entry_w LDAP_P(( Backend *be, char *dn, char **matched ));
+
+/*
+ * filterindex.c
+ */
+
+ID_BLOCK * bdb2i_filter_candidates LDAP_P(( Backend *be, Filter *f ));
+
+/*
+ * id2children.c
+ */
+
+int bdb2i_id2children_add LDAP_P(( Backend *be, Entry *p, Entry *e ));
+int bdb2i_id2children_remove LDAP_P(( Backend *be, Entry *p, Entry *e ));
+int bdb2i_has_children LDAP_P(( Backend *be, Entry *p ));
+
+/*
+ * id2entry.c
+ */
+
+int bdb2i_id2entry_add LDAP_P(( Backend *be, Entry *e ));
+int bdb2i_id2entry_delete LDAP_P(( Backend *be, Entry *e ));
+Entry * bdb2i_id2entry LDAP_P(( Backend *be, ID id, int rw )); 
+Entry * bdb2i_id2entry_r LDAP_P(( Backend *be, ID id ));
+Entry * bdb2i_id2entry_w LDAP_P(( Backend *be, ID id ));
+
+/*
+ * idl.c
+ */
+
+ID_BLOCK * bdb2i_idl_alloc LDAP_P(( int nids ));
+ID_BLOCK * bdb2i_idl_allids LDAP_P(( Backend *be ));
+void bdb2i_idl_free LDAP_P(( ID_BLOCK *idl ));
+ID_BLOCK * bdb2i_idl_fetch LDAP_P(( Backend *be, struct dbcache *db, Datum key ));
+int bdb2i_idl_insert_key LDAP_P(( Backend *be, struct dbcache *db, Datum key, ID id ));
+int bdb2i_idl_insert LDAP_P(( ID_BLOCK **idl, ID id, int maxids ));
+int bdb2i_idl_delete_key LDAP_P(( Backend *be, struct dbcache *db, Datum key, ID id ));
+ID_BLOCK * bdb2i_idl_intersection LDAP_P(( Backend *be, ID_BLOCK *a, ID_BLOCK *b ));
+ID_BLOCK * bdb2i_idl_union LDAP_P(( Backend *be, ID_BLOCK *a, ID_BLOCK *b ));
+ID_BLOCK * bdb2i_idl_notin LDAP_P(( Backend *be, ID_BLOCK *a, ID_BLOCK *b ));
+ID bdb2i_idl_firstid LDAP_P(( ID_BLOCK *idl ));
+ID bdb2i_idl_nextid LDAP_P(( ID_BLOCK *idl, ID id ));
+
+/*
+ * index.c
+ */
+
+int bdb2i_index_add_entry LDAP_P(( Backend *be, Entry *e ));
+int bdb2i_index_add_mods LDAP_P(( Backend *be, LDAPModList *ml, ID id ));
+ID_BLOCK * bdb2i_index_read LDAP_P(( Backend *be, char *type, int indextype, char *val ));
+int bdb2i_index_add_values LDAP_P(( Backend *be, char *type, struct berval **vals, ID  id ));
+
+/*
+ * kerberos.c
+ */
+
+#ifdef HAVE_KERBEROS
+/* bdb2i_krbv4_ldap_auth LDAP_P(( Backend *be, struct berval *cred, AUTH_DAT *ad )); */
+#endif
+
+/*
+ * nextid.c
+ */
+
+ID bdb2i_next_id LDAP_P(( Backend *be ));
+void bdb2i_next_id_return LDAP_P(( Backend *be, ID id ));
+ID bdb2i_next_id_get LDAP_P(( Backend *be ));
+int bdb2i_next_id_save LDAP_P(( Backend *be ));
+
+/*
+ *  timing.c
+ */
+
+char *bdb2i_elapsed LDAP_P(( struct timeval firsttime,
+ struct timeval secondtime ));
+
+/*
+ * porter.c
+ */
+
+int bdb2i_enter_backend_r  LDAP_P(( DB_ENV *dbEnv, DB_LOCK *lock ));
+int bdb2i_enter_backend_w  LDAP_P(( DB_ENV *dbEnv, DB_LOCK *lock ));
+int bdb2i_leave_backend    LDAP_P(( DB_ENV *dbEnv, DB_LOCK lock ));
+
+/*
+ *  txn.c
+ */
+
+void bdb2i_txn_head_init  LDAP_P(( BDB2_TXN_HEAD *head ));
+void bdb2i_txn_attr_config LDAP_P((
+ struct ldbminfo  *li,
+ char *attr,
+ int open ));
+void bdb2i_txn_open_files LDAP_P(( struct ldbminfo *li ));
+void bdb2i_txn_close_files LDAP_P(( BDB2_TXN_HEAD *head ));
+BDB2_TXN_FILES *bdb2i_get_db_file_cache LDAP_P((
+ struct ldbminfo *li,
+ char *name ));
+void bdb2i_check_additional_attr_index LDAP_P(( struct ldbminfo *li ));
+void bdb2i_check_default_attr_index_add LDAP_P((
+ struct ldbminfo *li,
+ Entry *e ));
+void bdb2i_check_default_attr_index_mod LDAP_P((
+ struct ldbminfo *li,
+ LDAPModList *modlist ));
+
+
+
+LDAP_END_DECL
+#endif
diff --git a/servers/slapd/back-bdb2/search.c b/servers/slapd/back-bdb2/search.c
new file mode 100644 (file)
index 0000000..75afff6
--- /dev/null
@@ -0,0 +1,559 @@
+/* search.c - bdb2 backend search function */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "back-bdb2.h"
+#include "proto-back-bdb2.h"
+
+static ID_BLOCK        *base_candidates(Backend *be, Connection *conn, Operation *op, char *base, Filter *filter, char **attrs, int attrsonly, char **matched, int *err);
+static ID_BLOCK        *onelevel_candidates(Backend *be, Connection *conn, Operation *op, char *base, Filter *filter, char **attrs, int attrsonly, char **matched, int *err);
+static ID_BLOCK        *subtree_candidates(Backend *be, Connection *conn, Operation *op, char *base, Filter *filter, char **attrs, int attrsonly, char **matched, Entry *e, int *err, int lookupbase);
+
+#define GRABSIZE       BUFSIZ
+
+#define MAKE_SPACE( n ) { \
+       if ( rcur + n > rbuf + rmaxsize ) { \
+               int     offset = rcur - rbuf; \
+               rbuf =  ch_realloc( rbuf, rmaxsize + GRABSIZE ); \
+               rmaxsize += GRABSIZE; \
+               rcur = rbuf + offset; \
+       } \
+}
+
+static int
+bdb2i_back_search_internal(
+    Backend    *be,
+    Connection *conn,
+    Operation  *op,
+    char       *base,
+    int                scope,
+    int                deref,
+    int                slimit,
+    int                tlimit,
+    Filter     *filter,
+    char       *filterstr,
+    char       **attrs,
+    int                attrsonly
+)
+{
+       struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+       int             err;
+       time_t          stoptime;
+       ID_BLOCK                *candidates;
+       ID              id;
+       Entry           *e;
+       Attribute       *ref;
+       char            *matched = NULL;
+       int             rmaxsize, nrefs;
+       char            *rbuf, *rcur, *r;
+       int             nentries = 0;
+       char            *realBase;
+
+       Debug(LDAP_DEBUG_ARGS, "=> bdb2i_back_search\n", 0, 0, 0);
+
+       if ( tlimit == 0 && be_isroot( be, op->o_ndn ) ) {
+               tlimit = -1;    /* allow root to set no limit */
+       } else {
+               tlimit = (tlimit > be->be_timelimit || tlimit < 1) ?
+                   be->be_timelimit : tlimit;
+               stoptime = op->o_time + tlimit;
+       }
+       if ( slimit == 0 && be_isroot( be, op->o_ndn ) ) {
+               slimit = -1;    /* allow root to set no limit */
+       } else {
+               slimit = (slimit > be->be_sizelimit || slimit < 1) ?
+                   be->be_sizelimit : slimit;
+       }
+
+       /*
+        * check and apply aliasing where the dereferencing applies to
+        * the subordinates of the base
+        */
+
+       switch ( deref ) {
+       case LDAP_DEREF_FINDING:
+       case LDAP_DEREF_ALWAYS:
+               realBase = bdb2i_derefDN ( be, conn, op, base );
+               break;
+       default:
+               realBase = ch_strdup(base);
+       }
+
+       (void) dn_normalize_case( realBase );
+
+       Debug( LDAP_DEBUG_TRACE, "using base \"%s\"\n",
+               realBase, 0, 0 );
+
+       switch ( scope ) {
+       case LDAP_SCOPE_BASE:
+               candidates = base_candidates( be, conn, op, realBase, filter,
+                   attrs, attrsonly, &matched, &err );
+               break;
+
+       case LDAP_SCOPE_ONELEVEL:
+               candidates = onelevel_candidates( be, conn, op, realBase, filter,
+                   attrs, attrsonly, &matched, &err );
+               break;
+
+       case LDAP_SCOPE_SUBTREE:
+               candidates = subtree_candidates( be, conn, op, realBase, filter,
+                   attrs, attrsonly, &matched, NULL, &err, 1 );
+               break;
+
+       default:
+               send_ldap_result( conn, op, LDAP_PROTOCOL_ERROR, "",
+                   "Bad scope" );
+               if( realBase != NULL) {
+                       free( realBase );
+               }
+               return( -1 );
+       }
+
+       /* null candidates means we could not find the base object */
+       if ( candidates == NULL ) {
+               send_ldap_result( conn, op, err, matched, "" );
+               if ( matched != NULL ) {
+                       free( matched );
+               }
+               if( realBase != NULL) {
+                       free( realBase );
+               }
+               return( -1 );
+       }
+
+       if ( matched != NULL ) {
+               free( matched );
+       }
+
+       rmaxsize = 0;
+       nrefs = 0;
+       rbuf = rcur = NULL;
+       MAKE_SPACE( sizeof("Referral:") + 1 );
+       strcpy( rbuf, "Referral:" );
+       rcur = strchr( rbuf, '\0' );
+       for ( id = bdb2i_idl_firstid( candidates ); id != NOID;
+           id = bdb2i_idl_nextid( candidates, id ) ) {
+               /* check for abandon */
+               ldap_pvt_thread_mutex_lock( &op->o_abandonmutex );
+               if ( op->o_abandon ) {
+                       ldap_pvt_thread_mutex_unlock( &op->o_abandonmutex );
+                       bdb2i_idl_free( candidates );
+                       free( rbuf );
+                       if( realBase != NULL) {
+                               free( realBase );
+                       }
+                       return( 0 );
+               }
+               ldap_pvt_thread_mutex_unlock( &op->o_abandonmutex );
+
+               /* check time limit */
+               ldap_pvt_thread_mutex_lock( &currenttime_mutex );
+               time( &currenttime );
+               if ( tlimit != -1 && currenttime > stoptime ) {
+                       ldap_pvt_thread_mutex_unlock( &currenttime_mutex );
+                       send_ldap_search_result( conn, op,
+                           LDAP_TIMELIMIT_EXCEEDED, NULL, nrefs > 0 ? rbuf :
+                           NULL, nentries );
+                       bdb2i_idl_free( candidates );
+                       free( rbuf );
+                       if( realBase != NULL) {
+                               free( realBase );
+                       }
+                       return( 0 );
+               }
+               ldap_pvt_thread_mutex_unlock( &currenttime_mutex );
+
+               /* get the entry with reader lock */
+               if ( (e = bdb2i_id2entry_r( be, id )) == NULL ) {
+                       Debug( LDAP_DEBUG_ARGS, "candidate %lu not found\n",
+                              id, 0, 0 );
+                       continue;
+               }
+
+               /*
+                * if it's a referral, add it to the list of referrals. only do
+                * this for subtree searches, and don't check the filter explicitly
+                * here since it's only a candidate anyway.
+                */
+               if ( scope == LDAP_SCOPE_SUBTREE &&
+                       e->e_ndn != NULL &&
+                       strncmp( e->e_ndn, "REF=", 4 ) == 0 &&
+                       (ref = attr_find( e->e_attrs, "ref" )) != NULL )
+               {
+                       int     i, len;
+
+                       if ( ref->a_vals == NULL ) {
+                               Debug( LDAP_DEBUG_ANY, "null ref in (%s)\n", 
+                                       e->e_dn, 0, 0 );
+                       } else {
+                               for ( i = 0; ref->a_vals[i] != NULL; i++ ) {
+                                       /* referral + newline + null */
+                                       MAKE_SPACE( ref->a_vals[i]->bv_len + 2 );
+                                       *rcur++ = '\n';
+                                       strncpy( rcur, ref->a_vals[i]->bv_val,
+                                               ref->a_vals[i]->bv_len );
+                                       rcur = rcur + ref->a_vals[i]->bv_len;
+                                       *rcur = '\0';
+                                       nrefs++;
+                               }
+                       }
+
+               /* otherwise it's an entry - see if it matches the filter */
+               } else {
+                       /* if it matches the filter and scope, send it */
+                       if ( test_filter( be, conn, op, e, filter ) == 0 ) {
+                               int             scopeok;
+                               char    *dn;
+
+                               /* check scope */
+                               scopeok = 1;
+                               if ( scope == LDAP_SCOPE_ONELEVEL ) {
+                                       if ( (dn = dn_parent( be, e->e_dn )) != NULL ) {
+                                               (void) dn_normalize_case( dn );
+                                               scopeok = (dn == realBase)
+                                                       ? 1
+                                                       : (strcmp( dn, realBase ) ? 0 : 1 );
+                                               free( dn );
+                                       } else {
+                                               scopeok = (realBase == NULL || *realBase == '\0');
+                                       }
+                               } else if ( scope == LDAP_SCOPE_SUBTREE ) {
+                                       dn = ch_strdup( e->e_ndn );
+                                       scopeok = dn_issuffix( dn, realBase );
+                                       free( dn );
+                               }
+
+                               if ( scopeok ) {
+                                       /* check size limit */
+                                       if ( --slimit == -1 ) {
+                                               bdb2i_cache_return_entry_r( &li->li_cache, e );
+                                               send_ldap_search_result( conn, op,
+                                                       LDAP_SIZELIMIT_EXCEEDED, NULL,
+                                                       nrefs > 0 ? rbuf : NULL, nentries );
+                                               bdb2i_idl_free( candidates );
+                                               free( rbuf );
+
+                                               if( realBase != NULL) {
+                                                       free( realBase );
+                                               }
+                                               return( 0 );
+                                       }
+
+                                       /*
+                                        * check and apply aliasing where the dereferencing applies to
+                                        * the subordinates of the base
+                                        */
+                                       switch ( deref ) {
+                                       case LDAP_DEREF_SEARCHING:
+                                       case LDAP_DEREF_ALWAYS:
+                                               {
+                                                       Entry *newe = bdb2i_derefAlias_r( be, conn, op, e );
+                                                       if ( newe == NULL ) { /* problem with the alias */
+                                                               bdb2i_cache_return_entry_r( &li->li_cache, e );
+                                                               e = NULL;
+                                                       }
+                                                       else if ( newe != e ) { /* reassign e */
+                                                               bdb2i_cache_return_entry_r( &li->li_cache, e );
+                                                               e = newe;
+                                                       }
+                                               }
+                                               break;
+                                       }
+
+                                       if (e) {
+                                               switch ( send_search_entry( be, conn, op, e,
+                                                       attrs, attrsonly ) ) {
+                                               case 0:         /* entry sent ok */
+                                                       nentries++;
+                                                       break;
+                                               case 1:         /* entry not sent */
+                                                       break;
+                                               case -1:        /* connection closed */
+                                                       bdb2i_cache_return_entry_r( &li->li_cache, e );
+                                                       bdb2i_idl_free( candidates );
+                                                       free( rbuf );
+
+                                                       if( realBase != NULL) {
+                                                               free( realBase );
+                                                       }
+                                                       return( 0 );
+                                               }
+                                       }
+                               }
+                       }
+               }
+
+               if( e != NULL ) {
+                       /* free reader lock */
+                       bdb2i_cache_return_entry_r( &li->li_cache, e );
+               }
+
+               ldap_pvt_thread_yield();
+       }
+       bdb2i_idl_free( candidates );
+       if ( nrefs > 0 ) {
+               send_ldap_search_result( conn, op, LDAP_PARTIAL_RESULTS, NULL,
+                   rbuf, nentries );
+       } else {
+               send_ldap_search_result( conn, op, LDAP_SUCCESS, NULL, NULL,
+                   nentries );
+       }
+       free( rbuf );
+
+       if( realBase != NULL) {
+               free( realBase );
+       }
+
+       return( 0 );
+}
+
+
+int
+bdb2_back_search(
+    Backend    *be,
+    Connection *conn,
+    Operation  *op,
+    char       *base,
+    int                scope,
+    int                deref,
+    int                slimit,
+    int                tlimit,
+    Filter     *filter,
+    char       *filterstr,
+    char       **attrs,
+    int                attrsonly
+)
+{
+       DB_LOCK  lock;
+       struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+
+       struct timeval  time1, time2;
+       char   *elapsed_time;
+       int    ret;
+
+       gettimeofday( &time1, NULL );
+
+       if ( bdb2i_enter_backend_r( &li->li_db_env, &lock ) != 0 ) {
+
+               send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR, "", "" );
+               return( -1 );
+
+       }
+
+       ret = bdb2i_back_search_internal( be, conn, op, base, scope, deref,
+                                       slimit, tlimit, filter, filterstr, attrs, attrsonly );
+
+       (void) bdb2i_leave_backend( &li->li_db_env, lock );
+
+       if ( bdb2i_do_timing ) {
+
+               gettimeofday( &time2, NULL);
+               elapsed_time = bdb2i_elapsed( time1, time2 );
+               Debug( LDAP_DEBUG_ANY, "conn=%d op=%d SRCH elapsed=%s\n",
+                               conn->c_connid, op->o_opid, elapsed_time );
+               free( elapsed_time );
+
+       }
+
+       return( ret );
+}
+
+
+static ID_BLOCK *
+base_candidates(
+    Backend    *be,
+    Connection *conn,
+    Operation  *op,
+    char       *base,
+    Filter     *filter,
+    char       **attrs,
+    int                attrsonly,
+    char       **matched,
+    int                *err
+)
+{
+       struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+       int             rc;
+       ID              id;
+       ID_BLOCK                *idl;
+       Entry           *e;
+
+       Debug(LDAP_DEBUG_TRACE, "base_candidates: base: \"%s\"\n", base, 0, 0);
+
+       *err = LDAP_SUCCESS;
+
+       /* get entry with reader lock */
+       if ( (e = bdb2i_dn2entry_r( be, base, matched )) == NULL ) {
+               *err = LDAP_NO_SUCH_OBJECT;
+               return( NULL );
+       }
+
+       /* check for deleted */
+
+       idl = bdb2i_idl_alloc( 1 );
+       bdb2i_idl_insert( &idl, e->e_id, 1 );
+
+
+       /* free reader lock */
+       bdb2i_cache_return_entry_r( &li->li_cache, e );
+
+       return( idl );
+}
+
+static ID_BLOCK *
+onelevel_candidates(
+    Backend    *be,
+    Connection *conn,
+    Operation  *op,
+    char       *base,
+    Filter     *filter,
+    char       **attrs,
+    int                attrsonly,
+    char       **matched,
+    int                *err
+)
+{
+       struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+       Entry           *e = NULL;
+       Filter          *f;
+       char            buf[20];
+       ID_BLOCK                *candidates;
+
+       Debug(LDAP_DEBUG_TRACE, "onelevel_candidates: base: \"%s\"\n", base, 0, 0);
+
+       *err = LDAP_SUCCESS;
+
+       /* get the base object with reader lock */
+       if ( base != NULL && *base != '\0' &&
+               (e = bdb2i_dn2entry_r( be, base, matched )) == NULL )
+       {
+               *err = LDAP_NO_SUCH_OBJECT;
+               return( NULL );
+       }
+
+       /*
+        * modify the filter to be something like this:
+        *
+        *      parent=baseobject & originalfilter
+        */
+
+       f = (Filter *) ch_malloc( sizeof(Filter) );
+       f->f_next = NULL;
+       f->f_choice = LDAP_FILTER_AND;
+       f->f_and = (Filter *) ch_malloc( sizeof(Filter) );
+       f->f_and->f_choice = LDAP_FILTER_EQUALITY;
+       f->f_and->f_ava.ava_type = ch_strdup( "id2children" );
+       sprintf( buf, "%ld", e != NULL ? e->e_id : 0 );
+       f->f_and->f_ava.ava_value.bv_val = ch_strdup( buf );
+       f->f_and->f_ava.ava_value.bv_len = strlen( buf );
+       f->f_and->f_next = filter;
+
+       /* from here, it's just like subtree_candidates */
+       candidates = subtree_candidates( be, conn, op, base, f, attrs,
+           attrsonly, matched, e, err, 0 );
+
+       /* free up just the filter stuff we allocated above */
+       f->f_and->f_next = NULL;
+       filter_free( f );
+
+       /* free entry and reader lock */
+       if( e != NULL ) {
+               bdb2i_cache_return_entry_r( &li->li_cache, e );
+       }
+       return( candidates );
+}
+
+static ID_BLOCK *
+subtree_candidates(
+    Backend    *be,
+    Connection *conn,
+    Operation  *op,
+    char       *base,
+    Filter     *filter,
+    char       **attrs,
+    int                attrsonly,
+    char       **matched,
+    Entry      *e,
+    int                *err,
+    int                lookupbase
+)
+{
+       struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+       Filter          *f, **filterarg_ptr;
+       ID_BLOCK                *candidates;
+
+       Debug(LDAP_DEBUG_TRACE, "subtree_candidates: base: \"%s\" %s\n",
+               base ? base : "NULL", lookupbase ? "lookupbase" : "", 0);
+
+       /*
+        * get the base object - unless we already have it (from one-level).
+        * also, unless this is a one-level search or a subtree search
+        * starting at the very top of our subtree, we need to modify the
+        * filter to be something like this:
+        *
+        *      dn=*baseobjectdn & (originalfilter | ref=*)
+        *
+        * the "objectclass=referral" part is used to select referrals to return
+        */
+
+       *err = LDAP_SUCCESS;
+       f = NULL;
+       if ( lookupbase ) {
+               e = NULL;
+
+               if ( base != NULL && *base != '\0' &&
+                       (e = bdb2i_dn2entry_r( be, base, matched )) == NULL )
+               {
+                       *err = LDAP_NO_SUCH_OBJECT;
+                       return( NULL );
+               }
+
+               if (e) {
+                       bdb2i_cache_return_entry_r( &li->li_cache, e );
+               }
+
+               f = (Filter *) ch_malloc( sizeof(Filter) );
+               f->f_next = NULL;
+               f->f_choice = LDAP_FILTER_OR;
+               f->f_or = (Filter *) ch_malloc( sizeof(Filter) );
+               f->f_or->f_choice = LDAP_FILTER_EQUALITY;
+               f->f_or->f_avtype = ch_strdup( "objectclass" );
+               /* Patch to use normalized uppercase */
+               f->f_or->f_avvalue.bv_val = ch_strdup( "REFERRAL" );
+               f->f_or->f_avvalue.bv_len = strlen( "REFERRAL" );
+               filterarg_ptr = &f->f_or->f_next;
+               *filterarg_ptr = filter;
+               filter = f;
+
+               if ( ! be_issuffix( be, base ) ) {
+                       f = (Filter *) ch_malloc( sizeof(Filter) );
+                       f->f_next = NULL;
+                       f->f_choice = LDAP_FILTER_AND;
+                       f->f_and = (Filter *) ch_malloc( sizeof(Filter) );
+                       f->f_and->f_choice = LDAP_FILTER_SUBSTRINGS;
+                       f->f_and->f_sub_type = ch_strdup( "dn" );
+                       f->f_and->f_sub_initial = NULL;
+                       f->f_and->f_sub_any = NULL;
+                       f->f_and->f_sub_final = ch_strdup( base );
+                       value_normalize( f->f_and->f_sub_final, SYNTAX_CIS );
+                       f->f_and->f_next = filter;
+                       filter = f;
+               }
+       }
+
+       candidates = bdb2i_filter_candidates( be, filter );
+
+       /* free up just the parts we allocated above */
+       if ( f != NULL ) {
+               *filterarg_ptr = NULL;
+               filter_free( f );
+       }
+
+       return( candidates );
+}
diff --git a/servers/slapd/back-bdb2/startup.c b/servers/slapd/back-bdb2/startup.c
new file mode 100644 (file)
index 0000000..c27f6ad
--- /dev/null
@@ -0,0 +1,217 @@
+/* startup.c - startup bdb2 backend */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "ldapconfig.h"
+#include "slap.h"
+#include "back-bdb2.h"
+
+#include "db.h"
+
+static void remove_old_locks( char *home );
+
+
+static void
+bdb2i_db_errcall( char *prefix, char *message )
+{
+       Debug( LDAP_DEBUG_ANY, "dbd2_db_errcall(): %s %s", prefix, message, 0 );
+}
+
+
+void
+bdb2i_back_startup_internal(
+    Backend    *be
+)
+{
+       struct ldbminfo  *li = (struct ldbminfo *) be->be_private;
+       DB_ENV           *dbEnv = &li->li_db_env;
+       int    envFlags = DB_CREATE | DB_THREAD | DB_INIT_LOCK | DB_INIT_MPOOL;
+       int    err      = 0;
+       char   *home;
+       char   datadir[MAXPATHLEN];
+       char   *config[2] = { datadir, NULL };
+
+       /*  if the data directory is not an absolute path, have it relative
+        to the current working directory (which should not be configured !)  */
+       if ( *li->li_directory != *DEFAULT_DIRSEP ) {
+               char   cwd[MAXPATHLEN];
+
+               (void) getcwd( cwd, MAXPATHLEN );
+               sprintf( cwd, "%s%s%s", cwd, DEFAULT_DIRSEP, li->li_directory );
+               free( li->li_directory );
+               li->li_directory = strdup( cwd );
+
+       }
+
+       /*  set the DB home directory to the configured one, or the data dir  */
+       if ( li->li_dbhome ) {
+
+               if ( *li->li_dbhome != *DEFAULT_DIRSEP ) {
+                       char   cwd[MAXPATHLEN];
+
+                       (void) getcwd( cwd, MAXPATHLEN );
+                       sprintf( cwd, "%s%s%s", cwd, DEFAULT_DIRSEP, li->li_dbhome );
+                       free( li->li_dbhome );
+                       li->li_dbhome = strdup( cwd );
+
+               }
+               home = li->li_dbhome;
+
+       } else {
+
+               home = li->li_directory;
+
+       }
+
+       /*  set the DATA_DIR  */
+       sprintf( datadir, "DB_DATA_DIR %s", li->li_directory );
+
+       /*  general initialization of the environment  */
+       memset( dbEnv, 0, sizeof( DB_ENV ));
+       dbEnv->db_errcall = bdb2i_db_errcall;
+       dbEnv->db_errpfx  = "==>";
+
+       /*  initialize the lock subsystem  */
+       dbEnv->lk_max     = 0;
+
+       /*  remove old locking tables  */
+       remove_old_locks( home );
+
+       /*  initialize the mpool subsystem  */
+       dbEnv->mp_size   = (size_t) li->li_dbcachesize;
+
+       /*  now do the db_appinit  */
+       if ( ( err = db_appinit( home, config, dbEnv, envFlags )) ) {
+               char  error[BUFSIZ];
+
+               if ( err < 0 ) sprintf( error, "%ld\n", (long) err );
+               else           sprintf( error, "%s\n", strerror( err ));
+
+               fprintf( stderr,
+                               "bdb2i_back_startup(): FATAL error in db_appinit() : %s\n",
+                               error );
+               exit( 1 );
+
+       }
+
+       bdb2i_with_dbenv = 1;
+
+       /*  if there are more index files, add them to the DB file list  */
+       bdb2i_check_additional_attr_index( li );
+
+       /*  now open all DB files  */
+       bdb2i_txn_open_files( li );
+
+}
+
+
+static void
+bdb2i_back_shutdown_internal(
+    Backend    *be
+)
+{
+       struct ldbminfo  *li = (struct ldbminfo *) be->be_private;
+       DB_ENV           *dbEnv = &li->li_db_env;
+       int              err;
+
+       /*  close all DB files  */
+       bdb2i_txn_close_files( &li->li_txn_head );
+
+       /*  remove old locking tables  */
+       dbEnv->db_errpfx  = "bdb2i_back_shutdown(): lock_unlink:";
+       if ( ( err = lock_unlink( NULL, 1, dbEnv )) != 0 )
+               Debug( LDAP_DEBUG_ANY, "bdb2i_back_shutdown(): lock_unlink: %s\n",
+                                       strerror( err ), 0, 0);
+
+       /*  remove old memory pool  */
+       dbEnv->db_errpfx  = "bdb2i_back_shutdown(): memp_unlink:";
+       if ( ( err = memp_unlink( NULL, 1, dbEnv )) != 0 )
+               Debug( LDAP_DEBUG_ANY, "bdb2i_back_shutdown(): memp_unlink: %s\n",
+                                       strerror( err ), 0, 0);
+
+       (void) db_appexit( &li->li_db_env );
+
+}
+
+
+void
+bdb2_back_startup(
+    Backend    *be
+)
+{
+       struct timeval  time1, time2;
+       char   *elapsed_time;
+
+       gettimeofday( &time1, NULL );
+
+       bdb2i_back_startup_internal( be );
+
+       if ( bdb2i_do_timing ) {
+
+               gettimeofday( &time2, NULL);
+               elapsed_time = bdb2i_elapsed( time1, time2 );
+               Debug( LDAP_DEBUG_ANY, "START elapsed=%s\n",
+                               elapsed_time, 0, 0 );
+               free( elapsed_time );
+
+       }
+}
+
+
+void
+bdb2_back_shutdown(
+    Backend    *be
+)
+{
+       struct timeval  time1, time2;
+       char   *elapsed_time;
+
+       gettimeofday( &time1, NULL );
+
+       bdb2i_back_shutdown_internal( be );
+
+       if ( bdb2i_do_timing ) {
+
+               gettimeofday( &time2, NULL);
+               elapsed_time = bdb2i_elapsed( time1, time2 );
+               Debug( LDAP_DEBUG_ANY, "SHUTDOWN elapsed=%s\n",
+                               elapsed_time, 0, 0 );
+               free( elapsed_time );
+
+       }
+}
+
+
+static void
+remove_old_locks( char *home )
+{
+       DB_ENV  dbEnv;
+       int     err;
+
+       memset( &dbEnv, 0, sizeof( DB_ENV ));
+       dbEnv.db_errcall = stderr;
+       dbEnv.db_errpfx  = "remove_old_locks(): db_appinit:";
+       dbEnv.lk_max     = 0;
+
+       if ( ( err = db_appinit( home, NULL, &dbEnv, 0 )) != 0 )
+               Debug( LDAP_DEBUG_ANY, "remove_old_locks(): db_appinit: %s\n",
+                                       strerror( err ), 0, 0);
+
+       dbEnv.db_errpfx  = "remove_old_locks(): lock_unlink:";
+       if ( ( err = lock_unlink( NULL, 1, &dbEnv )) != 0 )
+               Debug( LDAP_DEBUG_ANY, "remove_old_locks(): lock_unlink: %s\n",
+                                       strerror( err ), 0, 0);
+
+       dbEnv.db_errpfx  = "remove_old_locks(): db_appexit:";
+       if ( ( err = db_appexit( &dbEnv )) != 0 )
+               Debug( LDAP_DEBUG_ANY, "remove_old_locks(): db_appexit: %s\n",
+                                       strerror( err ), 0, 0);
+
+}
+
+
diff --git a/servers/slapd/back-bdb2/timing.c b/servers/slapd/back-bdb2/timing.c
new file mode 100644 (file)
index 0000000..a8f6cfc
--- /dev/null
@@ -0,0 +1,35 @@
+/* timing.c - timing bdb2 backend */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#include <ac/string.h>
+
+#include "slap.h"
+#include "back-bdb2.h"
+
+
+int  bdb2i_do_timing = 0;
+
+
+char *
+bdb2i_elapsed( struct timeval firsttime,  struct timeval secondtime )
+{
+    long int elapsedmicrosec, elapsedsec;
+    char elapsed_string[BUFSIZ];
+    
+    elapsedsec = secondtime.tv_sec - firsttime.tv_sec;
+    elapsedmicrosec = secondtime.tv_usec - firsttime.tv_usec;
+    if(elapsedmicrosec < 0) {
+        elapsedmicrosec += 1000000;
+        elapsedsec -= 1;
+    }
+
+    sprintf( elapsed_string, "%ld.%.6ld", elapsedsec, elapsedmicrosec );
+    return( strdup( elapsed_string ));
+}
+
+
diff --git a/servers/slapd/back-bdb2/txn.c b/servers/slapd/back-bdb2/txn.c
new file mode 100644 (file)
index 0000000..335f9c5
--- /dev/null
@@ -0,0 +1,321 @@
+/* txn.c - TP support functions of the bdb2 backend */
+
+#include "txn.h"
+
+
+void
+bdb2i_txn_head_init( BDB2_TXN_HEAD *head )
+{
+       int             dbFile;
+       BDB2_TXN_FILES  **fileNodeH;
+
+       /*  for each fixed DB file allocate a file descriptor node and
+        initialize the file's name  */
+       fileNodeH = &head->dbFiles;
+       for ( dbFile = BDB2_DB_DN_FILE; dbFile <= BDB2_DB_OC_IDX_FILE; dbFile++ ) {
+
+               char fileName[MAXPATHLEN];
+
+               *fileNodeH = head->dbFileHandle[dbFile] =
+                       (BDB2_TXN_FILES *) ch_calloc( 1, sizeof( BDB2_TXN_FILES ));
+               if ( *fileNodeH == NULL ) {
+
+                       Debug( LDAP_DEBUG_ANY, "bdb2i_txn_head_init(): out of memory!\n",
+                                       0, 0, 0 );
+                       exit( 1 );
+
+               }
+
+               sprintf( fileName, "%s%s", bdb2i_fixed_filenames[dbFile], LDBM_SUFFIX );
+               (*fileNodeH)->dbc_name = strdup( fileName );
+
+               fileNodeH = &(*fileNodeH)->next;
+
+       }
+
+}
+
+
+static void
+bdb2i_init_db_file_cache( struct ldbminfo *li, BDB2_TXN_FILES *fileinfo )
+{
+       time_t      curtime;
+       struct stat st;
+       char        buf[MAXPATHLEN];
+
+       pthread_mutex_lock( &currenttime_mutex );
+       curtime = currenttime;
+       pthread_mutex_unlock( &currenttime_mutex );
+
+       fileinfo->dbc_refcnt = 1;
+       fileinfo->dbc_lastref = curtime;
+
+       sprintf( buf, "%s%s%s", li->li_directory, DEFAULT_DIRSEP,
+                                       fileinfo->dbc_name );
+       if ( stat( buf, &st ) == 0 ) {
+               fileinfo->dbc_blksize = st.st_blksize;
+       } else {
+               fileinfo->dbc_blksize = DEFAULT_BLOCKSIZE;
+       }
+
+       fileinfo->dbc_maxids = ( fileinfo->dbc_blksize / sizeof( ID )) - 2;
+       fileinfo->dbc_maxindirect = ( SLAPD_LDBM_MIN_MAXIDS /
+               fileinfo->dbc_maxids ) + 1;
+
+}
+
+
+void
+bdb2i_txn_attr_config(
+       struct ldbminfo  *li,
+       char             *attr,
+       int              open )
+{
+       BDB2_TXN_HEAD  *head = &li->li_txn_head;
+
+       /*  the "attribute" 'default' is special  */
+       if ( strcasecmp( attr, "default" )) {
+
+               /*  create a new index file node, if the index is not known  already  */
+               BDB2_TXN_FILES  **fileNodeH;
+               char            fileName[MAXPATHLEN];
+
+               sprintf( fileName, "%s%s", attr,  LDBM_SUFFIX );
+
+               /*  search for the end of the list or a node describing
+                       the current attribute  */
+               for ( fileNodeH = &head->dbFiles;
+                               ( *fileNodeH && strcasecmp( (*fileNodeH)->dbc_name, fileName ));
+                               fileNodeH = &(*fileNodeH)->next ) {
+
+               }
+
+               /*  unless we have that attribute already...  */
+               if ( *fileNodeH == NULL ) {
+                       BDB2_TXN_FILES *p;
+
+                       Debug( LDAP_DEBUG_TRACE,
+                                       "bdb2i_txn_attr_config(): adding node for \"%s\"\n",
+                                       fileName, 0, 0 );
+
+                       /*  if we're out of memory, we have to see, how to exit...  */
+                       if ( ( *fileNodeH = p = (BDB2_TXN_FILES *)
+                                       ch_calloc( 1, sizeof( BDB2_TXN_FILES )) ) == NULL ) {
+
+                               Debug( LDAP_DEBUG_ANY,
+                                               "bdb2i_txn_attr_config(): out of memory -- FATAL.\n",
+                                               0, 0, 0 );
+
+                               /*  during configuration (no files are opened)
+                                       we can just exit, otherwise we kill ourself and
+                                       hope to shutdown cleanly...  */
+                               if ( open ) {
+                                       pthread_kill( pthread_self(), LDAP_SIGUSR1 );
+                               } else {
+                                       exit( 1 );
+                               }
+                       }
+
+                       p->dbc_name = strdup( fileName );
+
+                       /*  if requested for, we have to open the DB file  */
+                       /*  BUT NOT "objectclass", 'cause that's a default index !  */
+                       if ( open && strcasecmp( fileName, "objectclass" )) {
+
+                               /*  since we have an mpool, we should not define a cache size */
+                               p->dbc_db = ldbm_open_env( p->dbc_name, LDBM_WRCREAT,
+                                                       li->li_mode, 0, &li->li_db_env );
+
+                               /*  if the files could not be opened, something is wrong;
+                                       complain  */
+                               if ( p->dbc_db == NULL ) {
+
+                                       Debug( LDAP_DEBUG_ANY,
+                               "bdb2i_txn_open_files(): couldn't open file \"%s\" -- FATAL.\n",
+                                               p->dbc_name, 0, 0 );
+                                       pthread_kill( pthread_self(), LDAP_SIGUSR1 );
+
+                               }
+
+                               bdb2i_init_db_file_cache( li, p );
+
+                               Debug( LDAP_DEBUG_TRACE,
+                                       "bdb2i_txn_attr_config(): NEW INDEX FILE \"%s\"\n",
+                                       p->dbc_name, 0, 0 );
+
+                       }
+               }
+
+       } else {  /*  it is "attribute" 'default'  */
+
+               head->withDefIDX = BDB2_WITH_DEF_IDX;
+
+       }
+}
+
+
+void
+bdb2i_txn_open_files( struct ldbminfo *li )
+{
+       BDB2_TXN_HEAD   *head = &li->li_txn_head;
+       BDB2_TXN_FILES  *dbFile;
+
+       for ( dbFile = head->dbFiles; dbFile; dbFile = dbFile->next ) {
+
+               /*  since we have an mpool, we should not define a cache size */
+               dbFile->dbc_db = ldbm_open_env( dbFile->dbc_name, LDBM_WRCREAT,
+                                                       li->li_mode, 0, &li->li_db_env );
+
+               /*  if the files could not be opened, something is wrong; complain  */
+               if ( dbFile->dbc_db == NULL ) {
+
+                       Debug( LDAP_DEBUG_ANY,
+                               "bdb2i_txn_open_files(): couldn't open file \"%s\" -- FATAL.\n",
+                               dbFile->dbc_name, 0, 0 );
+                       exit( 1 );
+
+               }
+
+               /*  initialize the file info  */
+               bdb2i_init_db_file_cache( li, dbFile );
+
+               Debug( LDAP_DEBUG_TRACE, "bdb2i_txn_open_files(): OPEN INDEX \"%s\"\n",
+                               dbFile->dbc_name, 0, 0 );
+
+       }
+
+}
+
+
+void
+bdb2i_txn_close_files( BDB2_TXN_HEAD *head)
+{
+       BDB2_TXN_FILES  *dbFile;
+
+       for ( dbFile = head->dbFiles; dbFile; dbFile = dbFile->next ) {
+
+               ldbm_close( dbFile->dbc_db );
+
+       }
+}
+
+
+BDB2_TXN_FILES *
+bdb2i_get_db_file_cache( struct ldbminfo *li, char *name )
+{
+       BDB2_TXN_HEAD  *head = &li->li_txn_head;
+       BDB2_TXN_FILES *dbFile;
+       int            dbFileNum;
+
+       for ( dbFile = head->dbFiles; dbFile; dbFile = dbFile->next ) {
+
+               if ( !strcasecmp( dbFile->dbc_name, name )) return( dbFile );
+
+       }
+
+       Debug( LDAP_DEBUG_ANY,
+               "bdb2i_get_db_file_cache(): UPS, could't find \"%s\" \n", name, 0, 0 );
+
+       /*  ups, we couldn't find the file  */
+       return( NULL );
+
+}
+
+
+/*  check for new attribute indexes, that might have been created
+    during former runs of slapd  */
+/*  this is called during startup of the slapd server  */
+void
+bdb2i_check_additional_attr_index( struct ldbminfo *li )
+{
+       DIR            *datadir;
+       struct dirent  *file;
+
+       if ( ( datadir = opendir( li->li_directory ) ) == NULL ) {
+       /* if ( ( datadir = opendir( "/tmp" ) ) == NULL ) { */
+
+               Debug( LDAP_DEBUG_ANY,
+       "bdb2i_check_additional_attr_index(): ERROR while opening datadir: %s\n",
+                               strerror( errno ), 0, 0 );
+               exit( 1 );
+
+       }
+
+       for ( file = readdir( datadir ); file; file = readdir( datadir )) {
+               char  filename[MAXPATHLEN];
+               int   namelen;
+
+               strcpy( filename, file->d_name );
+               namelen = strlen( filename );
+
+               if ( namelen > strlen( LDBM_SUFFIX )) {
+
+                       if ( !strcasecmp( filename + namelen - strlen( LDBM_SUFFIX ),
+                                                       LDBM_SUFFIX )) {
+
+                               *(filename + namelen - strlen( LDBM_SUFFIX )) = '\0';
+                               bdb2i_txn_attr_config( li, filename, 0 );
+
+                               Debug( LDAP_DEBUG_TRACE, "INDEX FILE: %s\n", filename, 0, 0 );
+
+                       }
+
+               }
+
+       }
+
+       closedir( datadir );
+
+}
+
+
+/*  check for the addition of new attribute indexes during add  */
+/*  this is called after startup of the slapd server  */
+/*  DON'T WORRY ABOUT ACCESS RIGHTS, THAT MIGHT PREVENT US
+       FROM ADDING ATTRIBUTES LATER ON  */
+void
+bdb2i_check_default_attr_index_add( struct ldbminfo *li, Entry *e )
+{
+       BDB2_TXN_HEAD  *head = &li->li_txn_head;
+
+       if ( head->withDefIDX == BDB2_WITH_DEF_IDX ) {
+               Attribute   *ap;
+
+               for ( ap = e->e_attrs; ap != NULL; ap = ap->a_next ) {
+                       if ( strcasecmp( ap->a_type, "objectclass" ))
+                               bdb2i_txn_attr_config( li, ap->a_type, 1 );
+               }
+       }
+}
+
+
+/*  check for the addition of new attribute indexes during modify  */
+/*  this is called after startup of the slapd server  */
+/*  DON'T WORRY ABOUT ACCESS RIGHTS, THAT MIGHT PREVENT US
+       FROM ADDING ATTRIBUTES LATER ON  */
+void
+bdb2i_check_default_attr_index_mod( struct ldbminfo *li, LDAPModList *modlist )
+{
+       BDB2_TXN_HEAD  *head = &li->li_txn_head;
+
+       if ( head->withDefIDX == BDB2_WITH_DEF_IDX ) {
+               LDAPModList *ml;
+               char  *default_attrs[] = { "modifytimestamp", "modifiersname", NULL };
+               int   attr;
+
+               for ( ml = modlist; ml != NULL; ml = ml->ml_next ) {
+                       LDAPMod *mod = &ml->ml_mod;
+
+                       if (( mod->mod_op & ~LDAP_MOD_BVALUES ) == LDAP_MOD_ADD )
+                               if ( strcasecmp( mod->mod_type, "objectclass" ))
+                                       bdb2i_txn_attr_config( li, mod->mod_type, 1 );
+               }
+
+               /*  these attributes are default when modifying  */
+               for ( attr = 0; default_attrs[attr]; attr++ ) {
+                       bdb2i_txn_attr_config( li, default_attrs[attr], 1 );
+               }
+       }
+}
+
+
diff --git a/servers/slapd/back-bdb2/txn.h b/servers/slapd/back-bdb2/txn.h
new file mode 100644 (file)
index 0000000..a0d060e
--- /dev/null
@@ -0,0 +1,38 @@
+/* txn.h - Header for TP support functions of the bdb2 backend */
+
+#ifndef _BDB2_TXN_H_
+#define _BDB2_TXN_H_
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <dirent.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+#include <ac/signal.h>
+
+#include "ldapconfig.h"
+#include "slap.h"
+#include "back-bdb2.h"
+
+
+
+#define  BDB2_TXN_CHKP_MAX_CNT     20                   /*  checkpoint every
+                                                            20 transactions */
+#define  BDB2_TXN_CHKP_MAX_TIME    600                  /*  checkpoint after
+                                                            600 seconds     */
+
+
+char  *bdb2i_fixed_filenames[] = {
+
+               "dn", "dn2id", "id2entry", "id2children", "objectclass"
+
+       };
+
+
+#endif  /*  _BDB2_TXN_H_  */
+
diff --git a/servers/slapd/back-bdb2/unbind.c b/servers/slapd/back-bdb2/unbind.c
new file mode 100644 (file)
index 0000000..f183765
--- /dev/null
@@ -0,0 +1,48 @@
+/* unbind.c - handle an ldap unbind operation */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "back-bdb2.h"
+
+static int
+bdb2i_back_unbind_internal(
+       Backend     *be,
+       Connection  *conn,
+       Operation   *op
+)
+{
+       return( 0 );
+}
+
+
+int
+bdb2_back_unbind(
+       Backend     *be,
+       Connection  *conn,
+       Operation   *op
+)
+{
+       struct timeval  time1, time2;
+       char   *elapsed_time;
+       int    ret;
+
+       gettimeofday( &time1, NULL );
+
+       ret = bdb2i_back_unbind_internal( be, conn, op );
+
+       if ( bdb2i_do_timing ) {
+
+               gettimeofday( &time2, NULL);
+               elapsed_time = bdb2i_elapsed( time1, time2 );
+               Debug( LDAP_DEBUG_ANY, "conn=%d op=%d UNBIND elapsed=%s\n",
+                               conn->c_connid, op->o_opid, elapsed_time );
+               free( elapsed_time );
+
+       }
+
+       return( ret );
+}
index bc818ada9c5de5ce013e5fa124242f2f413fce34..a6e780143eb2bfc9f08c07f731e690699dfd430d 100644 (file)
@@ -315,7 +315,6 @@ char *derefDN ( Backend     *be,
   }
   
   Debug( LDAP_DEBUG_TRACE, "<= returning deref DN of \"%s\"\n", newDN, 0, 0 ); 
-       if (matched != NULL) free(matched);
 
   return newDN;
 }