From: Kurt Spanier Date: Fri, 5 Feb 1999 16:23:03 +0000 (+0000) Subject: Introduction of a new Berkeley DB version 2 (!) specific backend. X-Git-Tag: OPENLDAP_SLAPD_BACK_LDAP~625 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=d92b9d3c9e1e1891d800afe908a80cbc978f1772;p=openldap Introduction of a new Berkeley DB version 2 (!) specific backend. 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. --- diff --git a/servers/slapd/back-bdb2/Makefile.in b/servers/slapd/back-bdb2/Makefile.in new file mode 100644 index 0000000000..b2ca6b4ad8 --- /dev/null +++ b/servers/slapd/back-bdb2/Makefile.in @@ -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 index 0000000000..6231e0530f --- /dev/null +++ b/servers/slapd/back-bdb2/Version.c @@ -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 index 0000000000..1d9c1e9daf --- /dev/null +++ b/servers/slapd/back-bdb2/abandon.c @@ -0,0 +1,55 @@ +/* abandon.c - ldbm backend abandon routine */ + +#include "portable.h" + +#include +#include +#include + +#include + +#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 index 0000000000..ef3b1031e7 --- /dev/null +++ b/servers/slapd/back-bdb2/add.c @@ -0,0 +1,289 @@ +/* add.c - ldap bdb2 back-end add routine */ + +#include "portable.h" + +#include + +#include +#include + +#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 index 0000000000..984ca5683a --- /dev/null +++ b/servers/slapd/back-bdb2/alias.c @@ -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 +#include +#include /* 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 index 0000000000..729ed9e91f --- /dev/null +++ b/servers/slapd/back-bdb2/attr.c @@ -0,0 +1,171 @@ +/* attr.c - backend routines for dealing with attributes */ + +#include "portable.h" + +#include + +#include +#include + +#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 index 0000000000..64f9c26c69 --- /dev/null +++ b/servers/slapd/back-bdb2/back-bdb2.h @@ -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 index 0000000000..264c216a6a --- /dev/null +++ b/servers/slapd/back-bdb2/bind.c @@ -0,0 +1,262 @@ +/* bind.c - bdb2 backend bind and unbind routines */ + +#include "portable.h" + +#include + +#include +#include +#include +#include + +#include "slap.h" +#include "back-bdb2.h" +#include "proto-back-bdb2.h" + +#include + +#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 index 0000000000..8e696e1c88 --- /dev/null +++ b/servers/slapd/back-bdb2/cache.c @@ -0,0 +1,462 @@ +/* cache.c - routines to maintain an in-core cache of entries */ + +#include "portable.h" + +#include + +#include +#include +#include + +#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 index 0000000000..90dda95068 --- /dev/null +++ b/servers/slapd/back-bdb2/close.c @@ -0,0 +1,52 @@ +/* close.c - close bdb2 backend */ + +#include "portable.h" + +#include + +#include + +#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 index 0000000000..df38525b29 --- /dev/null +++ b/servers/slapd/back-bdb2/compare.c @@ -0,0 +1,107 @@ +/* compare.c - bdb2 backend compare routine */ + +#include "portable.h" + +#include + +#include +#include + +#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 index 0000000000..92d2e46db2 --- /dev/null +++ b/servers/slapd/back-bdb2/config.c @@ -0,0 +1,138 @@ +/* config.c - bdb2 backend configuration file routine */ + +#include "portable.h" + +#include + +#include +#include + +#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 \" 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 \" 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 [pres,eq,approx,sub]\" line\n", + fname, lineno ); + return( 1 ); + } else if ( argc > 3 ) { + fprintf( stderr, +"%s: line %d: extra junk after \"index [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 \" 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 \" 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 index 0000000000..259245a7ac --- /dev/null +++ b/servers/slapd/back-bdb2/dbcache.c @@ -0,0 +1,252 @@ +/* ldbmcache.c - maintain a cache of open bdb2 files */ + +#include "portable.h" + +#include + +#include +#include +#include +#include + +#include + +#ifdef HAVE_SYS_PARAM_H +#include +#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( ¤ttime_mutex ); + curtime = currenttime; + ldap_pvt_thread_mutex_unlock( ¤ttime_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 index 0000000000..241734c4d6 --- /dev/null +++ b/servers/slapd/back-bdb2/delete.c @@ -0,0 +1,194 @@ +/* delete.c - bdb2 backend delete routine */ + +#include "portable.h" + +#include + +#include +#include + +#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 index 0000000000..5d960e5a1b --- /dev/null +++ b/servers/slapd/back-bdb2/dn2id.c @@ -0,0 +1,232 @@ +/* dn2id.c - routines to deal with the dn2id index */ + +#include "portable.h" + +#include + +#include +#include + +#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 index 0000000000..0617d482e2 --- /dev/null +++ b/servers/slapd/back-bdb2/external.h @@ -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 index 0000000000..71b3556c9a --- /dev/null +++ b/servers/slapd/back-bdb2/filterindex.c @@ -0,0 +1,353 @@ +/* filterindex.c - generate the list of candidate entries from a filter */ + +#include "portable.h" + +#include + +#include +#include + +#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 index 0000000000..959699a259 --- /dev/null +++ b/servers/slapd/back-bdb2/group.c @@ -0,0 +1,171 @@ +/* group.c - bdb2 backend acl group routine */ + +#include "portable.h" + +#include + +#include +#include + +#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 index 0000000000..d404e393da --- /dev/null +++ b/servers/slapd/back-bdb2/id2children.c @@ -0,0 +1,139 @@ +/* id2children.c - routines to deal with the id2children index */ + +#include "portable.h" + +#include +#include + +#include + +#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 index 0000000000..d999c54ceb --- /dev/null +++ b/servers/slapd/back-bdb2/id2entry.c @@ -0,0 +1,174 @@ +/* id2entry.c - routines to deal with the id2entry index */ + +#include "portable.h" + +#include + +#include + +#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 index 0000000000..d9be424ced --- /dev/null +++ b/servers/slapd/back-bdb2/idl.c @@ -0,0 +1,1032 @@ +/* idl.c - ldap id list handling routines */ + +#include "portable.h" + +#include + +#include +#include + +#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 index 0000000000..9d99a38d99 --- /dev/null +++ b/servers/slapd/back-bdb2/index.c @@ -0,0 +1,362 @@ +/* index.c - routines for dealing with attribute indexes */ + +#include "portable.h" + +#include + +#include +#include + +#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 index 0000000000..6a3f55eb8e --- /dev/null +++ b/servers/slapd/back-bdb2/init.c @@ -0,0 +1,196 @@ +/* init.c - initialize bdb2 backend */ + +#include "portable.h" + +#include + +#include +#include + +#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 index 0000000000..c7a917a893 --- /dev/null +++ b/servers/slapd/back-bdb2/kerberos.c @@ -0,0 +1,48 @@ +/* kerberos.c - bdb2 backend kerberos bind routines */ + +#include "portable.h" + +#ifdef HAVE_KERBEROS + +#include + +#include +#include +#include + +#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 index 0000000000..f36ba6e653 --- /dev/null +++ b/servers/slapd/back-bdb2/modify.c @@ -0,0 +1,264 @@ +/* modify.c - bdb2 backend modify routine */ + +#include "portable.h" + +#include + +#include +#include + +#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 index 0000000000..b79a429a46 --- /dev/null +++ b/servers/slapd/back-bdb2/modrdn.c @@ -0,0 +1,237 @@ +/* modrdn.c - bdb2 backend modrdn routine */ + +#include "portable.h" + +#include + +#include +#include + +#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 index 0000000000..c4d1eb4719 --- /dev/null +++ b/servers/slapd/back-bdb2/nextid.c @@ -0,0 +1,184 @@ +/* id.c - keep track of the next id to be given out */ + +#include "portable.h" + +#include + +#include + +#ifdef HAVE_SYS_PARAM_H +#include +#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 index 0000000000..6047463614 --- /dev/null +++ b/servers/slapd/back-bdb2/porter.c @@ -0,0 +1,127 @@ +/* porter.c - port functions of the bdb2 backend */ + +#include "portable.h" + +#include +#include + +#include + +#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 index 0000000000..f85fabc8bb --- /dev/null +++ b/servers/slapd/back-bdb2/proto-back-bdb2.h @@ -0,0 +1,177 @@ +#ifndef _PROTO_BACK_BDB2 +#define _PROTO_BACK_BDB2 + +#include + +#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 index 0000000000..75afff6edb --- /dev/null +++ b/servers/slapd/back-bdb2/search.c @@ -0,0 +1,559 @@ +/* search.c - bdb2 backend search function */ + +#include "portable.h" + +#include + +#include +#include + +#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( ¤ttime_mutex ); + time( ¤ttime ); + if ( tlimit != -1 && currenttime > stoptime ) { + ldap_pvt_thread_mutex_unlock( ¤ttime_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( ¤ttime_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 index 0000000000..c27f6ad54d --- /dev/null +++ b/servers/slapd/back-bdb2/startup.c @@ -0,0 +1,217 @@ +/* startup.c - startup bdb2 backend */ + +#include "portable.h" + +#include + +#include +#include + +#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 index 0000000000..a8f6cfcb7d --- /dev/null +++ b/servers/slapd/back-bdb2/timing.c @@ -0,0 +1,35 @@ +/* timing.c - timing bdb2 backend */ + +#include "portable.h" + +#include +#include +#include + +#include + +#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 index 0000000000..335f9c549f --- /dev/null +++ b/servers/slapd/back-bdb2/txn.c @@ -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( ¤ttime_mutex ); + curtime = currenttime; + pthread_mutex_unlock( ¤ttime_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 index 0000000000..a0d060e050 --- /dev/null +++ b/servers/slapd/back-bdb2/txn.h @@ -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 +#include +#include +#include +#include + +#include +#include +#include + +#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 index 0000000000..f183765317 --- /dev/null +++ b/servers/slapd/back-bdb2/unbind.c @@ -0,0 +1,48 @@ +/* unbind.c - handle an ldap unbind operation */ + +#include "portable.h" + +#include +#include + +#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 ); +} diff --git a/servers/slapd/back-ldbm/alias.c b/servers/slapd/back-ldbm/alias.c index bc818ada9c..a6e780143e 100644 --- a/servers/slapd/back-ldbm/alias.c +++ b/servers/slapd/back-ldbm/alias.c @@ -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; }