--- /dev/null
+.TH SLAPD-MDB 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2011 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapd\-mdb \- Memory-Mapped DB backend to slapd
+.SH SYNOPSIS
+.B ETCDIR/slapd.conf
+.SH DESCRIPTION
+The \fBmdb\fP backend to
+.BR slapd (8)
+uses OpenLDAP's own Memory-Mapped DB (MDB) library to store data.
+It relies completely on the underlying operating system for memory
+management and does no caching of its own.
+.LP
+The \fBmdb\fP backend is similar to the \fBhdb\fP backend in that
+it uses a hierarchical database layout which
+supports subtree renames. It is both more space-efficient and more
+execution-efficient than the \fBbdb\fP backend, while being overall
+much simpler to manage.
+.SH CONFIGURATION
+These
+.B slapd.conf
+options apply to the \fBmdb\fP backend database.
+That is, they must follow a "database mdb" line and
+come before any subsequent "backend" or "database" lines.
+Other database options are described in the
+.BR slapd.conf (5)
+manual page.
+.TP
+.BI checkpoint \ <kbyte>\ <min>
+Specify the frequency for flushing the database disk buffers.
+This setting is only needed if the \fBdbnosync\fP option is used.
+The checkpoint will occur if either \fI<kbyte>\fP data has been written or
+\fI<min>\fP minutes have passed since the last checkpoint.
+Both arguments default to zero, in which case they are ignored. When
+the \fI<min>\fP argument is non-zero, an internal task will run every
+\fI<min>\fP minutes to perform the checkpoint.
+Note: currently the \fI<kbyte>\fP setting is unimplemented.
+.TP
+.B dbnosync
+Specify that on-disk database contents should not be immediately
+synchronized with in memory changes.
+Enabling this option may improve performance at the expense of data
+security. By default, a full data flush/sync is performed when each
+transaction is committed.
+.TP
+.BI directory \ <directory>
+Specify the directory where the MDB files containing this database and
+associated indexes live.
+A separate directory must be specified for each database.
+The default is
+.BR LOCALSTATEDIR/openldap\-data .
+.TP
+\fBindex \fR{\fI<attrlist>\fR|\fBdefault\fR} [\fBpres\fR,\fBeq\fR,\fBapprox\fR,\fBsub\fR,\fI<special>\fR]
+Specify the indexes to maintain for the given attribute (or
+list of attributes).
+Some attributes only support a subset of indexes.
+If only an \fI<attr>\fP is given, the indices specified for \fBdefault\fR
+are maintained.
+Note that setting a default does not imply that all attributes will be
+indexed. Also, for best performance, an
+.B eq
+index should always be configured for the
+.B objectClass
+attribute.
+
+A number of special index parameters may be specified.
+The index type
+.B sub
+can be decomposed into
+.BR subinitial ,
+.BR subany ,\ and
+.B subfinal
+indices.
+The special type
+.B nolang
+may be specified to disallow use of this index by language subtypes.
+The special type
+.B nosubtypes
+may be specified to disallow use of this index by named subtypes.
+Note: changing \fBindex\fP settings in
+.BR slapd.conf (5)
+requires rebuilding indices, see
+.BR slapindex (8);
+changing \fBindex\fP settings
+dynamically by LDAPModifying "cn=config" automatically causes rebuilding
+of the indices online in a background task.
+.TP
+.BI maxreaders \ <integer>
+Specify the maximum number of threads that may have concurrent read access
+to the database. Tools such as slapcat count as a single thread,
+in addition to threads in any active slapd processes. The
+default is 126.
+.TP
+.BI maxsize \ <bytes>
+Specify the maximum size of the database in bytes. A memory map of this
+size is allocated at startup time and the database will not be allowed
+to grow beyond this size. The default is 10485760 bytes. This setting
+may be changed upward if the configured limit needs to be increased.
+
+Note: It is important to set this to as large a value as possible,
+(relative to anticipated growth of the actual data over time) since
+growing the size later may not be practical when the system is under
+heavy load.
+.TP
+.BI mode \ <integer>
+Specify the file protection mode that newly created database
+files should have.
+The default is 0600.
+.TP
+.BI searchstack \ <depth>
+Specify the depth of the stack used for search filter evaluation.
+Search filters are evaluated on a stack to accommodate nested AND / OR
+clauses. An individual stack is assigned to each server thread.
+The depth of the stack determines how complex a filter can be
+evaluated without requiring any additional memory allocation. Filters that
+are nested deeper than the search stack depth will cause a separate
+stack to be allocated for that particular search operation. These
+allocations can have a major negative impact on server performance,
+but specifying too much stack will also consume a great deal of memory.
+Each search stack uses 512K bytes per level. The default stack depth
+is 16, thus 8MB per thread is used.
+.SH ACCESS CONTROL
+The
+.B mdb
+backend honors access control semantics as indicated in
+.BR slapd.access (5).
+.SH FILES
+.TP
+.B ETCDIR/slapd.conf
+default
+.B slapd
+configuration file
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd\-config (5),
+.BR slapd (8),
+.BR slapadd (8),
+.BR slapcat (8),
+.BR slapindex (8),
+OpenLDAP MDB documentation.
+.SH ACKNOWLEDGEMENTS
+.so ../Project
+Written by Howard Chu.
--- /dev/null
+# Makefile.in for back-mdb
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2011 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+SRCS = init.c tools.c config.c \
+ add.c bind.c compare.c delete.c modify.c modrdn.c search.c \
+ extended.c referral.c operational.c \
+ attr.c index.c key.c filterindex.c \
+ dn2entry.c dn2id.c id2entry.c idl.c \
+ nextid.c monitor.c
+
+OBJS = init.lo tools.lo config.lo \
+ add.lo bind.lo compare.lo delete.lo modify.lo modrdn.lo search.lo \
+ extended.lo referral.lo operational.lo \
+ attr.lo index.lo key.lo filterindex.lo \
+ dn2entry.lo dn2id.lo id2entry.lo idl.lo \
+ nextid.lo monitor.lo mdb.lo midl.lo
+
+LDAP_INCDIR= ../../../include
+LDAP_LIBDIR= ../../../libraries
+
+BUILD_OPT = "--enable-mdb"
+BUILD_MOD = @BUILD_MDB@
+
+mod_DEFS = -DSLAPD_IMPORT
+MOD_DEFS = $(@BUILD_MDB@_DEFS)
+MOD_LIBS = $(MDB_LIBS)
+
+shared_LDAP_LIBS = $(LDAP_LIBLDAP_R_LA) $(LDAP_LIBLBER_LA)
+NT_LINK_LIBS = -L.. -lslapd $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS)
+UNIX_LINK_LIBS = $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS)
+
+LIBBASE = back_mdb
+
+XINCPATH = -I.. -I$(srcdir)/.. -I$(srcdir)/$(LDAP_LIBDIR)/libmdb
+XDEFS = $(MODULES_CPPFLAGS)
+
+all-local-lib: ../.backend
+
+../.backend: lib$(LIBBASE).a
+ @touch $@
+
+mdb.lo: $(srcdir)/$(LDAP_LIBDIR)/libmdb/mdb.c
+ $(LTCOMPILE_MOD) $<
+
+midl.lo: $(srcdir)/$(LDAP_LIBDIR)/libmdb/midl.c
+ $(LTCOMPILE_MOD) $<
+
+veryclean-local-lib: FORCE
+ $(RM) $(XXHEADERS) $(XXSRCS) .links
--- /dev/null
+/* add.c - ldap mdb back-end add routine */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2011 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-mdb.h"
+
+int
+mdb_add(Operation *op, SlapReply *rs )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ struct berval pdn;
+ Entry *p = NULL, *oe = op->ora_e;
+ char textbuf[SLAP_TEXT_BUFLEN];
+ size_t textlen = sizeof textbuf;
+ AttributeDescription *children = slap_schema.si_ad_children;
+ AttributeDescription *entry = slap_schema.si_ad_entry;
+ MDB_txn *txn = NULL;
+ ID eid = NOID, pid = 0;
+ mdb_op_info opinfo = {{{ 0 }}}, *moi = &opinfo;
+ int subentry;
+
+ int success;
+
+ LDAPControl **postread_ctrl = NULL;
+ LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
+ int num_ctrls = 0;
+
+#ifdef LDAP_X_TXN
+ int settle = 0;
+#endif
+
+ Debug(LDAP_DEBUG_ARGS, "==> " LDAP_XSTRING(mdb_add) ": %s\n",
+ op->ora_e->e_name.bv_val, 0, 0);
+
+#ifdef LDAP_X_TXN
+ if( op->o_txnSpec ) {
+ /* acquire connection lock */
+ ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
+ if( op->o_conn->c_txn == CONN_TXN_INACTIVE ) {
+ rs->sr_text = "invalid transaction identifier";
+ rs->sr_err = LDAP_X_TXN_ID_INVALID;
+ goto txnReturn;
+ } else if( op->o_conn->c_txn == CONN_TXN_SETTLE ) {
+ settle=1;
+ goto txnReturn;
+ }
+
+ if( op->o_conn->c_txn_backend == NULL ) {
+ op->o_conn->c_txn_backend = op->o_bd;
+
+ } else if( op->o_conn->c_txn_backend != op->o_bd ) {
+ rs->sr_text = "transaction cannot span multiple database contexts";
+ rs->sr_err = LDAP_AFFECTS_MULTIPLE_DSAS;
+ goto txnReturn;
+ }
+
+ /* insert operation into transaction */
+
+ rs->sr_text = "transaction specified";
+ rs->sr_err = LDAP_X_TXN_SPECIFY_OKAY;
+
+txnReturn:
+ /* release connection lock */
+ ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
+
+ if( !settle ) {
+ send_ldap_result( op, rs );
+ return rs->sr_err;
+ }
+ }
+#endif
+
+ ctrls[num_ctrls] = 0;
+
+ /* check entry's schema */
+ rs->sr_err = entry_schema_check( op, op->ora_e, NULL,
+ get_relax(op), 1, NULL, &rs->sr_text, textbuf, textlen );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_add) ": entry failed schema check: "
+ "%s (%d)\n", rs->sr_text, rs->sr_err, 0 );
+ goto return_results;
+ }
+
+ /* add opattrs to shadow as well, only missing attrs will actually
+ * be added; helps compatibility with older OL versions */
+ rs->sr_err = slap_add_opattrs( op, &rs->sr_text, textbuf, textlen, 1 );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_add) ": entry failed op attrs add: "
+ "%s (%d)\n", rs->sr_text, rs->sr_err, 0 );
+ goto return_results;
+ }
+
+ if ( get_assert( op ) &&
+ ( test_filter( op, op->ora_e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
+ {
+ rs->sr_err = LDAP_ASSERTION_FAILED;
+ goto return_results;
+ }
+
+ subentry = is_entry_subentry( op->ora_e );
+
+ /* begin transaction */
+ rs->sr_err = mdb_opinfo_get( op, mdb, 0, &moi );
+ rs->sr_text = NULL;
+ if( rs->sr_err != 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_add) ": txn_begin failed: %s (%d)\n",
+ mdb_strerror(rs->sr_err), rs->sr_err, 0 );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ txn = moi->moi_txn;
+
+ /*
+ * Get the parent dn and see if the corresponding entry exists.
+ */
+ if ( be_issuffix( op->o_bd, &op->ora_e->e_nname ) ) {
+ pdn = slap_empty_bv;
+ } else {
+ dnParent( &op->ora_e->e_nname, &pdn );
+ }
+
+ /* get entry or parent */
+ rs->sr_err = mdb_dn2entry( op, txn, &op->ora_e->e_nname, &p, 1 );
+ switch( rs->sr_err ) {
+ case 0:
+ rs->sr_err = LDAP_ALREADY_EXISTS;
+ mdb_entry_return( p );
+ p = NULL;
+ goto return_results;
+ case MDB_NOTFOUND:
+ break;
+ case LDAP_BUSY:
+ rs->sr_text = "ldap server busy";
+ goto return_results;
+ default:
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ if ( !p )
+ p = (Entry *)&slap_entry_root;
+
+ if ( !bvmatch( &pdn, &p->e_nname ) ) {
+ rs->sr_matched = ber_strdup_x( p->e_name.bv_val,
+ op->o_tmpmemctx );
+ if ( p != (Entry *)&slap_entry_root && is_entry_referral( p )) {
+ BerVarray ref = get_entry_referrals( op, p );
+ rs->sr_ref = referral_rewrite( ref, &p->e_name,
+ &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+ ber_bvarray_free( ref );
+ } else {
+ rs->sr_ref = NULL;
+ }
+ if ( p != (Entry *)&slap_entry_root )
+ mdb_entry_return( p );
+ p = NULL;
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_add) ": parent "
+ "does not exist\n", 0, 0, 0 );
+
+ rs->sr_err = LDAP_REFERRAL;
+ rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
+ goto return_results;
+ }
+
+ rs->sr_err = access_allowed( op, p,
+ children, NULL, ACL_WADD, NULL );
+
+ if ( ! rs->sr_err ) {
+ if ( p != (Entry *)&slap_entry_root )
+ mdb_entry_return( p );
+ p = NULL;
+
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_add) ": no write access to parent\n",
+ 0, 0, 0 );
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ rs->sr_text = "no write access to parent";
+ goto return_results;;
+ }
+
+ if ( p != (Entry *)&slap_entry_root ) {
+ if ( is_entry_subentry( p ) ) {
+ mdb_entry_return( p );
+ p = NULL;
+ /* parent is a subentry, don't allow add */
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_add) ": parent is subentry\n",
+ 0, 0, 0 );
+ rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
+ rs->sr_text = "parent is a subentry";
+ goto return_results;;
+ }
+
+ if ( is_entry_alias( p ) ) {
+ mdb_entry_return( p );
+ p = NULL;
+ /* parent is an alias, don't allow add */
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_add) ": parent is alias\n",
+ 0, 0, 0 );
+ rs->sr_err = LDAP_ALIAS_PROBLEM;
+ rs->sr_text = "parent is an alias";
+ goto return_results;;
+ }
+
+ if ( is_entry_referral( p ) ) {
+ BerVarray ref = get_entry_referrals( op, p );
+ /* parent is a referral, don't allow add */
+ rs->sr_matched = ber_strdup_x( p->e_name.bv_val,
+ op->o_tmpmemctx );
+ rs->sr_ref = referral_rewrite( ref, &p->e_name,
+ &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+ ber_bvarray_free( ref );
+ mdb_entry_return( p );
+ p = NULL;
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_add) ": parent is referral\n",
+ 0, 0, 0 );
+
+ rs->sr_err = LDAP_REFERRAL;
+ rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
+ goto return_results;
+ }
+
+ }
+
+ if ( subentry ) {
+ /* FIXME: */
+ /* parent must be an administrative point of the required kind */
+ }
+
+ /* free parent */
+ if ( p != (Entry *)&slap_entry_root ) {
+ pid = p->e_id;
+ if ( p->e_nname.bv_len ) {
+ struct berval ppdn;
+
+ /* ITS#5326: use parent's DN if differs from provided one */
+ dnParent( &op->ora_e->e_name, &ppdn );
+ if ( !dn_match( &p->e_name, &ppdn ) ) {
+ struct berval rdn;
+ struct berval newdn;
+
+ dnRdn( &op->ora_e->e_name, &rdn );
+
+ build_new_dn( &newdn, &p->e_name, &rdn, NULL );
+ if ( op->ora_e->e_name.bv_val != op->o_req_dn.bv_val )
+ ber_memfree( op->ora_e->e_name.bv_val );
+ op->ora_e->e_name = newdn;
+
+ /* FIXME: should check whether
+ * dnNormalize(newdn) == e->e_nname ... */
+ }
+ }
+
+ mdb_entry_return( p );
+ }
+ p = NULL;
+
+ rs->sr_err = access_allowed( op, op->ora_e,
+ entry, NULL, ACL_WADD, NULL );
+
+ if ( ! rs->sr_err ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_add) ": no write access to entry\n",
+ 0, 0, 0 );
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ rs->sr_text = "no write access to entry";
+ goto return_results;;
+ }
+
+ /*
+ * Check ACL for attribute write access
+ */
+ if (!acl_check_modlist(op, oe, op->ora_modlist)) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_add) ": no write access to attribute\n",
+ 0, 0, 0 );
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ rs->sr_text = "no write access to attribute";
+ goto return_results;;
+ }
+
+ if ( eid == NOID ) {
+ rs->sr_err = mdb_next_id( op->o_bd, txn, &eid );
+ if( rs->sr_err != 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_add) ": next_id failed (%d)\n",
+ rs->sr_err, 0, 0 );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+ op->ora_e->e_id = eid;
+ }
+
+ /* dn2id index */
+ rs->sr_err = mdb_dn2id_add( op, txn, pid, op->ora_e );
+ if ( rs->sr_err != 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_add) ": dn2id_add failed: %s (%d)\n",
+ mdb_strerror(rs->sr_err), rs->sr_err, 0 );
+
+ switch( rs->sr_err ) {
+ case MDB_KEYEXIST:
+ rs->sr_err = LDAP_ALREADY_EXISTS;
+ break;
+ default:
+ rs->sr_err = LDAP_OTHER;
+ }
+ goto return_results;
+ }
+
+ /* attribute indexes */
+ rs->sr_err = mdb_index_entry_add( op, txn, op->ora_e );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_add) ": index_entry_add failed\n",
+ 0, 0, 0 );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "index generation failed";
+ goto return_results;
+ }
+
+ /* id2entry index */
+ rs->sr_err = mdb_id2entry_add( op, txn, op->ora_e );
+ if ( rs->sr_err != 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_add) ": id2entry_add failed\n",
+ 0, 0, 0 );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "entry store failed";
+ goto return_results;
+ }
+
+ /* post-read */
+ if( op->o_postread ) {
+ if( postread_ctrl == NULL ) {
+ postread_ctrl = &ctrls[num_ctrls++];
+ ctrls[num_ctrls] = NULL;
+ }
+ if ( slap_read_controls( op, rs, op->ora_e,
+ &slap_post_read_bv, postread_ctrl ) )
+ {
+ Debug( LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(mdb_add) ": post-read "
+ "failed!\n", 0, 0, 0 );
+ if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
+ /* FIXME: is it correct to abort
+ * operation if control fails? */
+ goto return_results;
+ }
+ }
+ }
+
+ if ( moi == &opinfo ) {
+ LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.moi_oe, OpExtra, oe_next );
+ opinfo.moi_oe.oe_key = NULL;
+ if ( op->o_noop ) {
+ mdb_txn_abort( txn );
+ rs->sr_err = LDAP_X_NO_OPERATION;
+ txn = NULL;
+ goto return_results;
+ }
+
+ if (( rs->sr_err = mdb_txn_commit( txn )) != 0 ) {
+ rs->sr_text = "txn_commit failed";
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_add) ": %s : %s (%d)\n",
+ rs->sr_text, mdb_strerror(rs->sr_err), rs->sr_err );
+ rs->sr_err = LDAP_OTHER;
+ goto return_results;
+ }
+ txn = NULL;
+ }
+
+ Debug(LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_add) ": added%s id=%08lx dn=\"%s\"\n",
+ op->o_noop ? " (no-op)" : "",
+ op->ora_e->e_id, op->ora_e->e_dn );
+
+ rs->sr_text = NULL;
+ if( num_ctrls ) rs->sr_ctrls = ctrls;
+
+return_results:
+ success = rs->sr_err;
+ send_ldap_result( op, rs );
+
+ if( moi == &opinfo ) {
+ if( txn != NULL ) {
+ mdb_txn_abort( txn );
+ }
+ if ( opinfo.moi_oe.oe_key ) {
+ LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.moi_oe, OpExtra, oe_next );
+ }
+ }
+
+ if( success == LDAP_SUCCESS ) {
+#if 0
+ if ( mdb->bi_txn_cp_kbyte ) {
+ TXN_CHECKPOINT( mdb->bi_dbenv,
+ mdb->bi_txn_cp_kbyte, mdb->bi_txn_cp_min, 0 );
+ }
+#endif
+ }
+
+ slap_graduate_commit_csn( op );
+
+ if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) {
+ slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
+ slap_sl_free( *postread_ctrl, op->o_tmpmemctx );
+ }
+ return rs->sr_err;
+}
--- /dev/null
+/* attr.c - backend routines for dealing with attributes */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2011 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "back-mdb.h"
+#include "config.h"
+#include "lutil.h"
+
+/* Find the ad, return -1 if not found,
+ * set point for insertion if ins is non-NULL
+ */
+int
+mdb_attr_slot( struct mdb_info *mdb, AttributeDescription *ad, int *ins )
+{
+ unsigned base = 0, cursor = 0;
+ unsigned n = mdb->mi_nattrs;
+ int val = 0;
+
+ while ( 0 < n ) {
+ unsigned pivot = n >> 1;
+ cursor = base + pivot;
+
+ val = SLAP_PTRCMP( ad, mdb->mi_attrs[cursor]->ai_desc );
+ if ( val < 0 ) {
+ n = pivot;
+ } else if ( val > 0 ) {
+ base = cursor + 1;
+ n -= pivot + 1;
+ } else {
+ return cursor;
+ }
+ }
+ if ( ins ) {
+ if ( val > 0 )
+ ++cursor;
+ *ins = cursor;
+ }
+ return -1;
+}
+
+static int
+ainfo_insert( struct mdb_info *mdb, AttrInfo *a )
+{
+ int x;
+ int i = mdb_attr_slot( mdb, a->ai_desc, &x );
+
+ /* Is it a dup? */
+ if ( i >= 0 )
+ return -1;
+
+ mdb->mi_attrs = ch_realloc( mdb->mi_attrs, ( mdb->mi_nattrs+1 ) *
+ sizeof( AttrInfo * ));
+ if ( x < mdb->mi_nattrs )
+ AC_MEMCPY( &mdb->mi_attrs[x+1], &mdb->mi_attrs[x],
+ ( mdb->mi_nattrs - x ) * sizeof( AttrInfo *));
+ mdb->mi_attrs[x] = a;
+ mdb->mi_nattrs++;
+ return 0;
+}
+
+AttrInfo *
+mdb_attr_mask(
+ struct mdb_info *mdb,
+ AttributeDescription *desc )
+{
+ int i = mdb_attr_slot( mdb, desc, NULL );
+ return i < 0 ? NULL : mdb->mi_attrs[i];
+}
+
+/* Open all un-opened index DB handles */
+int
+mdb_attr_dbs_open(
+ BackendDB *be, MDB_txn *tx0, ConfigReply *cr )
+{
+ struct mdb_info *mdb = (struct mdb_info *) be->be_private;
+ MDB_txn *txn;
+ int i, flags;
+ int rc;
+
+ txn = tx0;
+ if ( txn == NULL ) {
+ rc = mdb_txn_begin( mdb->mi_dbenv, 0, &txn );
+ if ( rc ) {
+ snprintf( cr->msg, sizeof(cr->msg), "database \"%s\": "
+ "txn_begin failed: %s (%d).",
+ be->be_suffix[0].bv_val, mdb_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ LDAP_XSTRING(mdb_attr_dbs) ": %s\n",
+ cr->msg, 0, 0 );
+ return rc;
+ }
+ } else {
+ rc = 0;
+ }
+
+ flags = MDB_DUPSORT|MDB_DUPFIXED|MDB_INTEGERDUP;
+ if ( !(slapMode & SLAP_TOOL_READONLY) )
+ flags |= MDB_CREATE;
+
+ for ( i=0; i<mdb->mi_nattrs; i++ ) {
+ if ( mdb->mi_attrs[i]->ai_dbi ) /* already open */
+ continue;
+ rc = mdb_open( txn, mdb->mi_attrs[i]->ai_desc->ad_type->sat_cname.bv_val,
+ flags, &mdb->mi_attrs[i]->ai_dbi );
+ if ( rc ) {
+ snprintf( cr->msg, sizeof(cr->msg), "database \"%s\": "
+ "mdb_open(%s) failed: %s (%d).",
+ be->be_suffix[0].bv_val,
+ mdb->mi_attrs[i]->ai_desc->ad_type->sat_cname.bv_val,
+ mdb_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ LDAP_XSTRING(mdb_attr_dbs) ": %s\n",
+ cr->msg, 0, 0 );
+ break;
+ }
+ }
+
+ /* Only commit if this is our txn */
+ if ( tx0 == NULL ) {
+ if ( !rc ) {
+ rc = mdb_txn_commit( txn );
+ if ( rc ) {
+ snprintf( cr->msg, sizeof(cr->msg), "database \"%s\": "
+ "txn_commit failed: %s (%d).",
+ be->be_suffix[0].bv_val, mdb_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ LDAP_XSTRING(mdb_attr_dbs) ": %s\n",
+ cr->msg, 0, 0 );
+ }
+ } else {
+ mdb_txn_abort( txn );
+ }
+ }
+
+ return rc;
+}
+
+void
+mdb_attr_dbs_close(
+ struct mdb_info *mdb,
+ MDB_txn *txn
+)
+{
+ int i;
+ for ( i=0; i<mdb->mi_nattrs; i++ )
+ if ( mdb->mi_attrs[i]->ai_dbi )
+ mdb_close( txn, mdb->mi_attrs[i]->ai_dbi );
+}
+
+int
+mdb_attr_index_config(
+ struct mdb_info *mdb,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv,
+ struct config_reply_s *c_reply)
+{
+ int rc = 0;
+ int i;
+ slap_mask_t mask;
+ char **attrs;
+ char **indexes = NULL;
+
+ attrs = ldap_str2charray( argv[0], "," );
+
+ if( attrs == NULL ) {
+ fprintf( stderr, "%s: line %d: "
+ "no attributes specified: %s\n",
+ fname, lineno, argv[0] );
+ return LDAP_PARAM_ERROR;
+ }
+
+ if ( argc > 1 ) {
+ indexes = ldap_str2charray( argv[1], "," );
+
+ if( indexes == NULL ) {
+ fprintf( stderr, "%s: line %d: "
+ "no indexes specified: %s\n",
+ fname, lineno, argv[1] );
+ rc = LDAP_PARAM_ERROR;
+ goto done;
+ }
+ }
+
+ if( indexes == NULL ) {
+ mask = mdb->mi_defaultmask;
+
+ } else {
+ mask = 0;
+
+ for ( i = 0; indexes[i] != NULL; i++ ) {
+ slap_mask_t index;
+ rc = slap_str2index( indexes[i], &index );
+
+ if( rc != LDAP_SUCCESS ) {
+ if ( c_reply )
+ {
+ snprintf(c_reply->msg, sizeof(c_reply->msg),
+ "index type \"%s\" undefined", indexes[i] );
+
+ fprintf( stderr, "%s: line %d: %s\n",
+ fname, lineno, c_reply->msg );
+ }
+ rc = LDAP_PARAM_ERROR;
+ goto done;
+ }
+
+ mask |= index;
+ }
+ }
+
+ if( !mask ) {
+ if ( c_reply )
+ {
+ snprintf(c_reply->msg, sizeof(c_reply->msg),
+ "no indexes selected" );
+ fprintf( stderr, "%s: line %d: %s\n",
+ fname, lineno, c_reply->msg );
+ }
+ rc = LDAP_PARAM_ERROR;
+ goto done;
+ }
+
+ for ( i = 0; attrs[i] != NULL; i++ ) {
+ AttrInfo *a;
+ AttributeDescription *ad;
+ const char *text;
+#ifdef LDAP_COMP_MATCH
+ ComponentReference* cr = NULL;
+ AttrInfo *a_cr = NULL;
+#endif
+
+ if( strcasecmp( attrs[i], "default" ) == 0 ) {
+ mdb->mi_defaultmask |= mask;
+ continue;
+ }
+
+#ifdef LDAP_COMP_MATCH
+ if ( is_component_reference( attrs[i] ) ) {
+ rc = extract_component_reference( attrs[i], &cr );
+ if ( rc != LDAP_SUCCESS ) {
+ if ( c_reply )
+ {
+ snprintf(c_reply->msg, sizeof(c_reply->msg),
+ "index component reference\"%s\" undefined",
+ attrs[i] );
+ fprintf( stderr, "%s: line %d: %s\n",
+ fname, lineno, c_reply->msg );
+ }
+ goto done;
+ }
+ cr->cr_indexmask = mask;
+ /*
+ * After extracting a component reference
+ * only the name of a attribute will be remaining
+ */
+ } else {
+ cr = NULL;
+ }
+#endif
+ ad = NULL;
+ rc = slap_str2ad( attrs[i], &ad, &text );
+
+ if( rc != LDAP_SUCCESS ) {
+ if ( c_reply )
+ {
+ snprintf(c_reply->msg, sizeof(c_reply->msg),
+ "index attribute \"%s\" undefined",
+ attrs[i] );
+
+ fprintf( stderr, "%s: line %d: %s\n",
+ fname, lineno, c_reply->msg );
+ }
+ goto done;
+ }
+
+ if( ad == slap_schema.si_ad_entryDN || slap_ad_is_binary( ad ) ) {
+ if (c_reply) {
+ snprintf(c_reply->msg, sizeof(c_reply->msg),
+ "index of attribute \"%s\" disallowed", attrs[i] );
+ fprintf( stderr, "%s: line %d: %s\n",
+ fname, lineno, c_reply->msg );
+ }
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ if( IS_SLAP_INDEX( mask, SLAP_INDEX_APPROX ) && !(
+ ad->ad_type->sat_approx
+ && ad->ad_type->sat_approx->smr_indexer
+ && ad->ad_type->sat_approx->smr_filter ) )
+ {
+ if (c_reply) {
+ snprintf(c_reply->msg, sizeof(c_reply->msg),
+ "approx index of attribute \"%s\" disallowed", attrs[i] );
+ fprintf( stderr, "%s: line %d: %s\n",
+ fname, lineno, c_reply->msg );
+ }
+ rc = LDAP_INAPPROPRIATE_MATCHING;
+ goto done;
+ }
+
+ if( IS_SLAP_INDEX( mask, SLAP_INDEX_EQUALITY ) && !(
+ ad->ad_type->sat_equality
+ && ad->ad_type->sat_equality->smr_indexer
+ && ad->ad_type->sat_equality->smr_filter ) )
+ {
+ if (c_reply) {
+ snprintf(c_reply->msg, sizeof(c_reply->msg),
+ "equality index of attribute \"%s\" disallowed", attrs[i] );
+ fprintf( stderr, "%s: line %d: %s\n",
+ fname, lineno, c_reply->msg );
+ }
+ rc = LDAP_INAPPROPRIATE_MATCHING;
+ goto done;
+ }
+
+ if( IS_SLAP_INDEX( mask, SLAP_INDEX_SUBSTR ) && !(
+ ad->ad_type->sat_substr
+ && ad->ad_type->sat_substr->smr_indexer
+ && ad->ad_type->sat_substr->smr_filter ) )
+ {
+ if (c_reply) {
+ snprintf(c_reply->msg, sizeof(c_reply->msg),
+ "substr index of attribute \"%s\" disallowed", attrs[i] );
+ fprintf( stderr, "%s: line %d: %s\n",
+ fname, lineno, c_reply->msg );
+ }
+ rc = LDAP_INAPPROPRIATE_MATCHING;
+ goto done;
+ }
+
+ Debug( LDAP_DEBUG_CONFIG, "index %s 0x%04lx\n",
+ ad->ad_cname.bv_val, mask, 0 );
+
+ a = (AttrInfo *) ch_malloc( sizeof(AttrInfo) );
+
+#ifdef LDAP_COMP_MATCH
+ a->ai_cr = NULL;
+#endif
+ a->ai_desc = ad;
+ a->ai_dbi = 0;
+
+ if ( mdb->mi_flags & MDB_IS_OPEN ) {
+ a->ai_indexmask = 0;
+ a->ai_newmask = mask;
+ } else {
+ a->ai_indexmask = mask;
+ a->ai_newmask = 0;
+ }
+
+#ifdef LDAP_COMP_MATCH
+ if ( cr ) {
+ a_cr = mdb_attr_mask( mdb, ad );
+ if ( a_cr ) {
+ /*
+ * AttrInfo is already in AVL
+ * just add the extracted component reference
+ * in the AttrInfo
+ */
+ rc = insert_component_reference( cr, &a_cr->ai_cr );
+ if ( rc != LDAP_SUCCESS) {
+ fprintf( stderr, " error during inserting component reference in %s ", attrs[i]);
+ rc = LDAP_PARAM_ERROR;
+ goto done;
+ }
+ continue;
+ } else {
+ rc = insert_component_reference( cr, &a->ai_cr );
+ if ( rc != LDAP_SUCCESS) {
+ fprintf( stderr, " error during inserting component reference in %s ", attrs[i]);
+ rc = LDAP_PARAM_ERROR;
+ goto done;
+ }
+ }
+ }
+#endif
+ rc = ainfo_insert( mdb, a );
+ if( rc ) {
+ if ( mdb->mi_flags & MDB_IS_OPEN ) {
+ AttrInfo *b = mdb_attr_mask( mdb, ad );
+ /* If there is already an index defined for this attribute
+ * it must be replaced. Otherwise we end up with multiple
+ * olcIndex values for the same attribute */
+ if ( b->ai_indexmask & MDB_INDEX_DELETING ) {
+ /* If we were editing this attr, reset it */
+ b->ai_indexmask &= ~MDB_INDEX_DELETING;
+ /* If this is leftover from a previous add, commit it */
+ if ( b->ai_newmask )
+ b->ai_indexmask = b->ai_newmask;
+ b->ai_newmask = a->ai_newmask;
+ ch_free( a );
+ rc = 0;
+ continue;
+ }
+ }
+ if (c_reply) {
+ snprintf(c_reply->msg, sizeof(c_reply->msg),
+ "duplicate index definition for attr \"%s\"",
+ attrs[i] );
+ fprintf( stderr, "%s: line %d: %s\n",
+ fname, lineno, c_reply->msg );
+ }
+
+ rc = LDAP_PARAM_ERROR;
+ goto done;
+ }
+ }
+
+done:
+ ldap_charray_free( attrs );
+ if ( indexes != NULL ) ldap_charray_free( indexes );
+
+ return rc;
+}
+
+static int
+mdb_attr_index_unparser( void *v1, void *v2 )
+{
+ AttrInfo *ai = v1;
+ BerVarray *bva = v2;
+ struct berval bv;
+ char *ptr;
+
+ slap_index2bvlen( ai->ai_indexmask, &bv );
+ if ( bv.bv_len ) {
+ bv.bv_len += ai->ai_desc->ad_cname.bv_len + 1;
+ ptr = ch_malloc( bv.bv_len+1 );
+ bv.bv_val = lutil_strcopy( ptr, ai->ai_desc->ad_cname.bv_val );
+ *bv.bv_val++ = ' ';
+ slap_index2bv( ai->ai_indexmask, &bv );
+ bv.bv_val = ptr;
+ ber_bvarray_add( bva, &bv );
+ }
+ return 0;
+}
+
+static AttributeDescription addef = { NULL, NULL, BER_BVC("default") };
+static AttrInfo aidef = { &addef };
+
+void
+mdb_attr_index_unparse( struct mdb_info *mdb, BerVarray *bva )
+{
+ int i;
+
+ if ( mdb->mi_defaultmask ) {
+ aidef.ai_indexmask = mdb->mi_defaultmask;
+ mdb_attr_index_unparser( &aidef, bva );
+ }
+ for ( i=0; i<mdb->mi_nattrs; i++ )
+ mdb_attr_index_unparser( mdb->mi_attrs[i], bva );
+}
+
+void
+mdb_attr_info_free( AttrInfo *ai )
+{
+#ifdef LDAP_COMP_MATCH
+ free( ai->ai_cr );
+#endif
+ free( ai );
+}
+
+void
+mdb_attr_index_destroy( struct mdb_info *mdb )
+{
+ int i;
+
+ for ( i=0; i<mdb->mi_nattrs; i++ )
+ mdb_attr_info_free( mdb->mi_attrs[i] );
+
+ free( mdb->mi_attrs );
+}
+
+void mdb_attr_index_free( struct mdb_info *mdb, AttributeDescription *ad )
+{
+ int i;
+
+ i = mdb_attr_slot( mdb, ad, NULL );
+ if ( i >= 0 ) {
+ mdb_attr_info_free( mdb->mi_attrs[i] );
+ mdb->mi_nattrs--;
+ for (; i<mdb->mi_nattrs; i++)
+ mdb->mi_attrs[i] = mdb->mi_attrs[i+1];
+ }
+}
+
+void mdb_attr_flush( struct mdb_info *mdb )
+{
+ int i;
+
+ for ( i=0; i<mdb->mi_nattrs; i++ ) {
+ if ( mdb->mi_attrs[i]->ai_indexmask & MDB_INDEX_DELETING ) {
+ int j;
+ mdb_attr_info_free( mdb->mi_attrs[i] );
+ mdb->mi_nattrs--;
+ for (j=i; j<mdb->mi_nattrs; j++)
+ mdb->mi_attrs[j] = mdb->mi_attrs[j+1];
+ i--;
+ }
+ }
+}
--- /dev/null
+/* back-mdb.h - mdb back-end header file */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2011 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef _BACK_MDB_H_
+#define _BACK_MDB_H_
+
+#include <portable.h>
+#include "slap.h"
+#include "mdb.h"
+
+LDAP_BEGIN_DECL
+
+#define DN_BASE_PREFIX SLAP_INDEX_EQUALITY_PREFIX
+#define DN_ONE_PREFIX '%'
+#define DN_SUBTREE_PREFIX '@'
+
+#define MDB_AD2ID 0
+#define MDB_DN2ID 1
+#define MDB_ID2ENTRY 2
+#define MDB_NDB 3
+
+/* The default search IDL stack cache depth */
+#define DEFAULT_SEARCH_STACK_DEPTH 16
+
+/* The minimum we can function with */
+#define MINIMUM_SEARCH_STACK_DEPTH 8
+
+#define MDB_INDICES 128
+
+/* Default to 10MB max */
+#define DEFAULT_MAPSIZE (10*1048576)
+
+#ifdef LDAP_DEVEL
+#define MDB_MONITOR_IDX
+#endif /* LDAP_DEVEL */
+
+typedef struct mdb_monitor_t {
+ void *mdm_cb;
+ struct berval mdm_ndn;
+} mdb_monitor_t;
+
+/* From ldap_rq.h */
+struct re_s;
+
+struct mdb_info {
+ MDB_env *mi_dbenv;
+
+ /* DB_ENV parameters */
+ /* The DB_ENV can be tuned via DB_CONFIG */
+ char *mi_dbenv_home;
+ u_int32_t mi_dbenv_flags;
+ int mi_dbenv_mode;
+
+ size_t mi_mapsize;
+
+ slap_mask_t mi_defaultmask;
+ int mi_nattrs;
+ struct mdb_attrinfo **mi_attrs;
+ void *mi_search_stack;
+ int mi_search_stack_depth;
+ int mi_readers;
+
+ int mi_txn_cp;
+ u_int32_t mi_txn_cp_min;
+ u_int32_t mi_txn_cp_kbyte;
+ struct re_s *mi_txn_cp_task;
+ struct re_s *mi_index_task;
+
+ mdb_monitor_t mi_monitor;
+
+#ifdef MDB_MONITOR_IDX
+ ldap_pvt_thread_mutex_t mi_idx_mutex;
+ Avlnode *mi_idx;
+#endif /* MDB_MONITOR_IDX */
+
+ int mi_flags;
+#define MDB_IS_OPEN 0x01
+#define MDB_OPEN_INDEX 0x02
+#define MDB_DEL_INDEX 0x08
+#define MDB_RE_OPEN 0x10
+
+ MDB_dbi mi_dbis[MDB_NDB];
+};
+
+#define mi_id2entry mi_dbis[MDB_ID2ENTRY]
+#define mi_dn2id mi_dbis[MDB_DN2ID]
+#define mi_ad2id mi_dbis[MDB_AD2ID]
+
+typedef struct mdb_op_info {
+ OpExtra moi_oe;
+ MDB_txn* moi_txn;
+ int moi_ref;
+ char moi_flag;
+} mdb_op_info;
+#define MOI_READER 0x01
+#define MOI_FREEIT 0x02
+
+/* Copy an ID "src" to pointer "dst" in big-endian byte order */
+#define MDB_ID2DISK( src, dst ) \
+ do { int i0; ID tmp; unsigned char *_p; \
+ tmp = (src); _p = (unsigned char *)(dst); \
+ for ( i0=sizeof(ID)-1; i0>=0; i0-- ) { \
+ _p[i0] = tmp & 0xff; tmp >>= 8; \
+ } \
+ } while(0)
+
+/* Copy a pointer "src" to a pointer "dst" from big-endian to native order */
+#define MDB_DISK2ID( src, dst ) \
+ do { unsigned i0; ID tmp = 0; unsigned char *_p; \
+ _p = (unsigned char *)(src); \
+ for ( i0=0; i0<sizeof(ID); i0++ ) { \
+ tmp <<= 8; tmp |= *_p++; \
+ } *(dst) = tmp; \
+ } while (0)
+
+LDAP_END_DECL
+
+/* for the cache of attribute information (which are indexed, etc.) */
+typedef struct mdb_attrinfo {
+ AttributeDescription *ai_desc; /* attribute description cn;lang-en */
+ slap_mask_t ai_indexmask; /* how the attr is indexed */
+ slap_mask_t ai_newmask; /* new settings to replace old mask */
+#ifdef LDAP_COMP_MATCH
+ ComponentReference* ai_cr; /*component indexing*/
+#endif
+ int ai_idx; /* position in AI array */
+ MDB_dbi ai_dbi;
+} AttrInfo;
+
+/* These flags must not clash with SLAP_INDEX flags or ops in slap.h! */
+#define MDB_INDEX_DELETING 0x8000U /* index is being modified */
+#define MDB_INDEX_UPDATE_OP 0x03 /* performing an index update */
+
+/* For slapindex to record which attrs in an entry belong to which
+ * index database
+ */
+typedef struct AttrList {
+ struct AttrList *next;
+ Attribute *attr;
+} AttrList;
+
+typedef struct IndexRec {
+ AttrInfo *ai;
+ AttrList *attrs;
+} IndexRec;
+
+#include "proto-mdb.h"
+
+#endif /* _BACK_MDB_H_ */
--- /dev/null
+/* bind.c - mdb backend bind routine */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2011 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+
+#include "back-mdb.h"
+
+int
+mdb_bind( Operation *op, SlapReply *rs )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ Entry *e;
+ Attribute *a;
+
+ AttributeDescription *password = slap_schema.si_ad_userPassword;
+
+ MDB_txn *rtxn;
+ mdb_op_info opinfo = {0}, *moi = &opinfo;
+
+ Debug( LDAP_DEBUG_ARGS,
+ "==> " LDAP_XSTRING(mdb_bind) ": dn: %s\n",
+ op->o_req_dn.bv_val, 0, 0);
+
+ /* allow noauth binds */
+ switch ( be_rootdn_bind( op, NULL ) ) {
+ case LDAP_SUCCESS:
+ /* frontend will send result */
+ return rs->sr_err = LDAP_SUCCESS;
+
+ default:
+ /* give the database a chance */
+ /* NOTE: this behavior departs from that of other backends,
+ * since the others, in case of password checking failure
+ * do not give the database a chance. If an entry with
+ * rootdn's name does not exist in the database the result
+ * will be the same. See ITS#4962 for discussion. */
+ break;
+ }
+
+ rs->sr_err = mdb_opinfo_get(op, mdb, 1, &moi);
+ switch(rs->sr_err) {
+ case 0:
+ break;
+ default:
+ rs->sr_text = "internal error";
+ send_ldap_result( op, rs );
+ return rs->sr_err;
+ }
+
+ rtxn = moi->moi_txn;
+
+ /* get entry with reader lock */
+ rs->sr_err = mdb_dn2entry( op, rtxn, &op->o_req_ndn, &e, 0 );
+
+ switch(rs->sr_err) {
+ case MDB_NOTFOUND:
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ goto done;
+ case 0:
+ break;
+ case LDAP_BUSY:
+ rs->sr_text = "ldap_server_busy";
+ goto done;
+ default:
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto done;
+ }
+
+ ber_dupbv( &op->oq_bind.rb_edn, &e->e_name );
+
+ /* check for deleted */
+ if ( is_entry_subentry( e ) ) {
+ /* entry is an subentry, don't allow bind */
+ Debug( LDAP_DEBUG_TRACE, "entry is subentry\n", 0,
+ 0, 0 );
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ goto done;
+ }
+
+ if ( is_entry_alias( e ) ) {
+ /* entry is an alias, don't allow bind */
+ Debug( LDAP_DEBUG_TRACE, "entry is alias\n", 0, 0, 0 );
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ goto done;
+ }
+
+ if ( is_entry_referral( e ) ) {
+ Debug( LDAP_DEBUG_TRACE, "entry is referral\n", 0,
+ 0, 0 );
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ goto done;
+ }
+
+ switch ( op->oq_bind.rb_method ) {
+ case LDAP_AUTH_SIMPLE:
+ a = attr_find( e->e_attrs, password );
+ if ( a == NULL ) {
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ goto done;
+ }
+
+ if ( slap_passwd_check( op, e, a, &op->oq_bind.rb_cred,
+ &rs->sr_text ) != 0 )
+ {
+ /* failure; stop front end from sending result */
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ goto done;
+ }
+
+ rs->sr_err = 0;
+ break;
+
+ default:
+ assert( 0 ); /* should not be reachable */
+ rs->sr_err = LDAP_STRONG_AUTH_NOT_SUPPORTED;
+ rs->sr_text = "authentication method not supported";
+ }
+
+done:
+ if ( moi == &opinfo ) {
+ mdb_txn_reset( moi->moi_txn );
+ LDAP_SLIST_REMOVE( &op->o_extra, &moi->moi_oe, OpExtra, oe_next );
+ }
+ /* free entry and reader lock */
+ if( e != NULL ) {
+ mdb_entry_return( e );
+ }
+
+ if ( rs->sr_err ) {
+ send_ldap_result( op, rs );
+ if ( rs->sr_ref ) {
+ ber_bvarray_free( rs->sr_ref );
+ rs->sr_ref = NULL;
+ }
+ }
+ /* front end will send result on success (rs->sr_err==0) */
+ return rs->sr_err;
+}
--- /dev/null
+/* compare.c - mdb backend compare routine */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2011 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-mdb.h"
+
+int
+mdb_compare( Operation *op, SlapReply *rs )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ Entry *e = NULL;
+ int manageDSAit = get_manageDSAit( op );
+
+ MDB_txn *rtxn;
+ mdb_op_info opinfo = {0}, *moi = &opinfo;
+
+ rs->sr_err = mdb_opinfo_get(op, mdb, 1, &moi);
+ switch(rs->sr_err) {
+ case 0:
+ break;
+ default:
+ send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
+ return rs->sr_err;
+ }
+
+ rtxn = moi->moi_txn;
+
+ /* get entry */
+ rs->sr_err = mdb_dn2entry( op, rtxn, &op->o_req_ndn, &e, 1 );
+ switch( rs->sr_err ) {
+ case MDB_NOTFOUND:
+ case 0:
+ break;
+ case LDAP_BUSY:
+ rs->sr_text = "ldap server busy";
+ goto return_results;
+ default:
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ if ( rs->sr_err == MDB_NOTFOUND ) {
+ if ( e != NULL ) {
+ /* return referral only if "disclose" is granted on the object */
+ if ( ! access_allowed( op, e, slap_schema.si_ad_entry,
+ NULL, ACL_DISCLOSE, NULL ) )
+ {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+
+ } else {
+ rs->sr_matched = ch_strdup( e->e_dn );
+ if ( is_entry_referral( e )) {
+ BerVarray ref = get_entry_referrals( op, e );
+ rs->sr_ref = referral_rewrite( ref, &e->e_name,
+ &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+ ber_bvarray_free( ref );
+ } else {
+ rs->sr_ref = NULL;
+ }
+ rs->sr_err = LDAP_REFERRAL;
+ }
+ mdb_entry_return( e );
+ e = NULL;
+
+ } else {
+ rs->sr_ref = referral_rewrite( default_referral,
+ NULL, &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+ rs->sr_err = rs->sr_ref ? LDAP_REFERRAL : LDAP_NO_SUCH_OBJECT;
+ }
+
+ rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
+ send_ldap_result( op, rs );
+ goto done;
+ }
+
+ if (!manageDSAit && is_entry_referral( e ) ) {
+ /* return referral only if "disclose" is granted on the object */
+ if ( !access_allowed( op, e, slap_schema.si_ad_entry,
+ NULL, ACL_DISCLOSE, NULL ) )
+ {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ } else {
+ /* entry is a referral, don't allow compare */
+ rs->sr_ref = get_entry_referrals( op, e );
+ rs->sr_err = LDAP_REFERRAL;
+ rs->sr_matched = e->e_name.bv_val;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "entry is referral\n", 0, 0, 0 );
+
+ send_ldap_result( op, rs );
+
+ ber_bvarray_free( rs->sr_ref );
+ rs->sr_ref = NULL;
+ rs->sr_matched = NULL;
+ goto done;
+ }
+
+ rs->sr_err = slap_compare_entry( op, e, op->orc_ava );
+
+return_results:
+ send_ldap_result( op, rs );
+
+ switch ( rs->sr_err ) {
+ case LDAP_COMPARE_FALSE:
+ case LDAP_COMPARE_TRUE:
+ rs->sr_err = LDAP_SUCCESS;
+ break;
+ }
+
+done:
+ if ( moi == &opinfo ) {
+ mdb_txn_reset( moi->moi_txn );
+ LDAP_SLIST_REMOVE( &op->o_extra, &moi->moi_oe, OpExtra, oe_next );
+ }
+ /* free entry */
+ if ( e != NULL ) {
+ mdb_entry_return( e );
+ }
+
+ return rs->sr_err;
+}
--- /dev/null
+/* config.c - mdb backend configuration file routine */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2011 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/ctype.h>
+#include <ac/string.h>
+#include <ac/errno.h>
+
+#include "back-mdb.h"
+
+#include "config.h"
+
+#include "lutil.h"
+#include "ldap_rq.h"
+
+static ConfigDriver mdb_cf_gen;
+
+enum {
+ MDB_CHKPT = 1,
+ MDB_DIRECTORY,
+ MDB_DBNOSYNC,
+ MDB_INDEX,
+ MDB_MAXREADERS,
+ MDB_MAXSIZE,
+ MDB_MODE,
+ MDB_SSTACK,
+};
+
+static ConfigTable mdbcfg[] = {
+ { "directory", "dir", 2, 2, 0, ARG_STRING|ARG_MAGIC|MDB_DIRECTORY,
+ mdb_cf_gen, "( OLcfgDbAt:0.1 NAME 'olcDbDirectory' "
+ "DESC 'Directory for database content' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "checkpoint", "kbyte> <min", 3, 3, 0, ARG_MAGIC|MDB_CHKPT,
+ mdb_cf_gen, "( OLcfgDbAt:1.2 NAME 'olcDbCheckpoint' "
+ "DESC 'Database checkpoint interval in kbytes and minutes' "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )",NULL, NULL },
+ { "dbnosync", NULL, 1, 2, 0, ARG_ON_OFF|ARG_MAGIC|MDB_DBNOSYNC,
+ mdb_cf_gen, "( OLcfgDbAt:1.4 NAME 'olcDbNoSync' "
+ "DESC 'Disable synchronous database writes' "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "index", "attr> <[pres,eq,approx,sub]", 2, 3, 0, ARG_MAGIC|MDB_INDEX,
+ mdb_cf_gen, "( OLcfgDbAt:0.2 NAME 'olcDbIndex' "
+ "DESC 'Attribute index parameters' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "maxreaders", "num", 2, 2, 0, ARG_INT|ARG_MAGIC|MDB_MAXREADERS,
+ mdb_cf_gen, "( OLcfgDbAt:12.1 NAME 'olcDbMaxReaders' "
+ "DESC 'Maximum number of threads that may access the DB concurrently' "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+ { "maxsize", "size", 2, 2, 0, ARG_ULONG|ARG_MAGIC|MDB_MAXSIZE,
+ mdb_cf_gen, "( OLcfgDbAt:12.2 NAME 'olcDbMaxSize' "
+ "DESC 'Maximum size of DB in bytes' "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+ { "mode", "mode", 2, 2, 0, ARG_MAGIC|MDB_MODE,
+ mdb_cf_gen, "( OLcfgDbAt:0.3 NAME 'olcDbMode' "
+ "DESC 'Unix permissions of database files' "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "searchstack", "depth", 2, 2, 0, ARG_INT|ARG_MAGIC|MDB_SSTACK,
+ mdb_cf_gen, "( OLcfgDbAt:1.9 NAME 'olcDbSearchStack' "
+ "DESC 'Depth of search stack in IDLs' "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED,
+ NULL, NULL, NULL, NULL }
+};
+
+static ConfigOCs mdbocs[] = {
+ {
+ "( OLcfgDbOc:12.1 "
+ "NAME 'olcMdbConfig' "
+ "DESC 'MDB backend configuration' "
+ "SUP olcDatabaseConfig "
+ "MUST olcDbDirectory "
+ "MAY ( olcDbCheckpoint $ "
+ "olcDbNoSync $ olcDbIndex $ olcDbMaxReaders $ olcDbMaxsize $ "
+ "olcDbMode $ olcDbSearchStack ) )",
+ Cft_Database, mdbcfg },
+ { NULL, 0, NULL }
+};
+
+/* perform periodic syncs */
+static void *
+mdb_checkpoint( void *ctx, void *arg )
+{
+ struct re_s *rtask = arg;
+ struct mdb_info *mdb = rtask->arg;
+
+ mdb_env_sync( mdb->mi_dbenv, 0 );
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+ return NULL;
+}
+
+/* reindex entries on the fly */
+static void *
+mdb_online_index( void *ctx, void *arg )
+{
+ struct re_s *rtask = arg;
+ BackendDB *be = rtask->arg;
+ struct mdb_info *mdb = be->be_private;
+
+ Connection conn = {0};
+ OperationBuffer opbuf;
+ Operation *op;
+
+ MDB_cursor *curs;
+ MDB_val key, data;
+ MDB_txn *txn;
+ ID id;
+ Entry *e;
+ int rc, getnext = 1;
+ int i;
+
+ connection_fake_init( &conn, &opbuf, ctx );
+ op = &opbuf.ob_op;
+
+ op->o_bd = be;
+
+ id = 1;
+ key.mv_data = &id;
+ key.mv_size = sizeof(ID);
+
+ while ( 1 ) {
+ if ( slapd_shutdown )
+ break;
+
+ rc = mdb_txn_begin( mdb->mi_dbenv, 0, &txn );
+ if ( rc )
+ break;
+ if ( getnext ) {
+ getnext = 0;
+ rc = mdb_cursor_open( txn, mdb->mi_id2entry, &curs );
+ if ( rc ) {
+ mdb_txn_abort( txn );
+ break;
+ }
+ rc = mdb_cursor_get( curs, &key, &data, MDB_SET_RANGE );
+ memcpy( &id, key.mv_data, sizeof( id ));
+ mdb_cursor_close( curs );
+ if ( rc ) {
+ mdb_txn_abort( txn );
+ if ( rc == MDB_NOTFOUND )
+ rc = 0;
+ break;
+ }
+ }
+
+ rc = mdb_id2entry( op, txn, id, &e );
+ if ( rc ) {
+ mdb_txn_abort( txn );
+ if ( rc == MDB_NOTFOUND ) {
+ id++;
+ getnext = 1;
+ continue;
+ }
+ break;
+ }
+ rc = mdb_index_entry( op, txn, MDB_INDEX_UPDATE_OP, e );
+ if ( rc == 0 ) {
+ rc = mdb_txn_commit( txn );
+ txn = NULL;
+ }
+ if ( rc )
+ break;
+ id++;
+ getnext = 1;
+ }
+
+ for ( i = 0; i < mdb->mi_nattrs; i++ ) {
+ if ( mdb->mi_attrs[ i ]->ai_indexmask & MDB_INDEX_DELETING
+ || mdb->mi_attrs[ i ]->ai_newmask == 0 )
+ {
+ continue;
+ }
+ mdb->mi_attrs[ i ]->ai_indexmask = mdb->mi_attrs[ i ]->ai_newmask;
+ mdb->mi_attrs[ i ]->ai_newmask = 0;
+ }
+
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
+ mdb->mi_index_task = NULL;
+ ldap_pvt_runqueue_remove( &slapd_rq, rtask );
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+
+ return NULL;
+}
+
+/* Cleanup loose ends after Modify completes */
+static int
+mdb_cf_cleanup( ConfigArgs *c )
+{
+ struct mdb_info *mdb = c->be->be_private;
+ int rc = 0;
+
+ if ( mdb->mi_flags & MDB_DEL_INDEX ) {
+ mdb_attr_flush( mdb );
+ mdb->mi_flags ^= MDB_DEL_INDEX;
+ }
+
+ if ( mdb->mi_flags & MDB_RE_OPEN ) {
+ mdb->mi_flags ^= MDB_RE_OPEN;
+ rc = c->be->bd_info->bi_db_close( c->be, &c->reply );
+ if ( rc == 0 )
+ rc = c->be->bd_info->bi_db_open( c->be, &c->reply );
+ /* If this fails, we need to restart */
+ if ( rc ) {
+ slapd_shutdown = 2;
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "failed to reopen database, rc=%d", rc );
+ Debug( LDAP_DEBUG_ANY, LDAP_XSTRING(mdb_cf_cleanup)
+ ": %s\n", c->cr_msg, 0, 0 );
+ rc = LDAP_OTHER;
+ }
+ }
+
+ if ( mdb->mi_flags & MDB_OPEN_INDEX ) {
+ rc = mdb_attr_dbs_open( c->be, NULL, &c->reply );
+ if ( rc )
+ rc = LDAP_OTHER;
+ }
+ return rc;
+}
+
+static int
+mdb_cf_gen( ConfigArgs *c )
+{
+ struct mdb_info *mdb = c->be->be_private;
+ int rc;
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ rc = 0;
+ switch( c->type ) {
+ case MDB_MODE: {
+ char buf[64];
+ struct berval bv;
+ bv.bv_len = snprintf( buf, sizeof(buf), "0%o", mdb->mi_dbenv_mode );
+ if ( bv.bv_len > 0 && bv.bv_len < sizeof(buf) ) {
+ bv.bv_val = buf;
+ value_add_one( &c->rvalue_vals, &bv );
+ } else {
+ rc = 1;
+ }
+ } break;
+
+ case MDB_CHKPT:
+ if ( mdb->mi_txn_cp ) {
+ char buf[64];
+ struct berval bv;
+ bv.bv_len = snprintf( buf, sizeof(buf), "%ld %ld",
+ (long) mdb->mi_txn_cp_kbyte, (long) mdb->mi_txn_cp_min );
+ if ( bv.bv_len > 0 && bv.bv_len < sizeof(buf) ) {
+ bv.bv_val = buf;
+ value_add_one( &c->rvalue_vals, &bv );
+ } else {
+ rc = 1;
+ }
+ } else {
+ rc = 1;
+ }
+ break;
+
+ case MDB_DIRECTORY:
+ if ( mdb->mi_dbenv_home ) {
+ c->value_string = ch_strdup( mdb->mi_dbenv_home );
+ } else {
+ rc = 1;
+ }
+ break;
+
+ case MDB_DBNOSYNC:
+ if ( mdb->mi_dbenv_flags & MDB_NOSYNC )
+ c->value_int = 1;
+ break;
+
+ case MDB_INDEX:
+ mdb_attr_index_unparse( mdb, &c->rvalue_vals );
+ if ( !c->rvalue_vals ) rc = 1;
+ break;
+
+ case MDB_SSTACK:
+ c->value_int = mdb->mi_search_stack_depth;
+ break;
+
+ case MDB_MAXREADERS:
+ c->value_int = mdb->mi_readers;
+ break;
+
+ case MDB_MAXSIZE:
+ c->value_ulong = mdb->mi_mapsize;
+ break;
+ }
+ return rc;
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ rc = 0;
+ switch( c->type ) {
+ case MDB_MODE:
+#if 0
+ /* FIXME: does it make any sense to change the mode,
+ * if we don't exec a chmod()? */
+ mdb->bi_dbenv_mode = SLAPD_DEFAULT_DB_MODE;
+ break;
+#endif
+
+ /* single-valued no-ops */
+ case MDB_SSTACK:
+ case MDB_MAXREADERS:
+ case MDB_MAXSIZE:
+ break;
+
+ case MDB_CHKPT:
+ if ( mdb->mi_txn_cp_task ) {
+ struct re_s *re = mdb->mi_txn_cp_task;
+ mdb->mi_txn_cp_task = NULL;
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ if ( ldap_pvt_runqueue_isrunning( &slapd_rq, re ) )
+ ldap_pvt_runqueue_stoptask( &slapd_rq, re );
+ ldap_pvt_runqueue_remove( &slapd_rq, re );
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+ }
+ mdb->mi_txn_cp = 0;
+ break;
+ case MDB_DIRECTORY:
+ mdb->mi_flags |= MDB_RE_OPEN;
+ ch_free( mdb->mi_dbenv_home );
+ mdb->mi_dbenv_home = NULL;
+ c->cleanup = mdb_cf_cleanup;
+ ldap_pvt_thread_pool_purgekey( mdb->mi_dbenv );
+ break;
+ case MDB_DBNOSYNC:
+ mdb_env_set_flags( mdb->mi_dbenv, MDB_NOSYNC, 0 );
+ break;
+ case MDB_INDEX:
+ if ( c->valx == -1 ) {
+ int i;
+
+ /* delete all (FIXME) */
+ for ( i = 0; i < mdb->mi_nattrs; i++ ) {
+ mdb->mi_attrs[i]->ai_indexmask |= MDB_INDEX_DELETING;
+ }
+ mdb->mi_flags |= MDB_DEL_INDEX;
+ c->cleanup = mdb_cf_cleanup;
+
+ } else {
+ struct berval bv, def = BER_BVC("default");
+ char *ptr;
+
+ for (ptr = c->line; !isspace( (unsigned char) *ptr ); ptr++);
+
+ bv.bv_val = c->line;
+ bv.bv_len = ptr - bv.bv_val;
+ if ( bvmatch( &bv, &def )) {
+ mdb->mi_defaultmask = 0;
+
+ } else {
+ int i;
+ char **attrs;
+ char sep;
+
+ sep = bv.bv_val[ bv.bv_len ];
+ bv.bv_val[ bv.bv_len ] = '\0';
+ attrs = ldap_str2charray( bv.bv_val, "," );
+
+ for ( i = 0; attrs[ i ]; i++ ) {
+ AttributeDescription *ad = NULL;
+ const char *text;
+ AttrInfo *ai;
+
+ slap_str2ad( attrs[ i ], &ad, &text );
+ /* if we got here... */
+ assert( ad != NULL );
+
+ ai = mdb_attr_mask( mdb, ad );
+ /* if we got here... */
+ assert( ai != NULL );
+
+ ai->ai_indexmask |= MDB_INDEX_DELETING;
+ mdb->mi_flags |= MDB_DEL_INDEX;
+ c->cleanup = mdb_cf_cleanup;
+ }
+
+ bv.bv_val[ bv.bv_len ] = sep;
+ ldap_charray_free( attrs );
+ }
+ }
+ break;
+ }
+ return rc;
+ }
+
+ switch( c->type ) {
+ case MDB_MODE:
+ if ( ASCII_DIGIT( c->argv[1][0] ) ) {
+ long mode;
+ char *next;
+ errno = 0;
+ mode = strtol( c->argv[1], &next, 0 );
+ if ( errno != 0 || next == c->argv[1] || next[0] != '\0' ) {
+ fprintf( stderr, "%s: "
+ "unable to parse mode=\"%s\".\n",
+ c->log, c->argv[1] );
+ return 1;
+ }
+ mdb->mi_dbenv_mode = mode;
+
+ } else {
+ char *m = c->argv[1];
+ int who, what, mode = 0;
+
+ if ( strlen( m ) != STRLENOF("-rwxrwxrwx") ) {
+ return 1;
+ }
+
+ if ( m[0] != '-' ) {
+ return 1;
+ }
+
+ m++;
+ for ( who = 0; who < 3; who++ ) {
+ for ( what = 0; what < 3; what++, m++ ) {
+ if ( m[0] == '-' ) {
+ continue;
+ } else if ( m[0] != "rwx"[what] ) {
+ return 1;
+ }
+ mode += ((1 << (2 - what)) << 3*(2 - who));
+ }
+ }
+ mdb->mi_dbenv_mode = mode;
+ }
+ break;
+ case MDB_CHKPT: {
+ long l;
+ mdb->mi_txn_cp = 1;
+ if ( lutil_atolx( &l, c->argv[1], 0 ) != 0 ) {
+ fprintf( stderr, "%s: "
+ "invalid kbyte \"%s\" in \"checkpoint\".\n",
+ c->log, c->argv[1] );
+ return 1;
+ }
+ mdb->mi_txn_cp_kbyte = l;
+ if ( lutil_atolx( &l, c->argv[2], 0 ) != 0 ) {
+ fprintf( stderr, "%s: "
+ "invalid minutes \"%s\" in \"checkpoint\".\n",
+ c->log, c->argv[2] );
+ return 1;
+ }
+ mdb->mi_txn_cp_min = l;
+ /* If we're in server mode and time-based checkpointing is enabled,
+ * submit a task to perform periodic checkpoints.
+ */
+ if ((slapMode & SLAP_SERVER_MODE) && mdb->mi_txn_cp_min ) {
+ struct re_s *re = mdb->mi_txn_cp_task;
+ if ( re ) {
+ re->interval.tv_sec = mdb->mi_txn_cp_min * 60;
+ } else {
+ if ( c->be->be_suffix == NULL || BER_BVISNULL( &c->be->be_suffix[0] ) ) {
+ fprintf( stderr, "%s: "
+ "\"checkpoint\" must occur after \"suffix\".\n",
+ c->log );
+ return 1;
+ }
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ mdb->mi_txn_cp_task = ldap_pvt_runqueue_insert( &slapd_rq,
+ mdb->mi_txn_cp_min * 60, mdb_checkpoint, mdb,
+ LDAP_XSTRING(mdb_checkpoint), c->be->be_suffix[0].bv_val );
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+ }
+ }
+ } break;
+
+ case MDB_DIRECTORY: {
+ FILE *f;
+ char *ptr, *testpath;
+ int len;
+
+ len = strlen( c->value_string );
+ testpath = ch_malloc( len + STRLENOF(LDAP_DIRSEP) + STRLENOF("DUMMY") + 1 );
+ ptr = lutil_strcopy( testpath, c->value_string );
+ *ptr++ = LDAP_DIRSEP[0];
+ strcpy( ptr, "DUMMY" );
+ f = fopen( testpath, "w" );
+ if ( f ) {
+ fclose( f );
+ unlink( testpath );
+ }
+ ch_free( testpath );
+ if ( !f ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: invalid path: %s",
+ c->log, strerror( errno ));
+ Debug( LDAP_DEBUG_ANY, "%s\n", c->cr_msg, 0, 0 );
+ return -1;
+ }
+
+ if ( mdb->mi_dbenv_home )
+ ch_free( mdb->mi_dbenv_home );
+ mdb->mi_dbenv_home = c->value_string;
+
+ }
+ break;
+
+ case MDB_DBNOSYNC:
+ if ( c->value_int )
+ mdb->mi_dbenv_flags |= MDB_NOSYNC;
+ else
+ mdb->mi_dbenv_flags ^= MDB_NOSYNC;
+ if ( mdb->mi_flags & MDB_IS_OPEN ) {
+ mdb_env_set_flags( mdb->mi_dbenv, MDB_NOSYNC,
+ c->value_int );
+ }
+ break;
+
+ case MDB_INDEX:
+ rc = mdb_attr_index_config( mdb, c->fname, c->lineno,
+ c->argc - 1, &c->argv[1], &c->reply);
+
+ if( rc != LDAP_SUCCESS ) return 1;
+ c->cleanup = mdb_cf_cleanup;
+ mdb->mi_flags |= MDB_OPEN_INDEX;
+ if (( mdb->mi_flags & MDB_IS_OPEN ) && !mdb->mi_index_task ) {
+ /* Start the task as soon as we finish here. Set a long
+ * interval (10 hours) so that it only gets scheduled once.
+ */
+ if ( c->be->be_suffix == NULL || BER_BVISNULL( &c->be->be_suffix[0] ) ) {
+ fprintf( stderr, "%s: "
+ "\"index\" must occur after \"suffix\".\n",
+ c->log );
+ return 1;
+ }
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ mdb->mi_index_task = ldap_pvt_runqueue_insert( &slapd_rq, 36000,
+ mdb_online_index, c->be,
+ LDAP_XSTRING(mdb_online_index), c->be->be_suffix[0].bv_val );
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+ }
+ break;
+
+ case MDB_SSTACK:
+ if ( c->value_int < MINIMUM_SEARCH_STACK_DEPTH ) {
+ fprintf( stderr,
+ "%s: depth %d too small, using %d\n",
+ c->log, c->value_int, MINIMUM_SEARCH_STACK_DEPTH );
+ c->value_int = MINIMUM_SEARCH_STACK_DEPTH;
+ }
+ mdb->mi_search_stack_depth = c->value_int;
+ break;
+
+ case MDB_MAXREADERS:
+ mdb->mi_readers = c->value_int;
+ if ( mdb->mi_flags & MDB_IS_OPEN )
+ mdb->mi_flags |= MDB_RE_OPEN;
+ break;
+
+ case MDB_MAXSIZE:
+ mdb->mi_mapsize = c->value_ulong;
+ if ( mdb->mi_flags & MDB_IS_OPEN )
+ mdb->mi_flags |= MDB_RE_OPEN;
+ break;
+
+ }
+ return 0;
+}
+
+int mdb_back_init_cf( BackendInfo *bi )
+{
+ int rc;
+ bi->bi_cf_ocs = mdbocs;
+
+ rc = config_register_schema( mdbcfg, mdbocs );
+ if ( rc ) return rc;
+ return 0;
+}
--- /dev/null
+/* delete.c - mdb backend delete routine */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2011 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "lutil.h"
+#include "back-mdb.h"
+
+int
+mdb_delete( Operation *op, SlapReply *rs )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ struct berval pdn = {0, NULL};
+ Entry *e = NULL;
+ Entry *p = NULL;
+ int manageDSAit = get_manageDSAit( op );
+ AttributeDescription *children = slap_schema.si_ad_children;
+ AttributeDescription *entry = slap_schema.si_ad_entry;
+ MDB_txn *txn = NULL;
+ mdb_op_info opinfo = {{{ 0 }}}, *moi = &opinfo;
+
+ LDAPControl **preread_ctrl = NULL;
+ LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
+ int num_ctrls = 0;
+
+ int parent_is_glue = 0;
+ int parent_is_leaf = 0;
+
+#ifdef LDAP_X_TXN
+ int settle = 0;
+#endif
+
+ Debug( LDAP_DEBUG_ARGS, "==> " LDAP_XSTRING(mdb_delete) ": %s\n",
+ op->o_req_dn.bv_val, 0, 0 );
+
+#ifdef LDAP_X_TXN
+ if( op->o_txnSpec ) {
+ /* acquire connection lock */
+ ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
+ if( op->o_conn->c_txn == CONN_TXN_INACTIVE ) {
+ rs->sr_text = "invalid transaction identifier";
+ rs->sr_err = LDAP_X_TXN_ID_INVALID;
+ goto txnReturn;
+ } else if( op->o_conn->c_txn == CONN_TXN_SETTLE ) {
+ settle=1;
+ goto txnReturn;
+ }
+
+ if( op->o_conn->c_txn_backend == NULL ) {
+ op->o_conn->c_txn_backend = op->o_bd;
+
+ } else if( op->o_conn->c_txn_backend != op->o_bd ) {
+ rs->sr_text = "transaction cannot span multiple database contexts";
+ rs->sr_err = LDAP_AFFECTS_MULTIPLE_DSAS;
+ goto txnReturn;
+ }
+
+ /* insert operation into transaction */
+
+ rs->sr_text = "transaction specified";
+ rs->sr_err = LDAP_X_TXN_SPECIFY_OKAY;
+
+txnReturn:
+ /* release connection lock */
+ ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
+
+ if( !settle ) {
+ send_ldap_result( op, rs );
+ return rs->sr_err;
+ }
+ }
+#endif
+
+ ctrls[num_ctrls] = 0;
+
+ /* allocate CSN */
+ if ( BER_BVISNULL( &op->o_csn ) ) {
+ struct berval csn;
+ char csnbuf[LDAP_PVT_CSNSTR_BUFSIZE];
+
+ csn.bv_val = csnbuf;
+ csn.bv_len = sizeof(csnbuf);
+ slap_get_csn( op, &csn, 1 );
+ }
+
+ /* begin transaction */
+ rs->sr_err = mdb_opinfo_get( op, mdb, 0, &moi );
+ rs->sr_text = NULL;
+ if( rs->sr_err != 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_delete) ": txn_begin failed: "
+ "%s (%d)\n", mdb_strerror(rs->sr_err), rs->sr_err, 0 );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ txn = moi->moi_txn;
+
+ if ( !be_issuffix( op->o_bd, &op->o_req_ndn ) ) {
+ dnParent( &op->o_req_ndn, &pdn );
+ }
+
+ /* get parent */
+ rs->sr_err = mdb_dn2entry( op, txn, &pdn, &p, 1 );
+ switch( rs->sr_err ) {
+ case 0:
+ case MDB_NOTFOUND:
+ break;
+ case LDAP_BUSY:
+ rs->sr_text = "ldap server busy";
+ goto return_results;
+ default:
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+ if ( rs->sr_err == MDB_NOTFOUND ) {
+ Debug( LDAP_DEBUG_ARGS,
+ "<=- " LDAP_XSTRING(mdb_delete) ": no such object %s\n",
+ op->o_req_dn.bv_val, 0, 0);
+
+ if ( p && !BER_BVISEMPTY( &p->e_name )) {
+ rs->sr_matched = ch_strdup( p->e_name.bv_val );
+ if ( is_entry_referral( p )) {
+ BerVarray ref = get_entry_referrals( op, p );
+ rs->sr_ref = referral_rewrite( ref, &p->e_name,
+ &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+ ber_bvarray_free( ref );
+ } else {
+ rs->sr_ref = NULL;
+ }
+ } else {
+ rs->sr_ref = referral_rewrite( default_referral, NULL,
+ &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+ }
+ if ( p ) {
+ mdb_entry_return( p );
+ p = NULL;
+ }
+
+ rs->sr_err = LDAP_REFERRAL;
+ rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
+ goto return_results;
+ }
+
+ /* get entry */
+ rs->sr_err = mdb_dn2entry( op, txn, &op->o_req_ndn, &e, 0 );
+ switch( rs->sr_err ) {
+ case MDB_NOTFOUND:
+ e = p;
+ p = NULL;
+ case 0:
+ break;
+ case LDAP_BUSY:
+ rs->sr_text = "ldap server busy";
+ goto return_results;
+ default:
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ /* FIXME : dn2entry() should return non-glue entry */
+ if ( rs->sr_err == MDB_NOTFOUND || ( !manageDSAit && is_entry_glue( e ))) {
+ Debug( LDAP_DEBUG_ARGS,
+ "<=- " LDAP_XSTRING(mdb_delete) ": no such object %s\n",
+ op->o_req_dn.bv_val, 0, 0);
+
+ rs->sr_matched = ch_strdup( e->e_dn );
+ if ( is_entry_referral( e )) {
+ BerVarray ref = get_entry_referrals( op, e );
+ rs->sr_ref = referral_rewrite( ref, &e->e_name,
+ &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+ ber_bvarray_free( ref );
+ } else {
+ rs->sr_ref = NULL;
+ }
+ mdb_entry_return( e );
+ e = NULL;
+
+ rs->sr_err = LDAP_REFERRAL;
+ rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
+ goto return_results;
+ }
+
+ if ( pdn.bv_len != 0 ) {
+ /* check parent for "children" acl */
+ rs->sr_err = access_allowed( op, p,
+ children, NULL, ACL_WDEL, NULL );
+
+ if ( !rs->sr_err ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(mdb_delete) ": no write "
+ "access to parent\n", 0, 0, 0 );
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ rs->sr_text = "no write access to parent";
+ goto return_results;
+ }
+
+ } else {
+ /* no parent, must be root to delete */
+ if( ! be_isroot( op ) ) {
+ if ( be_issuffix( op->o_bd, (struct berval *)&slap_empty_bv )
+ || be_shadow_update( op ) ) {
+ p = (Entry *)&slap_entry_root;
+
+ /* check parent for "children" acl */
+ rs->sr_err = access_allowed( op, p,
+ children, NULL, ACL_WDEL, NULL );
+
+ p = NULL;
+
+ if ( !rs->sr_err ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(mdb_delete)
+ ": no access to parent\n",
+ 0, 0, 0 );
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ rs->sr_text = "no write access to parent";
+ goto return_results;
+ }
+
+ } else {
+ Debug( LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(mdb_delete)
+ ": no parent and not root\n", 0, 0, 0 );
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ goto return_results;
+ }
+ }
+ }
+
+ if ( get_assert( op ) &&
+ ( test_filter( op, e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
+ {
+ rs->sr_err = LDAP_ASSERTION_FAILED;
+ goto return_results;
+ }
+
+ rs->sr_err = access_allowed( op, e,
+ entry, NULL, ACL_WDEL, NULL );
+
+ if ( !rs->sr_err ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(mdb_delete) ": no write access "
+ "to entry\n", 0, 0, 0 );
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ rs->sr_text = "no write access to entry";
+ goto return_results;
+ }
+
+ if ( !manageDSAit && is_entry_referral( e ) ) {
+ /* entry is a referral, don't allow delete */
+ rs->sr_ref = get_entry_referrals( op, e );
+
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_delete) ": entry is referral\n",
+ 0, 0, 0 );
+
+ rs->sr_err = LDAP_REFERRAL;
+ rs->sr_matched = ch_strdup( e->e_name.bv_val );
+ rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
+ goto return_results;
+ }
+
+ /* pre-read */
+ if( op->o_preread ) {
+ if( preread_ctrl == NULL ) {
+ preread_ctrl = &ctrls[num_ctrls++];
+ ctrls[num_ctrls] = NULL;
+ }
+ if( slap_read_controls( op, rs, e,
+ &slap_pre_read_bv, preread_ctrl ) )
+ {
+ Debug( LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(mdb_delete) ": pre-read "
+ "failed!\n", 0, 0, 0 );
+ if ( op->o_preread & SLAP_CONTROL_CRITICAL ) {
+ /* FIXME: is it correct to abort
+ * operation if control fails? */
+ goto return_results;
+ }
+ }
+ }
+
+ rs->sr_text = NULL;
+
+ /* Can't do it if we have kids */
+ rs->sr_err = mdb_dn2id_children( op, txn, e );
+ if( rs->sr_err != MDB_NOTFOUND ) {
+ switch( rs->sr_err ) {
+ case 0:
+ Debug(LDAP_DEBUG_ARGS,
+ "<=- " LDAP_XSTRING(mdb_delete)
+ ": non-leaf %s\n",
+ op->o_req_dn.bv_val, 0, 0);
+ rs->sr_err = LDAP_NOT_ALLOWED_ON_NONLEAF;
+ rs->sr_text = "subordinate objects must be deleted first";
+ break;
+ default:
+ Debug(LDAP_DEBUG_ARGS,
+ "<=- " LDAP_XSTRING(mdb_delete)
+ ": has_children failed: %s (%d)\n",
+ mdb_strerror(rs->sr_err), rs->sr_err, 0 );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ }
+ goto return_results;
+ }
+
+ /* delete from dn2id */
+ rs->sr_err = mdb_dn2id_delete( op, txn, p->e_id, e );
+ if ( rs->sr_err != 0 ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(mdb_delete) ": dn2id failed: "
+ "%s (%d)\n", mdb_strerror(rs->sr_err), rs->sr_err, 0 );
+ rs->sr_text = "DN index delete failed";
+ rs->sr_err = LDAP_OTHER;
+ goto return_results;
+ }
+
+ /* delete indices for old attributes */
+ rs->sr_err = mdb_index_entry_del( op, txn, e );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(mdb_delete) ": index failed: "
+ "%s (%d)\n", mdb_strerror(rs->sr_err), rs->sr_err, 0 );
+ rs->sr_text = "entry index delete failed";
+ rs->sr_err = LDAP_OTHER;
+ goto return_results;
+ }
+
+ /* fixup delete CSN */
+ if ( !SLAP_SHADOW( op->o_bd )) {
+ struct berval vals[2];
+
+ assert( !BER_BVISNULL( &op->o_csn ) );
+ vals[0] = op->o_csn;
+ BER_BVZERO( &vals[1] );
+ rs->sr_err = mdb_index_values( op, txn, slap_schema.si_ad_entryCSN,
+ vals, 0, SLAP_INDEX_ADD_OP );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ rs->sr_text = "entryCSN index update failed";
+ rs->sr_err = LDAP_OTHER;
+ goto return_results;
+ }
+ }
+
+ /* delete from id2entry */
+ rs->sr_err = mdb_id2entry_delete( op->o_bd, txn, e );
+ if ( rs->sr_err != 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(mdb_delete) ": id2entry failed: "
+ "%s (%d)\n", mdb_strerror(rs->sr_err), rs->sr_err, 0 );
+ rs->sr_text = "entry delete failed";
+ rs->sr_err = LDAP_OTHER;
+ goto return_results;
+ }
+
+ if ( pdn.bv_len != 0 ) {
+ parent_is_glue = is_entry_glue(p);
+ rs->sr_err = mdb_dn2id_children( op, txn, p );
+ if ( rs->sr_err != MDB_NOTFOUND ) {
+ switch( rs->sr_err ) {
+ case 0:
+ break;
+ default:
+ Debug(LDAP_DEBUG_ARGS,
+ "<=- " LDAP_XSTRING(mdb_delete)
+ ": has_children failed: %s (%d)\n",
+ mdb_strerror(rs->sr_err), rs->sr_err, 0 );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+ parent_is_leaf = 1;
+ }
+ mdb_entry_return( p );
+ p = NULL;
+ }
+
+ if( moi == &opinfo ) {
+ LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.moi_oe, OpExtra, oe_next );
+ opinfo.moi_oe.oe_key = NULL;
+ if( op->o_noop ) {
+ mdb_txn_abort( txn );
+ rs->sr_err = LDAP_X_NO_OPERATION;
+ txn = NULL;
+ goto return_results;
+ } else {
+ rs->sr_err = mdb_txn_commit( txn );
+ }
+ txn = NULL;
+ }
+
+ if( rs->sr_err != 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_delete) ": txn_%s failed: %s (%d)\n",
+ op->o_noop ? "abort (no-op)" : "commit",
+ mdb_strerror(rs->sr_err), rs->sr_err );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "commit failed";
+
+ goto return_results;
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_delete) ": deleted%s id=%08lx dn=\"%s\"\n",
+ op->o_noop ? " (no-op)" : "",
+ e->e_id, op->o_req_dn.bv_val );
+ rs->sr_err = LDAP_SUCCESS;
+ rs->sr_text = NULL;
+ if( num_ctrls ) rs->sr_ctrls = ctrls;
+
+return_results:
+ if ( rs->sr_err == LDAP_SUCCESS && parent_is_glue && parent_is_leaf ) {
+ op->o_delete_glue_parent = 1;
+ }
+
+ if ( p != NULL ) {
+ mdb_entry_return( p );
+ }
+
+ /* free entry */
+ if( e != NULL ) {
+ mdb_entry_return( e );
+ }
+
+ if( moi == &opinfo ) {
+ if( txn != NULL ) {
+ mdb_txn_abort( txn );
+ }
+ if ( opinfo.moi_oe.oe_key ) {
+ LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.moi_oe, OpExtra, oe_next );
+ }
+ }
+
+ send_ldap_result( op, rs );
+ slap_graduate_commit_csn( op );
+
+ if( preread_ctrl != NULL && (*preread_ctrl) != NULL ) {
+ slap_sl_free( (*preread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
+ slap_sl_free( *preread_ctrl, op->o_tmpmemctx );
+ }
+
+#if 0
+ if( rs->sr_err == LDAP_SUCCESS && mdb->bi_txn_cp_kbyte ) {
+ TXN_CHECKPOINT( mdb->bi_dbenv,
+ mdb->bi_txn_cp_kbyte, mdb->bi_txn_cp_min, 0 );
+ }
+#endif
+ return rs->sr_err;
+}
--- /dev/null
+/* dn2entry.c - routines to deal with the dn2id / id2entry glue */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2011 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-mdb.h"
+
+/*
+ * dn2entry - look up dn in the cache/indexes and return the corresponding
+ * entry. If the requested DN is not found and matched is TRUE, return info
+ * for the closest ancestor of the DN. Otherwise e is NULL.
+ */
+
+int
+mdb_dn2entry(
+ Operation *op,
+ MDB_txn *tid,
+ struct berval *dn,
+ Entry **e,
+ int matched )
+{
+ int rc, rc2;
+ ID id = NOID;
+ struct berval mbv, nmbv;
+
+ Debug(LDAP_DEBUG_TRACE, "mdb_dn2entry(\"%s\")\n",
+ dn->bv_val, 0, 0 );
+
+ *e = NULL;
+
+ rc = mdb_dn2id( op, tid, dn, &id, &mbv, &nmbv );
+ if ( rc ) {
+ if ( matched )
+ rc2 = mdb_id2entry( op, tid, id, e );
+
+ } else {
+ rc = mdb_id2entry( op, tid, id, e );
+ }
+ if ( *e ) {
+ (*e)->e_name = mbv;
+ if ( rc == MDB_SUCCESS )
+ ber_dupbv_x( &(*e)->e_nname, dn, op->o_tmpmemctx );
+ else
+ ber_dupbv_x( &(*e)->e_nname, &nmbv, op->o_tmpmemctx );
+ }
+
+ return rc;
+}
--- /dev/null
+/* dn2id.c - routines to deal with the dn2id index */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2011 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-mdb.h"
+#include "idl.h"
+#include "lutil.h"
+
+/* Management routines for a hierarchically structured database.
+ *
+ * Instead of a ldbm-style dn2id database, we use a hierarchical one. Each
+ * entry in this database is a struct diskNode, keyed by entryID and with
+ * the data containing the RDN and entryID of the node's children. We use
+ * a B-Tree with sorted duplicates to store all the children of a node under
+ * the same key. Also, the first item under the key contains the entry's own
+ * rdn and the ID of the node's parent, to allow bottom-up tree traversal as
+ * well as top-down. To keep this info first in the list, the high bit of all
+ * subsequent nrdnlen's is always set. This means we can only accomodate
+ * RDNs up to length 32767, but that's fine since full DNs are already
+ * restricted to 8192.
+ *
+ * The diskNode is a variable length structure. This definition is not
+ * directly usable for in-memory manipulation.
+ */
+typedef struct diskNode {
+ unsigned char nrdnlen[2];
+ char nrdn[1];
+ char rdn[1]; /* variable placement */
+ unsigned char entryID[sizeof(ID)]; /* variable placement */
+} diskNode;
+
+/* Sort function for the sorted duplicate data items of a dn2id key.
+ * Sorts based on normalized RDN, in length order.
+ */
+int
+mdb_dup_compare(
+ const MDB_val *usrkey,
+ const MDB_val *curkey
+)
+{
+ diskNode *un, *cn;
+ int rc, nrlen;
+
+ un = (diskNode *)usrkey->mv_data;
+ cn = (diskNode *)curkey->mv_data;
+
+ /* data is not aligned, cannot compare directly */
+ rc = un->nrdnlen[0] - cn->nrdnlen[0];
+ if ( rc ) return rc;
+ rc = un->nrdnlen[1] - cn->nrdnlen[1];
+ if ( rc ) return rc;
+
+ nrlen = (un->nrdnlen[0] << 8) | un->nrdnlen[1];
+ return strncmp( un->nrdn, cn->nrdn, nrlen );
+}
+
+#if 0
+/* This function constructs a full DN for a given entry.
+ */
+int mdb_fix_dn(
+ Entry *e,
+ int checkit )
+{
+ EntryInfo *ei;
+ int rlen = 0, nrlen = 0;
+ char *ptr, *nptr;
+ int max = 0;
+
+ if ( !e->e_id )
+ return 0;
+
+ /* count length of all DN components */
+ for ( ei = BEI(e); ei && ei->bei_id; ei=ei->bei_parent ) {
+ rlen += ei->bei_rdn.bv_len + 1;
+ nrlen += ei->bei_nrdn.bv_len + 1;
+ if (ei->bei_modrdns > max) max = ei->bei_modrdns;
+ }
+
+ /* See if the entry DN was invalidated by a subtree rename */
+ if ( checkit ) {
+ if ( BEI(e)->bei_modrdns >= max ) {
+ return 0;
+ }
+ /* We found a mismatch, tell the caller to lock it */
+ if ( checkit == 1 ) {
+ return 1;
+ }
+ /* checkit == 2. do the fix. */
+ free( e->e_name.bv_val );
+ free( e->e_nname.bv_val );
+ }
+
+ e->e_name.bv_len = rlen - 1;
+ e->e_nname.bv_len = nrlen - 1;
+ e->e_name.bv_val = ch_malloc(rlen);
+ e->e_nname.bv_val = ch_malloc(nrlen);
+ ptr = e->e_name.bv_val;
+ nptr = e->e_nname.bv_val;
+ for ( ei = BEI(e); ei && ei->bei_id; ei=ei->bei_parent ) {
+ ptr = lutil_strcopy(ptr, ei->bei_rdn.bv_val);
+ nptr = lutil_strcopy(nptr, ei->bei_nrdn.bv_val);
+ if ( ei->bei_parent ) {
+ *ptr++ = ',';
+ *nptr++ = ',';
+ }
+ }
+ BEI(e)->bei_modrdns = max;
+ if ( ptr > e->e_name.bv_val ) ptr[-1] = '\0';
+ if ( nptr > e->e_nname.bv_val ) nptr[-1] = '\0';
+
+ return 0;
+}
+#endif
+
+/* We add two elements to the DN2ID database - a data item under the parent's
+ * entryID containing the child's RDN and entryID, and an item under the
+ * child's entryID containing the parent's entryID.
+ */
+int
+mdb_dn2id_add(
+ Operation *op,
+ MDB_txn *txn,
+ ID pid,
+ Entry *e )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ MDB_dbi dbi = mdb->mi_dn2id;
+ MDB_val key, data;
+ ID nid;
+ int rc, rlen, nrlen;
+ diskNode *d;
+ char *ptr;
+
+ Debug( LDAP_DEBUG_TRACE, "=> mdb_dn2id_add 0x%lx: \"%s\"\n",
+ e->e_id, e->e_ndn, 0 );
+
+ nrlen = dn_rdnlen( op->o_bd, &e->e_nname );
+ if (nrlen) {
+ rlen = dn_rdnlen( op->o_bd, &e->e_name );
+ } else {
+ nrlen = e->e_nname.bv_len;
+ rlen = e->e_name.bv_len;
+ }
+
+ d = op->o_tmpalloc(sizeof(diskNode) + rlen + nrlen, op->o_tmpmemctx);
+ d->nrdnlen[1] = nrlen & 0xff;
+ d->nrdnlen[0] = (nrlen >> 8) | 0x80;
+ ptr = lutil_strncopy( d->nrdn, e->e_nname.bv_val, nrlen );
+ *ptr++ = '\0';
+ ptr = lutil_strncopy( ptr, e->e_name.bv_val, rlen );
+ *ptr++ = '\0';
+ memcpy( ptr, &e->e_id, sizeof( ID ));
+
+ key.mv_size = sizeof(ID);
+ key.mv_data = &nid;
+
+ nid = pid;
+
+ /* Need to make dummy root node once. Subsequent attempts
+ * will fail harmlessly.
+ */
+ if ( pid == 0 ) {
+ diskNode dummy = {{0, 0}, "", "", ""};
+ data.mv_data = &dummy;
+ data.mv_size = sizeof(diskNode);
+
+ mdb_put( txn, dbi, &key, &data, MDB_NODUPDATA );
+ }
+
+ data.mv_data = d;
+ data.mv_size = sizeof(diskNode) + rlen + nrlen;
+
+ rc = mdb_put( txn, dbi, &key, &data, MDB_NODUPDATA );
+
+ if (rc == 0) {
+ nid = e->e_id;
+ memcpy( ptr, &pid, sizeof( ID ));
+ d->nrdnlen[0] ^= 0x80;
+
+ rc = mdb_put( txn, dbi, &key, &data, MDB_NODUPDATA );
+ }
+
+ op->o_tmpfree( d, op->o_tmpmemctx );
+ Debug( LDAP_DEBUG_TRACE, "<= mdb_dn2id_add 0x%lx: %d\n", e->e_id, rc, 0 );
+
+ return rc;
+}
+
+int
+mdb_dn2id_delete(
+ Operation *op,
+ MDB_txn *txn,
+ ID pid,
+ Entry *e )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ MDB_dbi dbi = mdb->mi_dn2id;
+ MDB_val key, data;
+ diskNode *d;
+ int rc, nrlen;
+ ID nid;
+
+ Debug( LDAP_DEBUG_TRACE, "=> mdb_dn2id_delete 0x%lx: \"%s\"\n",
+ e->e_id, e->e_ndn, 0 );
+
+ key.mv_size = sizeof(ID);
+ key.mv_data = &nid;
+ nid = pid;
+
+ nrlen = dn_rdnlen( op->o_bd, &e->e_nname );
+ data.mv_size = sizeof(diskNode) + nrlen - sizeof(ID) - 1;
+
+ d = op->o_tmpalloc( data.mv_size, op->o_tmpmemctx );
+ d->nrdnlen[1] = nrlen & 0xff;
+ d->nrdnlen[0] = (nrlen >> 8) | 0x80;
+ memcpy( d->nrdn, e->e_nname.bv_val, nrlen );
+ d->nrdn[nrlen] = '\0';
+ data.mv_data = d;
+
+ /* Delete our ID from the parent's list */
+ rc = mdb_del( txn, dbi, &key, &data );
+
+ /* Delete our ID from the tree. With sorted duplicates, this
+ * will leave any child nodes still hanging around. This is OK
+ * for modrdn, which will add our info back in later.
+ */
+ if ( rc == 0 ) {
+ nid = e->e_id;
+ d->nrdnlen[0] ^= 0x80;
+ rc = mdb_del( txn, dbi, &key, &data );
+ }
+
+ op->o_tmpfree( d, op->o_tmpmemctx );
+
+ Debug( LDAP_DEBUG_TRACE, "<= mdb_dn2id_delete 0x%lx: %d\n", e->e_id, rc, 0 );
+ return rc;
+}
+
+/* return last found ID in *id if no match */
+int
+mdb_dn2id(
+ Operation *op,
+ MDB_txn *txn,
+ struct berval *in,
+ ID *id,
+ struct berval *matched,
+ struct berval *nmatched )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ MDB_cursor *cursor;
+ MDB_dbi dbi = mdb->mi_dn2id;
+ MDB_val key, data;
+ int rc = 0, nrlen;
+ diskNode *d;
+ char *ptr;
+ char dn[SLAP_LDAPDN_MAXLEN];
+ ID pid, nid;
+ struct berval tmp;
+
+ Debug( LDAP_DEBUG_TRACE, "=> mdb_dn2id(\"%s\")\n", in->bv_val, 0, 0 );
+
+ if ( matched ) {
+ matched->bv_val = dn + sizeof(dn) - 1;
+ matched->bv_len = 0;
+ *matched->bv_val-- = '\0';
+ }
+ if ( nmatched ) {
+ nmatched->bv_len = 0;
+ nmatched->bv_val = 0;
+ }
+
+ if ( !in->bv_len ) {
+ *id = 0;
+ nid = 0;
+ goto done;
+ }
+
+ tmp = *in;
+
+ if ( op->o_bd->be_nsuffix[0].bv_len ) {
+ nrlen = tmp.bv_len - op->o_bd->be_nsuffix[0].bv_len;
+ tmp.bv_val += nrlen;
+ tmp.bv_len = op->o_bd->be_nsuffix[0].bv_len;
+ } else {
+ for ( ptr = tmp.bv_val + tmp.bv_len - 1; ptr >= tmp.bv_val; ptr-- )
+ if (DN_SEPARATOR(*ptr))
+ break;
+ ptr++;
+ tmp.bv_len -= ptr - tmp.bv_val;
+ tmp.bv_val = ptr;
+ }
+ nid = 0;
+ key.mv_size = sizeof(ID);
+
+ rc = mdb_cursor_open( txn, dbi, &cursor );
+ if ( rc ) return rc;
+
+ for (;;) {
+ key.mv_data = &pid;
+ pid = nid;
+
+ data.mv_size = sizeof(diskNode) + tmp.bv_len;
+ d = op->o_tmpalloc( data.mv_size, op->o_tmpmemctx );
+ d->nrdnlen[1] = tmp.bv_len & 0xff;
+ d->nrdnlen[0] = (tmp.bv_len >> 8) | 0x80;
+ ptr = lutil_strncopy( d->nrdn, tmp.bv_val, tmp.bv_len );
+ *ptr = '\0';
+ data.mv_data = d;
+ rc = mdb_cursor_get( cursor, &key, &data, MDB_GET_BOTH );
+ op->o_tmpfree( d, op->o_tmpmemctx );
+ if ( rc )
+ break;
+ ptr = (char *) data.mv_data + data.mv_size - sizeof(ID);
+ memcpy( &nid, ptr, sizeof(ID));
+
+ /* grab the non-normalized RDN */
+ if ( matched ) {
+ int rlen;
+ d = data.mv_data;
+ rlen = data.mv_size - sizeof(diskNode) - tmp.bv_len;
+ matched->bv_len += rlen;
+ matched->bv_val -= rlen + 1;
+ ptr = lutil_strcopy( matched->bv_val, d->rdn + tmp.bv_len );
+ if ( pid ) {
+ *ptr = ',';
+ matched->bv_len++;
+ }
+ }
+ if ( nmatched ) {
+ nmatched->bv_val = tmp.bv_val;
+ }
+
+ if ( tmp.bv_val > in->bv_val ) {
+ for (ptr = tmp.bv_val - 2; ptr > in->bv_val &&
+ !DN_SEPARATOR(*ptr); ptr--) /* empty */;
+ if ( ptr >= in->bv_val ) {
+ if (DN_SEPARATOR(*ptr)) ptr++;
+ tmp.bv_len = tmp.bv_val - ptr - 1;
+ tmp.bv_val = ptr;
+ }
+ } else {
+ break;
+ }
+ }
+ *id = nid;
+ mdb_cursor_close( cursor );
+done:
+ if ( matched ) {
+ if ( matched->bv_len ) {
+ ptr = op->o_tmpalloc( matched->bv_len+1, op->o_tmpmemctx );
+ strcpy( ptr, matched->bv_val );
+ matched->bv_val = ptr;
+ } else {
+ if ( BER_BVISEMPTY( &op->o_bd->be_nsuffix[0] ) && !nid ) {
+ ber_dupbv( matched, (struct berval *)&slap_empty_bv );
+ } else {
+ matched->bv_val = NULL;
+ }
+ }
+ }
+ if ( nmatched ) {
+ if ( nmatched->bv_val ) {
+ nmatched->bv_len = in->bv_len - (nmatched->bv_val - in->bv_val);
+ } else {
+ *nmatched = slap_empty_bv;
+ }
+ }
+
+ if( rc != 0 ) {
+ Debug( LDAP_DEBUG_TRACE, "<= mdb_dn2id: get failed: %s (%d)\n",
+ mdb_strerror( rc ), rc, 0 );
+ } else {
+ Debug( LDAP_DEBUG_TRACE, "<= mdb_dn2id: got id=0x%lx\n",
+ nid, 0, 0 );
+ }
+
+ return rc;
+}
+
+/* return IDs from root to parent of DN */
+int
+mdb_dn2sups(
+ Operation *op,
+ MDB_txn *txn,
+ struct berval *in,
+ ID *ids )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ MDB_cursor *cursor;
+ MDB_dbi dbi = mdb->mi_dn2id;
+ MDB_val key, data;
+ int rc = 0, nrlen;
+ diskNode *d;
+ char *ptr;
+ ID pid, nid;
+ struct berval tmp;
+
+ Debug( LDAP_DEBUG_TRACE, "=> mdb_dn2sups(\"%s\")\n", in->bv_val, 0, 0 );
+
+ if ( !in->bv_len ) {
+ goto done;
+ }
+
+ tmp = *in;
+
+ nrlen = tmp.bv_len - op->o_bd->be_nsuffix[0].bv_len;
+ tmp.bv_val += nrlen;
+ tmp.bv_len = op->o_bd->be_nsuffix[0].bv_len;
+ nid = 0;
+ key.mv_size = sizeof(ID);
+
+ rc = mdb_cursor_open( txn, dbi, &cursor );
+ if ( rc ) return rc;
+
+ for (;;) {
+ key.mv_data = &pid;
+ pid = nid;
+
+ data.mv_size = sizeof(diskNode) + tmp.bv_len;
+ d = op->o_tmpalloc( data.mv_size, op->o_tmpmemctx );
+ d->nrdnlen[1] = tmp.bv_len & 0xff;
+ d->nrdnlen[0] = (tmp.bv_len >> 8) | 0x80;
+ ptr = lutil_strncopy( d->nrdn, tmp.bv_val, tmp.bv_len );
+ *ptr = '\0';
+ data.mv_data = d;
+ rc = mdb_cursor_get( cursor, &key, &data, MDB_GET_BOTH );
+ op->o_tmpfree( d, op->o_tmpmemctx );
+ if ( rc ) {
+ mdb_cursor_close( cursor );
+ break;
+ }
+ ptr = (char *) data.mv_data + data.mv_size - sizeof(ID);
+ memcpy( &nid, ptr, sizeof(ID));
+
+ if ( pid )
+ mdb_idl_insert( ids, pid );
+
+ if ( tmp.bv_val > in->bv_val ) {
+ for (ptr = tmp.bv_val - 2; ptr > in->bv_val &&
+ !DN_SEPARATOR(*ptr); ptr--) /* empty */;
+ if ( ptr >= in->bv_val ) {
+ if (DN_SEPARATOR(*ptr)) ptr++;
+ tmp.bv_len = tmp.bv_val - ptr - 1;
+ tmp.bv_val = ptr;
+ }
+ } else {
+ break;
+ }
+ }
+
+done:
+ if( rc != 0 ) {
+ Debug( LDAP_DEBUG_TRACE, "<= mdb_dn2sups: get failed: %s (%d)\n",
+ mdb_strerror( rc ), rc, 0 );
+ }
+
+ return rc;
+}
+
+#if 0
+int
+mdb_dn2id_parent(
+ Operation *op,
+ DB_TXN *txn,
+ EntryInfo *ei,
+ ID *idp )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ DB *db = mdb->bi_dn2id->bdi_db;
+ DBT key, data;
+ DBC *cursor;
+ int rc = 0;
+ diskNode *d;
+ char *ptr;
+ ID nid;
+
+ DBTzero(&key);
+ key.size = sizeof(ID);
+ key.data = &nid;
+ key.ulen = sizeof(ID);
+ key.flags = DB_DBT_USERMEM;
+ MDB_ID2DISK( ei->bei_id, &nid );
+
+ DBTzero(&data);
+ data.flags = DB_DBT_USERMEM;
+
+ rc = db->cursor( db, txn, &cursor, mdb->bi_db_opflags );
+ if ( rc ) return rc;
+
+ data.ulen = sizeof(diskNode) + (SLAP_LDAPDN_MAXLEN * 2);
+ d = op->o_tmpalloc( data.ulen, op->o_tmpmemctx );
+ data.data = d;
+
+ rc = cursor->c_get( cursor, &key, &data, DB_SET );
+ if ( rc == 0 ) {
+ if (d->nrdnlen[0] & 0x80) {
+ rc = LDAP_OTHER;
+ } else {
+ db_recno_t dkids;
+ ptr = (char *) data.data + data.size - sizeof(ID);
+ MDB_DISK2ID( ptr, idp );
+ ei->bei_nrdn.bv_len = (d->nrdnlen[0] << 8) | d->nrdnlen[1];
+ ber_str2bv( d->nrdn, ei->bei_nrdn.bv_len, 1, &ei->bei_nrdn );
+ ei->bei_rdn.bv_len = data.size - sizeof(diskNode) -
+ ei->bei_nrdn.bv_len;
+ ptr = d->nrdn + ei->bei_nrdn.bv_len + 1;
+ ber_str2bv( ptr, ei->bei_rdn.bv_len, 1, &ei->bei_rdn );
+ /* How many children does this node have? */
+ cursor->c_count( cursor, &dkids, 0 );
+ ei->bei_dkids = dkids;
+ }
+ }
+ cursor->c_close( cursor );
+ op->o_tmpfree( d, op->o_tmpmemctx );
+ return rc;
+}
+#endif
+
+int
+mdb_dn2id_children(
+ Operation *op,
+ MDB_txn *txn,
+ Entry *e )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ MDB_dbi dbi = mdb->mi_dn2id;
+ MDB_val key, data;
+ MDB_cursor *cursor;
+ int rc;
+ ID id;
+
+ key.mv_size = sizeof(ID);
+ key.mv_data = &id;
+ id = e->e_id;
+
+ rc = mdb_cursor_open( txn, dbi, &cursor );
+ if ( rc ) return rc;
+
+ rc = mdb_cursor_get( cursor, &key, &data, MDB_SET );
+ if ( rc == 0 ) {
+ unsigned long dkids;
+ rc = mdb_cursor_count( cursor, &dkids );
+ if ( rc == 0 ) {
+ if ( dkids < 2 ) rc = MDB_NOTFOUND;
+ }
+ }
+ mdb_cursor_close( cursor );
+ return rc;
+}
+
+int
+mdb_id2name(
+ Operation *op,
+ MDB_txn *txn,
+ MDB_cursor **cursp,
+ ID id,
+ struct berval *name,
+ struct berval *nname )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ MDB_dbi dbi = mdb->mi_dn2id;
+ MDB_val key, data;
+ MDB_cursor *cursor;
+ int rc, len, nlen;
+ char dn[SLAP_LDAPDN_MAXLEN], ndn[SLAP_LDAPDN_MAXLEN], *ptr;
+ char *dptr, *nptr;
+ diskNode *d;
+
+ key.mv_size = sizeof(ID);
+
+ if ( !*cursp ) {
+ rc = mdb_cursor_open( txn, dbi, cursp );
+ if ( rc ) return rc;
+ }
+ cursor = *cursp;
+
+ len = 0;
+ nlen = 0;
+ dptr = dn;
+ nptr = ndn;
+ while (id) {
+ unsigned int nrlen, rlen;
+ key.mv_data = &id;
+ data.mv_size = 0;
+ data.mv_data = "";
+ rc = mdb_cursor_get( cursor, &key, &data, MDB_SET );
+ if ( rc ) break;
+ ptr = data.mv_data;
+ ptr += data.mv_size - sizeof(ID);
+ memcpy( &id, ptr, sizeof(ID) );
+ d = data.mv_data;
+ nrlen = (d->nrdnlen[0] << 8) | d->nrdnlen[1];
+ rlen = data.mv_size - sizeof(diskNode) - nrlen;
+ assert( nrlen < 1024 && rlen < 1024 ); /* FIXME: Sanity check */
+ if (nptr > ndn) {
+ *nptr++ = ',';
+ *dptr++ = ',';
+ }
+ /* copy name and trailing NUL */
+ memcpy( nptr, d->nrdn, nrlen+1 );
+ memcpy( dptr, d->nrdn+nrlen+1, rlen+1 );
+ nptr += nrlen;
+ dptr += rlen;
+ }
+ if ( rc == 0 ) {
+ name->bv_len = dptr - dn;
+ nname->bv_len = nptr - ndn;
+ name->bv_val = op->o_tmpalloc( name->bv_len + 1, op->o_tmpmemctx );
+ nname->bv_val = op->o_tmpalloc( nname->bv_len + 1, op->o_tmpmemctx );
+ memcpy( name->bv_val, dn, name->bv_len );
+ name->bv_val[name->bv_len] = '\0';
+ memcpy( nname->bv_val, ndn, nname->bv_len );
+ nname->bv_val[nname->bv_len] = '\0';
+ }
+ return rc;
+}
+
+/* Find each id in ids that is a child of base and move it to res.
+ */
+int
+mdb_idscope(
+ Operation *op,
+ MDB_txn *txn,
+ ID base,
+ ID *ids,
+ ID *res )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ MDB_dbi dbi = mdb->mi_dn2id;
+ MDB_val key, data;
+ MDB_cursor *cursor;
+ ID ida, id, cid, ci0, idc = 0;
+ char *ptr;
+ int rc;
+
+ key.mv_size = sizeof(ID);
+
+ MDB_IDL_ZERO( res );
+
+ rc = mdb_cursor_open( txn, dbi, &cursor );
+ if ( rc ) return rc;
+
+ ida = mdb_idl_first( ids, &cid );
+
+ /* Don't bother moving out of ids if it's a range */
+ if (!MDB_IDL_IS_RANGE(ids)) {
+ idc = ids[0];
+ ci0 = cid;
+ }
+
+ while (ida != NOID) {
+ id = ida;
+ while (id) {
+ key.mv_data = &id;
+ rc = mdb_cursor_get( cursor, &key, &data, MDB_SET );
+ if ( rc ) {
+ /* not found, move on to next */
+ if (idc) {
+ if (ci0 != cid)
+ ids[ci0] = ids[cid];
+ ci0++;
+ }
+ break;
+ }
+ ptr = data.mv_data;
+ ptr += data.mv_size - sizeof(ID);
+ memcpy( &id, ptr, sizeof(ID) );
+ if ( id == base ) {
+ res[0]++;
+ res[res[0]] = ida;
+ if (idc)
+ idc--;
+ break;
+ } else {
+ if (idc) {
+ if (ci0 != cid)
+ ids[ci0] = ids[cid];
+ ci0++;
+ }
+ }
+ if ( op->ors_scope == LDAP_SCOPE_ONELEVEL )
+ break;
+ }
+ ida = mdb_idl_next( ids, &cid );
+ }
+ if (!MDB_IDL_IS_RANGE( ids ))
+ ids[0] = idc;
+
+ mdb_cursor_close( cursor );
+ return rc;
+}
+
+/* See if base is a child of any of the scopes
+ */
+int
+mdb_idscopes(
+ Operation *op,
+ MDB_txn *txn,
+ MDB_cursor **cursp,
+ ID base,
+ ID *scopes )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ MDB_dbi dbi = mdb->mi_dn2id;
+ MDB_val key, data;
+ MDB_cursor *cursor;
+ ID id;
+ char *ptr;
+ int rc;
+ unsigned int x;
+
+ key.mv_size = sizeof(ID);
+
+ if ( !*cursp ) {
+ rc = mdb_cursor_open( txn, dbi, cursp );
+ if ( rc ) return rc;
+ }
+ cursor = *cursp;
+
+ id = base;
+ while (id) {
+ key.mv_data = &id;
+ rc = mdb_cursor_get( cursor, &key, &data, MDB_SET );
+ if ( rc )
+ break;
+ ptr = data.mv_data;
+ ptr += data.mv_size - sizeof(ID);
+ memcpy( &id, ptr, sizeof(ID) );
+ x = mdb_idl_search( scopes, id );
+ if ( scopes[x] == id )
+ return MDB_SUCCESS;
+ if ( op->ors_scope == LDAP_SCOPE_ONELEVEL )
+ break;
+ }
+ return MDB_NOTFOUND;
+}
+
+#if 0
+/* mdb_dn2idl:
+ * We can't just use mdb_idl_fetch_key because
+ * 1 - our data items are longer than just an entry ID
+ * 2 - our data items are sorted alphabetically by nrdn, not by ID.
+ *
+ * We descend the tree recursively, so we define this cookie
+ * to hold our necessary state information. The mdb_dn2idl_internal
+ * function uses this cookie when calling itself.
+ */
+
+struct dn2id_cookie {
+ struct mdb_info *mdb;
+ Operation *op;
+ DB_TXN *txn;
+ EntryInfo *ei;
+ ID *ids;
+ ID *tmp;
+ ID *buf;
+ DB *db;
+ DBC *dbc;
+ DBT key;
+ DBT data;
+ ID dbuf;
+ ID id;
+ ID nid;
+ int rc;
+ int depth;
+ char need_sort;
+ char prefix;
+};
+
+static int
+apply_func(
+ void *data,
+ void *arg )
+{
+ EntryInfo *ei = data;
+ ID *idl = arg;
+
+ mdb_idl_append_one( idl, ei->bei_id );
+ return 0;
+}
+
+static int
+mdb_dn2idl_internal(
+ struct dn2id_cookie *cx
+)
+{
+ MDB_IDL_ZERO( cx->tmp );
+
+ if ( cx->mdb->bi_idl_cache_size ) {
+ char *ptr = ((char *)&cx->id)-1;
+
+ cx->key.data = ptr;
+ cx->key.size = sizeof(ID)+1;
+ if ( cx->prefix == DN_SUBTREE_PREFIX ) {
+ ID *ids = cx->depth ? cx->tmp : cx->ids;
+ *ptr = cx->prefix;
+ cx->rc = mdb_idl_cache_get(cx->mdb, cx->db, &cx->key, ids);
+ if ( cx->rc == LDAP_SUCCESS ) {
+ if ( cx->depth ) {
+ mdb_idl_append( cx->ids, cx->tmp );
+ cx->need_sort = 1;
+ }
+ return cx->rc;
+ }
+ }
+ *ptr = DN_ONE_PREFIX;
+ cx->rc = mdb_idl_cache_get(cx->mdb, cx->db, &cx->key, cx->tmp);
+ if ( cx->rc == LDAP_SUCCESS ) {
+ goto gotit;
+ }
+ if ( cx->rc == DB_NOTFOUND ) {
+ return cx->rc;
+ }
+ }
+
+ mdb_cache_entryinfo_lock( cx->ei );
+
+ /* If number of kids in the cache differs from on-disk, load
+ * up all the kids from the database
+ */
+ if ( cx->ei->bei_ckids+1 != cx->ei->bei_dkids ) {
+ EntryInfo ei;
+ db_recno_t dkids = cx->ei->bei_dkids;
+ ei.bei_parent = cx->ei;
+
+ /* Only one thread should load the cache */
+ while ( cx->ei->bei_state & CACHE_ENTRY_ONELEVEL ) {
+ mdb_cache_entryinfo_unlock( cx->ei );
+ ldap_pvt_thread_yield();
+ mdb_cache_entryinfo_lock( cx->ei );
+ if ( cx->ei->bei_ckids+1 == cx->ei->bei_dkids ) {
+ goto synced;
+ }
+ }
+
+ cx->ei->bei_state |= CACHE_ENTRY_ONELEVEL;
+
+ mdb_cache_entryinfo_unlock( cx->ei );
+
+ cx->rc = cx->db->cursor( cx->db, NULL, &cx->dbc,
+ cx->mdb->bi_db_opflags );
+ if ( cx->rc )
+ goto done_one;
+
+ cx->data.data = &cx->dbuf;
+ cx->data.ulen = sizeof(ID);
+ cx->data.dlen = sizeof(ID);
+ cx->data.flags = DB_DBT_USERMEM | DB_DBT_PARTIAL;
+
+ /* The first item holds the parent ID. Ignore it. */
+ cx->key.data = &cx->nid;
+ cx->key.size = sizeof(ID);
+ cx->rc = cx->dbc->c_get( cx->dbc, &cx->key, &cx->data, DB_SET );
+ if ( cx->rc ) {
+ cx->dbc->c_close( cx->dbc );
+ goto done_one;
+ }
+
+ /* If the on-disk count is zero we've never checked it.
+ * Count it now.
+ */
+ if ( !dkids ) {
+ cx->dbc->c_count( cx->dbc, &dkids, 0 );
+ cx->ei->bei_dkids = dkids;
+ }
+
+ cx->data.data = cx->buf;
+ cx->data.ulen = MDB_IDL_UM_SIZE * sizeof(ID);
+ cx->data.flags = DB_DBT_USERMEM;
+
+ if ( dkids > 1 ) {
+ /* Fetch the rest of the IDs in a loop... */
+ while ( (cx->rc = cx->dbc->c_get( cx->dbc, &cx->key, &cx->data,
+ DB_MULTIPLE | DB_NEXT_DUP )) == 0 ) {
+ u_int8_t *j;
+ size_t len;
+ void *ptr;
+ DB_MULTIPLE_INIT( ptr, &cx->data );
+ while (ptr) {
+ DB_MULTIPLE_NEXT( ptr, &cx->data, j, len );
+ if (j) {
+ EntryInfo *ei2;
+ diskNode *d = (diskNode *)j;
+ short nrlen;
+
+ MDB_DISK2ID( j + len - sizeof(ID), &ei.bei_id );
+ nrlen = ((d->nrdnlen[0] ^ 0x80) << 8) | d->nrdnlen[1];
+ ei.bei_nrdn.bv_len = nrlen;
+ /* nrdn/rdn are set in-place.
+ * mdb_cache_load will copy them as needed
+ */
+ ei.bei_nrdn.bv_val = d->nrdn;
+ ei.bei_rdn.bv_len = len - sizeof(diskNode)
+ - ei.bei_nrdn.bv_len;
+ ei.bei_rdn.bv_val = d->nrdn + ei.bei_nrdn.bv_len + 1;
+ mdb_idl_append_one( cx->tmp, ei.bei_id );
+ mdb_cache_load( cx->mdb, &ei, &ei2 );
+ }
+ }
+ }
+ }
+
+ cx->rc = cx->dbc->c_close( cx->dbc );
+done_one:
+ mdb_cache_entryinfo_lock( cx->ei );
+ cx->ei->bei_state &= ~CACHE_ENTRY_ONELEVEL;
+ mdb_cache_entryinfo_unlock( cx->ei );
+ if ( cx->rc )
+ return cx->rc;
+
+ } else {
+ /* The in-memory cache is in sync with the on-disk data.
+ * do we have any kids?
+ */
+synced:
+ cx->rc = 0;
+ if ( cx->ei->bei_ckids > 0 ) {
+ /* Walk the kids tree; order is irrelevant since mdb_idl_sort
+ * will sort it later.
+ */
+ avl_apply( cx->ei->bei_kids, apply_func,
+ cx->tmp, -1, AVL_POSTORDER );
+ }
+ mdb_cache_entryinfo_unlock( cx->ei );
+ }
+
+ if ( !MDB_IDL_IS_RANGE( cx->tmp ) && cx->tmp[0] > 3 )
+ mdb_idl_sort( cx->tmp, cx->buf );
+ if ( cx->mdb->bi_idl_cache_max_size && !MDB_IDL_IS_ZERO( cx->tmp )) {
+ char *ptr = ((char *)&cx->id)-1;
+ cx->key.data = ptr;
+ cx->key.size = sizeof(ID)+1;
+ *ptr = DN_ONE_PREFIX;
+ mdb_idl_cache_put( cx->mdb, cx->db, &cx->key, cx->tmp, cx->rc );
+ }
+
+gotit:
+ if ( !MDB_IDL_IS_ZERO( cx->tmp )) {
+ if ( cx->prefix == DN_SUBTREE_PREFIX ) {
+ mdb_idl_append( cx->ids, cx->tmp );
+ cx->need_sort = 1;
+ if ( !(cx->ei->bei_state & CACHE_ENTRY_NO_GRANDKIDS)) {
+ ID *save, idcurs;
+ EntryInfo *ei = cx->ei;
+ int nokids = 1;
+ save = cx->op->o_tmpalloc( MDB_IDL_SIZEOF( cx->tmp ),
+ cx->op->o_tmpmemctx );
+ MDB_IDL_CPY( save, cx->tmp );
+
+ idcurs = 0;
+ cx->depth++;
+ for ( cx->id = mdb_idl_first( save, &idcurs );
+ cx->id != NOID;
+ cx->id = mdb_idl_next( save, &idcurs )) {
+ EntryInfo *ei2;
+ cx->ei = NULL;
+ if ( mdb_cache_find_id( cx->op, cx->txn, cx->id, &cx->ei,
+ ID_NOENTRY, NULL ))
+ continue;
+ if ( cx->ei ) {
+ ei2 = cx->ei;
+ if ( !( ei2->bei_state & CACHE_ENTRY_NO_KIDS )) {
+ MDB_ID2DISK( cx->id, &cx->nid );
+ mdb_dn2idl_internal( cx );
+ if ( !MDB_IDL_IS_ZERO( cx->tmp ))
+ nokids = 0;
+ }
+ mdb_cache_entryinfo_lock( ei2 );
+ ei2->bei_finders--;
+ mdb_cache_entryinfo_unlock( ei2 );
+ }
+ }
+ cx->depth--;
+ cx->op->o_tmpfree( save, cx->op->o_tmpmemctx );
+ if ( nokids ) {
+ mdb_cache_entryinfo_lock( ei );
+ ei->bei_state |= CACHE_ENTRY_NO_GRANDKIDS;
+ mdb_cache_entryinfo_unlock( ei );
+ }
+ }
+ /* Make sure caller knows it had kids! */
+ cx->tmp[0]=1;
+
+ cx->rc = 0;
+ } else {
+ MDB_IDL_CPY( cx->ids, cx->tmp );
+ }
+ }
+ return cx->rc;
+}
+
+int
+mdb_dn2idl(
+ Operation *op,
+ DB_TXN *txn,
+ struct berval *ndn,
+ EntryInfo *ei,
+ ID *ids,
+ ID *stack )
+{
+ struct mdb_info *mdb = (struct mdb_info *)op->o_bd->be_private;
+ struct dn2id_cookie cx;
+
+ Debug( LDAP_DEBUG_TRACE, "=> mdb_dn2idl(\"%s\")\n",
+ ndn->bv_val, 0, 0 );
+
+#ifndef MDB_MULTIPLE_SUFFIXES
+ if ( op->ors_scope != LDAP_SCOPE_ONELEVEL &&
+ ( ei->bei_id == 0 ||
+ ( ei->bei_parent->bei_id == 0 && op->o_bd->be_suffix[0].bv_len )))
+ {
+ MDB_IDL_ALL( mdb, ids );
+ return 0;
+ }
+#endif
+
+ cx.id = ei->bei_id;
+ MDB_ID2DISK( cx.id, &cx.nid );
+ cx.ei = ei;
+ cx.mdb = mdb;
+ cx.db = cx.mdb->bi_dn2id->bdi_db;
+ cx.prefix = (op->ors_scope == LDAP_SCOPE_ONELEVEL) ?
+ DN_ONE_PREFIX : DN_SUBTREE_PREFIX;
+ cx.ids = ids;
+ cx.tmp = stack;
+ cx.buf = stack + MDB_IDL_UM_SIZE;
+ cx.op = op;
+ cx.txn = txn;
+ cx.need_sort = 0;
+ cx.depth = 0;
+
+ if ( cx.prefix == DN_SUBTREE_PREFIX ) {
+ ids[0] = 1;
+ ids[1] = cx.id;
+ } else {
+ MDB_IDL_ZERO( ids );
+ }
+ if ( cx.ei->bei_state & CACHE_ENTRY_NO_KIDS )
+ return LDAP_SUCCESS;
+
+ DBTzero(&cx.key);
+ cx.key.ulen = sizeof(ID);
+ cx.key.size = sizeof(ID);
+ cx.key.flags = DB_DBT_USERMEM;
+
+ DBTzero(&cx.data);
+
+ mdb_dn2idl_internal(&cx);
+ if ( cx.need_sort ) {
+ char *ptr = ((char *)&cx.id)-1;
+ if ( !MDB_IDL_IS_RANGE( cx.ids ) && cx.ids[0] > 3 )
+ mdb_idl_sort( cx.ids, cx.tmp );
+ cx.key.data = ptr;
+ cx.key.size = sizeof(ID)+1;
+ *ptr = cx.prefix;
+ cx.id = ei->bei_id;
+ if ( cx.mdb->bi_idl_cache_max_size )
+ mdb_idl_cache_put( cx.mdb, cx.db, &cx.key, cx.ids, cx.rc );
+ }
+
+ if ( cx.rc == DB_NOTFOUND )
+ cx.rc = LDAP_SUCCESS;
+
+ return cx.rc;
+}
+#endif
--- /dev/null
+/* extended.c - mdb backend extended routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2011 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-mdb.h"
+#include "lber_pvt.h"
+
+static struct exop {
+ struct berval *oid;
+ BI_op_extended *extended;
+} exop_table[] = {
+ { NULL, NULL }
+};
+
+int
+mdb_extended( Operation *op, SlapReply *rs )
+/* struct berval *reqoid,
+ struct berval *reqdata,
+ char **rspoid,
+ struct berval **rspdata,
+ LDAPControl *** rspctrls,
+ const char** text,
+ BerVarray *refs
+) */
+{
+ int i;
+
+ for( i=0; exop_table[i].extended != NULL; i++ ) {
+ if( ber_bvcmp( exop_table[i].oid, &op->oq_extended.rs_reqoid ) == 0 ) {
+ return (exop_table[i].extended)( op, rs );
+ }
+ }
+
+ rs->sr_text = "not supported within naming context";
+ return rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+}
+
--- /dev/null
+/* filterindex.c - generate the list of candidate entries from a filter */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2011 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-mdb.h"
+#include "idl.h"
+#ifdef LDAP_COMP_MATCH
+#include <component.h>
+#endif
+
+static int presence_candidates(
+ Operation *op,
+ MDB_txn *rtxn,
+ AttributeDescription *desc,
+ ID *ids );
+
+static int equality_candidates(
+ Operation *op,
+ MDB_txn *rtxn,
+ AttributeAssertion *ava,
+ ID *ids,
+ ID *tmp );
+static int inequality_candidates(
+ Operation *op,
+ MDB_txn *rtxn,
+ AttributeAssertion *ava,
+ ID *ids,
+ ID *tmp,
+ int gtorlt );
+static int approx_candidates(
+ Operation *op,
+ MDB_txn *rtxn,
+ AttributeAssertion *ava,
+ ID *ids,
+ ID *tmp );
+static int substring_candidates(
+ Operation *op,
+ MDB_txn *rtxn,
+ SubstringsAssertion *sub,
+ ID *ids,
+ ID *tmp );
+
+static int list_candidates(
+ Operation *op,
+ MDB_txn *rtxn,
+ Filter *flist,
+ int ftype,
+ ID *ids,
+ ID *tmp,
+ ID *stack );
+
+static int
+ext_candidates(
+ Operation *op,
+ MDB_txn *rtxn,
+ MatchingRuleAssertion *mra,
+ ID *ids,
+ ID *tmp,
+ ID *stack);
+
+#ifdef LDAP_COMP_MATCH
+static int
+comp_candidates (
+ Operation *op,
+ MDB_txn *rtxn,
+ MatchingRuleAssertion *mra,
+ ComponentFilter *f,
+ ID *ids,
+ ID *tmp,
+ ID *stack);
+
+static int
+ava_comp_candidates (
+ Operation *op,
+ MDB_txn *rtxn,
+ AttributeAssertion *ava,
+ AttributeAliasing *aa,
+ ID *ids,
+ ID *tmp,
+ ID *stack);
+#endif
+
+int
+mdb_filter_candidates(
+ Operation *op,
+ MDB_txn *rtxn,
+ Filter *f,
+ ID *ids,
+ ID *tmp,
+ ID *stack )
+{
+ int rc = 0;
+#ifdef LDAP_COMP_MATCH
+ AttributeAliasing *aa;
+#endif
+ Debug( LDAP_DEBUG_FILTER, "=> mdb_filter_candidates\n", 0, 0, 0 );
+
+ if ( f->f_choice & SLAPD_FILTER_UNDEFINED ) {
+ MDB_IDL_ZERO( ids );
+ goto out;
+ }
+
+ switch ( f->f_choice ) {
+ case SLAPD_FILTER_COMPUTED:
+ switch( f->f_result ) {
+ case SLAPD_COMPARE_UNDEFINED:
+ /* This technically is not the same as FALSE, but it
+ * certainly will produce no matches.
+ */
+ /* FALL THRU */
+ case LDAP_COMPARE_FALSE:
+ MDB_IDL_ZERO( ids );
+ break;
+ case LDAP_COMPARE_TRUE:
+ MDB_IDL_ALL( ids );
+ break;
+ case LDAP_SUCCESS:
+ /* this is a pre-computed scope, leave it alone */
+ break;
+ }
+ break;
+ case LDAP_FILTER_PRESENT:
+ Debug( LDAP_DEBUG_FILTER, "\tPRESENT\n", 0, 0, 0 );
+ rc = presence_candidates( op, rtxn, f->f_desc, ids );
+ break;
+
+ case LDAP_FILTER_EQUALITY:
+ Debug( LDAP_DEBUG_FILTER, "\tEQUALITY\n", 0, 0, 0 );
+#ifdef LDAP_COMP_MATCH
+ if ( is_aliased_attribute && ( aa = is_aliased_attribute ( f->f_ava->aa_desc ) ) ) {
+ rc = ava_comp_candidates ( op, rtxn, f->f_ava, aa, ids, tmp, stack );
+ }
+ else
+#endif
+ {
+ rc = equality_candidates( op, rtxn, f->f_ava, ids, tmp );
+ }
+ break;
+
+ case LDAP_FILTER_APPROX:
+ Debug( LDAP_DEBUG_FILTER, "\tAPPROX\n", 0, 0, 0 );
+ rc = approx_candidates( op, rtxn, f->f_ava, ids, tmp );
+ break;
+
+ case LDAP_FILTER_SUBSTRINGS:
+ Debug( LDAP_DEBUG_FILTER, "\tSUBSTRINGS\n", 0, 0, 0 );
+ rc = substring_candidates( op, rtxn, f->f_sub, ids, tmp );
+ break;
+
+ case LDAP_FILTER_GE:
+ /* if no GE index, use pres */
+ Debug( LDAP_DEBUG_FILTER, "\tGE\n", 0, 0, 0 );
+ if( f->f_ava->aa_desc->ad_type->sat_ordering &&
+ ( f->f_ava->aa_desc->ad_type->sat_ordering->smr_usage & SLAP_MR_ORDERED_INDEX ) )
+ rc = inequality_candidates( op, rtxn, f->f_ava, ids, tmp, LDAP_FILTER_GE );
+ else
+ rc = presence_candidates( op, rtxn, f->f_ava->aa_desc, ids );
+ break;
+
+ case LDAP_FILTER_LE:
+ /* if no LE index, use pres */
+ Debug( LDAP_DEBUG_FILTER, "\tLE\n", 0, 0, 0 );
+ if( f->f_ava->aa_desc->ad_type->sat_ordering &&
+ ( f->f_ava->aa_desc->ad_type->sat_ordering->smr_usage & SLAP_MR_ORDERED_INDEX ) )
+ rc = inequality_candidates( op, rtxn, f->f_ava, ids, tmp, LDAP_FILTER_LE );
+ else
+ rc = presence_candidates( op, rtxn, f->f_ava->aa_desc, ids );
+ break;
+
+ case LDAP_FILTER_NOT:
+ /* no indexing to support NOT filters */
+ Debug( LDAP_DEBUG_FILTER, "\tNOT\n", 0, 0, 0 );
+ MDB_IDL_ALL( ids );
+ break;
+
+ case LDAP_FILTER_AND:
+ Debug( LDAP_DEBUG_FILTER, "\tAND\n", 0, 0, 0 );
+ rc = list_candidates( op, rtxn,
+ f->f_and, LDAP_FILTER_AND, ids, tmp, stack );
+ break;
+
+ case LDAP_FILTER_OR:
+ Debug( LDAP_DEBUG_FILTER, "\tOR\n", 0, 0, 0 );
+ rc = list_candidates( op, rtxn,
+ f->f_or, LDAP_FILTER_OR, ids, tmp, stack );
+ break;
+ case LDAP_FILTER_EXT:
+ Debug( LDAP_DEBUG_FILTER, "\tEXT\n", 0, 0, 0 );
+ rc = ext_candidates( op, rtxn, f->f_mra, ids, tmp, stack );
+ break;
+ default:
+ Debug( LDAP_DEBUG_FILTER, "\tUNKNOWN %lu\n",
+ (unsigned long) f->f_choice, 0, 0 );
+ /* Must not return NULL, otherwise extended filters break */
+ MDB_IDL_ALL( ids );
+ }
+
+out:
+ Debug( LDAP_DEBUG_FILTER,
+ "<= mdb_filter_candidates: id=%ld first=%ld last=%ld\n",
+ (long) ids[0],
+ (long) MDB_IDL_FIRST( ids ),
+ (long) MDB_IDL_LAST( ids ) );
+
+ return rc;
+}
+
+#ifdef LDAP_COMP_MATCH
+static int
+comp_list_candidates(
+ Operation *op,
+ MDB_txn *rtxn,
+ MatchingRuleAssertion* mra,
+ ComponentFilter *flist,
+ int ftype,
+ ID *ids,
+ ID *tmp,
+ ID *save )
+{
+ int rc = 0;
+ ComponentFilter *f;
+
+ Debug( LDAP_DEBUG_FILTER, "=> comp_list_candidates 0x%x\n", ftype, 0, 0 );
+ for ( f = flist; f != NULL; f = f->cf_next ) {
+ /* ignore precomputed scopes */
+ if ( f->cf_choice == SLAPD_FILTER_COMPUTED &&
+ f->cf_result == LDAP_SUCCESS ) {
+ continue;
+ }
+ MDB_IDL_ZERO( save );
+ rc = comp_candidates( op, rtxn, mra, f, save, tmp, save+MDB_IDL_UM_SIZE );
+
+ if ( rc != 0 ) {
+ if ( ftype == LDAP_COMP_FILTER_AND ) {
+ rc = 0;
+ continue;
+ }
+ break;
+ }
+
+ if ( ftype == LDAP_COMP_FILTER_AND ) {
+ if ( f == flist ) {
+ MDB_IDL_CPY( ids, save );
+ } else {
+ mdb_idl_intersection( ids, save );
+ }
+ if( MDB_IDL_IS_ZERO( ids ) )
+ break;
+ } else {
+ if ( f == flist ) {
+ MDB_IDL_CPY( ids, save );
+ } else {
+ mdb_idl_union( ids, save );
+ }
+ }
+ }
+
+ if( rc == LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_FILTER,
+ "<= comp_list_candidates: id=%ld first=%ld last=%ld\n",
+ (long) ids[0],
+ (long) MDB_IDL_FIRST(ids),
+ (long) MDB_IDL_LAST(ids) );
+
+ } else {
+ Debug( LDAP_DEBUG_FILTER,
+ "<= comp_list_candidates: undefined rc=%d\n",
+ rc, 0, 0 );
+ }
+
+ return rc;
+}
+
+static int
+comp_equality_candidates (
+ Operation *op,
+ MDB_txn *rtxn,
+ MatchingRuleAssertion *mra,
+ ComponentAssertion *ca,
+ ID *ids,
+ ID *tmp,
+ ID *stack)
+{
+ MDB_dbi dbi;
+ int i;
+ int rc;
+ slap_mask_t mask;
+ struct berval prefix = {0, NULL};
+ struct berval *keys = NULL;
+ MatchingRule *mr = mra->ma_rule;
+ Syntax *sat_syntax;
+ ComponentReference* cr_list, *cr;
+ AttrInfo *ai;
+
+ MDB_IDL_ALL( ids );
+
+ if ( !ca->ca_comp_ref )
+ return 0;
+
+ ai = mdb_attr_mask( op->o_bd->be_private, mra->ma_desc );
+ if( ai ) {
+ cr_list = ai->ai_cr;
+ }
+ else {
+ return 0;
+ }
+ /* find a component reference to be indexed */
+ sat_syntax = ca->ca_ma_rule->smr_syntax;
+ for ( cr = cr_list ; cr ; cr = cr->cr_next ) {
+ if ( cr->cr_string.bv_len == ca->ca_comp_ref->cr_string.bv_len &&
+ strncmp( cr->cr_string.bv_val, ca->ca_comp_ref->cr_string.bv_val,cr->cr_string.bv_len ) == 0 )
+ break;
+ }
+
+ if ( !cr )
+ return 0;
+
+ rc = mdb_index_param( op->o_bd, mra->ma_desc, LDAP_FILTER_EQUALITY,
+ &dbi, &mask, &prefix );
+
+ if( rc != LDAP_SUCCESS ) {
+ return 0;
+ }
+
+ if( !mr ) {
+ return 0;
+ }
+
+ if( !mr->smr_filter ) {
+ return 0;
+ }
+
+ rc = (ca->ca_ma_rule->smr_filter)(
+ LDAP_FILTER_EQUALITY,
+ cr->cr_indexmask,
+ sat_syntax,
+ ca->ca_ma_rule,
+ &prefix,
+ &ca->ca_ma_value,
+ &keys, op->o_tmpmemctx );
+
+ if( rc != LDAP_SUCCESS ) {
+ return 0;
+ }
+
+ if( keys == NULL ) {
+ return 0;
+ }
+ for ( i= 0; keys[i].bv_val != NULL; i++ ) {
+ rc = mdb_key_read( op->o_bd, rtxn, dbi, &keys[i], tmp, NULL, 0 );
+
+ if( rc == MDB_NOTFOUND ) {
+ MDB_IDL_ZERO( ids );
+ rc = 0;
+ break;
+ } else if( rc != LDAP_SUCCESS ) {
+ break;
+ }
+
+ if( MDB_IDL_IS_ZERO( tmp ) ) {
+ MDB_IDL_ZERO( ids );
+ break;
+ }
+
+ if ( i == 0 ) {
+ MDB_IDL_CPY( ids, tmp );
+ } else {
+ mdb_idl_intersection( ids, tmp );
+ }
+
+ if( MDB_IDL_IS_ZERO( ids ) )
+ break;
+ }
+ ber_bvarray_free_x( keys, op->o_tmpmemctx );
+
+ Debug( LDAP_DEBUG_TRACE,
+ "<= comp_equality_candidates: id=%ld, first=%ld, last=%ld\n",
+ (long) ids[0],
+ (long) MDB_IDL_FIRST(ids),
+ (long) MDB_IDL_LAST(ids) );
+ return( rc );
+}
+
+static int
+ava_comp_candidates (
+ Operation *op,
+ MDB_txn *rtxn,
+ AttributeAssertion *ava,
+ AttributeAliasing *aa,
+ ID *ids,
+ ID *tmp,
+ ID *stack )
+{
+ MatchingRuleAssertion mra;
+
+ mra.ma_rule = ava->aa_desc->ad_type->sat_equality;
+ if ( !mra.ma_rule ) {
+ MDB_IDL_ALL( ids );
+ return 0;
+ }
+ mra.ma_desc = aa->aa_aliased_ad;
+ mra.ma_rule = ava->aa_desc->ad_type->sat_equality;
+
+ return comp_candidates ( op, rtxn, &mra, ava->aa_cf, ids, tmp, stack );
+}
+
+static int
+comp_candidates (
+ Operation *op,
+ MDB_txn *rtxn,
+ MatchingRuleAssertion *mra,
+ ComponentFilter *f,
+ ID *ids,
+ ID *tmp,
+ ID *stack)
+{
+ int rc;
+
+ if ( !f ) return LDAP_PROTOCOL_ERROR;
+
+ Debug( LDAP_DEBUG_FILTER, "comp_candidates\n", 0, 0, 0 );
+ switch ( f->cf_choice ) {
+ case SLAPD_FILTER_COMPUTED:
+ rc = f->cf_result;
+ break;
+ case LDAP_COMP_FILTER_AND:
+ rc = comp_list_candidates( op, rtxn, mra, f->cf_and, LDAP_COMP_FILTER_AND, ids, tmp, stack );
+ break;
+ case LDAP_COMP_FILTER_OR:
+ rc = comp_list_candidates( op, rtxn, mra, f->cf_or, LDAP_COMP_FILTER_OR, ids, tmp, stack );
+ break;
+ case LDAP_COMP_FILTER_NOT:
+ /* No component indexing supported for NOT filter */
+ Debug( LDAP_DEBUG_FILTER, "\tComponent NOT\n", 0, 0, 0 );
+ MDB_IDL_ALL( ids );
+ rc = LDAP_PROTOCOL_ERROR;
+ break;
+ case LDAP_COMP_FILTER_ITEM:
+ rc = comp_equality_candidates( op, rtxn, mra, f->cf_ca, ids, tmp, stack );
+ break;
+ default:
+ MDB_IDL_ALL( ids );
+ rc = LDAP_PROTOCOL_ERROR;
+ }
+
+ return( rc );
+}
+#endif
+
+static int
+ext_candidates(
+ Operation *op,
+ MDB_txn *rtxn,
+ MatchingRuleAssertion *mra,
+ ID *ids,
+ ID *tmp,
+ ID *stack)
+{
+#ifdef LDAP_COMP_MATCH
+ /*
+ * Currently Only Component Indexing for componentFilterMatch is supported
+ * Indexing for an extensible filter is not supported yet
+ */
+ if ( mra->ma_cf ) {
+ return comp_candidates ( op, rtxn, mra, mra->ma_cf, ids, tmp, stack);
+ }
+#endif
+ if ( mra->ma_desc == slap_schema.si_ad_entryDN ) {
+ int rc;
+ ID id;
+
+ MDB_IDL_ZERO( ids );
+ if ( mra->ma_rule == slap_schema.si_mr_distinguishedNameMatch ) {
+base:
+ rc = mdb_dn2id( op, rtxn, &mra->ma_value, &id, NULL, NULL );
+ if ( rc == MDB_SUCCESS ) {
+ mdb_idl_insert( ids, id );
+ }
+ return 0;
+ } else if ( mra->ma_rule && mra->ma_rule->smr_match ==
+ dnRelativeMatch && dnIsSuffix( &mra->ma_value,
+ op->o_bd->be_nsuffix )) {
+ int scope;
+ if ( mra->ma_rule == slap_schema.si_mr_dnSuperiorMatch ) {
+ mdb_dn2sups( op, rtxn, &mra->ma_value, ids );
+ return 0;
+ }
+ if ( mra->ma_rule == slap_schema.si_mr_dnSubtreeMatch )
+ scope = LDAP_SCOPE_SUBTREE;
+ else if ( mra->ma_rule == slap_schema.si_mr_dnOneLevelMatch )
+ scope = LDAP_SCOPE_ONELEVEL;
+ else if ( mra->ma_rule == slap_schema.si_mr_dnSubordinateMatch )
+ scope = LDAP_SCOPE_SUBORDINATE;
+ else
+ goto base; /* scope = LDAP_SCOPE_BASE; */
+#if 0
+ if ( scope > LDAP_SCOPE_BASE ) {
+ ei = NULL;
+ rc = mdb_cache_find_ndn( op, rtxn, &mra->ma_value, &ei );
+ if ( ei )
+ mdb_cache_entryinfo_unlock( ei );
+ if ( rc == LDAP_SUCCESS ) {
+ int sc = op->ors_scope;
+ op->ors_scope = scope;
+ rc = mdb_dn2idl( op, rtxn, &mra->ma_value, ei, ids,
+ stack );
+ op->ors_scope = sc;
+ }
+ return 0;
+ }
+#endif
+ }
+ }
+
+ MDB_IDL_ALL( ids );
+ return 0;
+}
+
+static int
+list_candidates(
+ Operation *op,
+ MDB_txn *rtxn,
+ Filter *flist,
+ int ftype,
+ ID *ids,
+ ID *tmp,
+ ID *save )
+{
+ int rc = 0;
+ Filter *f;
+
+ Debug( LDAP_DEBUG_FILTER, "=> mdb_list_candidates 0x%x\n", ftype, 0, 0 );
+ for ( f = flist; f != NULL; f = f->f_next ) {
+ /* ignore precomputed scopes */
+ if ( f->f_choice == SLAPD_FILTER_COMPUTED &&
+ f->f_result == LDAP_SUCCESS ) {
+ continue;
+ }
+ MDB_IDL_ZERO( save );
+ rc = mdb_filter_candidates( op, rtxn, f, save, tmp,
+ save+MDB_IDL_UM_SIZE );
+
+ if ( rc != 0 ) {
+ if ( ftype == LDAP_FILTER_AND ) {
+ rc = 0;
+ continue;
+ }
+ break;
+ }
+
+
+ if ( ftype == LDAP_FILTER_AND ) {
+ if ( f == flist ) {
+ MDB_IDL_CPY( ids, save );
+ } else {
+ mdb_idl_intersection( ids, save );
+ }
+ if( MDB_IDL_IS_ZERO( ids ) )
+ break;
+ } else {
+ if ( f == flist ) {
+ MDB_IDL_CPY( ids, save );
+ } else {
+ mdb_idl_union( ids, save );
+ }
+ }
+ }
+
+ if( rc == LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_FILTER,
+ "<= mdb_list_candidates: id=%ld first=%ld last=%ld\n",
+ (long) ids[0],
+ (long) MDB_IDL_FIRST(ids),
+ (long) MDB_IDL_LAST(ids) );
+
+ } else {
+ Debug( LDAP_DEBUG_FILTER,
+ "<= mdb_list_candidates: undefined rc=%d\n",
+ rc, 0, 0 );
+ }
+
+ return rc;
+}
+
+static int
+presence_candidates(
+ Operation *op,
+ MDB_txn *rtxn,
+ AttributeDescription *desc,
+ ID *ids )
+{
+ MDB_dbi dbi;
+ int rc;
+ slap_mask_t mask;
+ struct berval prefix = {0, NULL};
+
+ Debug( LDAP_DEBUG_TRACE, "=> mdb_presence_candidates (%s)\n",
+ desc->ad_cname.bv_val, 0, 0 );
+
+ MDB_IDL_ALL( ids );
+
+ if( desc == slap_schema.si_ad_objectClass ) {
+ return 0;
+ }
+
+ rc = mdb_index_param( op->o_bd, desc, LDAP_FILTER_PRESENT,
+ &dbi, &mask, &prefix );
+
+ if( rc == LDAP_INAPPROPRIATE_MATCHING ) {
+ /* not indexed */
+ Debug( LDAP_DEBUG_TRACE,
+ "<= mdb_presence_candidates: (%s) not indexed\n",
+ desc->ad_cname.bv_val, 0, 0 );
+ return 0;
+ }
+
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= mdb_presence_candidates: (%s) index_param "
+ "returned=%d\n",
+ desc->ad_cname.bv_val, rc, 0 );
+ return 0;
+ }
+
+ if( prefix.bv_val == NULL ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= mdb_presence_candidates: (%s) no prefix\n",
+ desc->ad_cname.bv_val, 0, 0 );
+ return -1;
+ }
+
+ rc = mdb_key_read( op->o_bd, rtxn, dbi, &prefix, ids, NULL, 0 );
+
+ if( rc == MDB_NOTFOUND ) {
+ MDB_IDL_ZERO( ids );
+ rc = 0;
+ } else if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= mdb_presense_candidates: (%s) "
+ "key read failed (%d)\n",
+ desc->ad_cname.bv_val, rc, 0 );
+ goto done;
+ }
+
+ Debug(LDAP_DEBUG_TRACE,
+ "<= mdb_presence_candidates: id=%ld first=%ld last=%ld\n",
+ (long) ids[0],
+ (long) MDB_IDL_FIRST(ids),
+ (long) MDB_IDL_LAST(ids) );
+
+done:
+ return rc;
+}
+
+static int
+equality_candidates(
+ Operation *op,
+ MDB_txn *rtxn,
+ AttributeAssertion *ava,
+ ID *ids,
+ ID *tmp )
+{
+ MDB_dbi dbi;
+ int i;
+ int rc;
+ slap_mask_t mask;
+ struct berval prefix = {0, NULL};
+ struct berval *keys = NULL;
+ MatchingRule *mr;
+
+ Debug( LDAP_DEBUG_TRACE, "=> mdb_equality_candidates (%s)\n",
+ ava->aa_desc->ad_cname.bv_val, 0, 0 );
+
+ if ( ava->aa_desc == slap_schema.si_ad_entryDN ) {
+ ID id;
+ rc = mdb_dn2id( op, rtxn, &ava->aa_value, &id, NULL, NULL );
+ if ( rc == LDAP_SUCCESS ) {
+ /* exactly one ID can match */
+ ids[0] = 1;
+ ids[1] = id;
+ }
+ return rc;
+ }
+
+ MDB_IDL_ALL( ids );
+
+ rc = mdb_index_param( op->o_bd, ava->aa_desc, LDAP_FILTER_EQUALITY,
+ &dbi, &mask, &prefix );
+
+ if ( rc == LDAP_INAPPROPRIATE_MATCHING ) {
+ Debug( LDAP_DEBUG_ANY,
+ "<= mdb_equality_candidates: (%s) not indexed\n",
+ ava->aa_desc->ad_cname.bv_val, 0, 0 );
+ return 0;
+ }
+
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "<= mdb_equality_candidates: (%s) "
+ "index_param failed (%d)\n",
+ ava->aa_desc->ad_cname.bv_val, rc, 0 );
+ return 0;
+ }
+
+ mr = ava->aa_desc->ad_type->sat_equality;
+ if( !mr ) {
+ return 0;
+ }
+
+ if( !mr->smr_filter ) {
+ return 0;
+ }
+
+ rc = (mr->smr_filter)(
+ LDAP_FILTER_EQUALITY,
+ mask,
+ ava->aa_desc->ad_type->sat_syntax,
+ mr,
+ &prefix,
+ &ava->aa_value,
+ &keys, op->o_tmpmemctx );
+
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= mdb_equality_candidates: (%s, %s) "
+ "MR filter failed (%d)\n",
+ prefix.bv_val, ava->aa_desc->ad_cname.bv_val, rc );
+ return 0;
+ }
+
+ if( keys == NULL ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= mdb_equality_candidates: (%s) no keys\n",
+ ava->aa_desc->ad_cname.bv_val, 0, 0 );
+ return 0;
+ }
+
+ for ( i= 0; keys[i].bv_val != NULL; i++ ) {
+ rc = mdb_key_read( op->o_bd, rtxn, dbi, &keys[i], tmp, NULL, 0 );
+
+ if( rc == MDB_NOTFOUND ) {
+ MDB_IDL_ZERO( ids );
+ rc = 0;
+ break;
+ } else if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= mdb_equality_candidates: (%s) "
+ "key read failed (%d)\n",
+ ava->aa_desc->ad_cname.bv_val, rc, 0 );
+ break;
+ }
+
+ if( MDB_IDL_IS_ZERO( tmp ) ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= mdb_equality_candidates: (%s) NULL\n",
+ ava->aa_desc->ad_cname.bv_val, 0, 0 );
+ MDB_IDL_ZERO( ids );
+ break;
+ }
+
+ if ( i == 0 ) {
+ MDB_IDL_CPY( ids, tmp );
+ } else {
+ mdb_idl_intersection( ids, tmp );
+ }
+
+ if( MDB_IDL_IS_ZERO( ids ) )
+ break;
+ }
+
+ ber_bvarray_free_x( keys, op->o_tmpmemctx );
+
+ Debug( LDAP_DEBUG_TRACE,
+ "<= mdb_equality_candidates: id=%ld, first=%ld, last=%ld\n",
+ (long) ids[0],
+ (long) MDB_IDL_FIRST(ids),
+ (long) MDB_IDL_LAST(ids) );
+ return( rc );
+}
+
+
+static int
+approx_candidates(
+ Operation *op,
+ MDB_txn *rtxn,
+ AttributeAssertion *ava,
+ ID *ids,
+ ID *tmp )
+{
+ MDB_dbi dbi;
+ int i;
+ int rc;
+ slap_mask_t mask;
+ struct berval prefix = {0, NULL};
+ struct berval *keys = NULL;
+ MatchingRule *mr;
+
+ Debug( LDAP_DEBUG_TRACE, "=> mdb_approx_candidates (%s)\n",
+ ava->aa_desc->ad_cname.bv_val, 0, 0 );
+
+ MDB_IDL_ALL( ids );
+
+ rc = mdb_index_param( op->o_bd, ava->aa_desc, LDAP_FILTER_APPROX,
+ &dbi, &mask, &prefix );
+
+ if ( rc == LDAP_INAPPROPRIATE_MATCHING ) {
+ Debug( LDAP_DEBUG_ANY,
+ "<= mdb_approx_candidates: (%s) not indexed\n",
+ ava->aa_desc->ad_cname.bv_val, 0, 0 );
+ return 0;
+ }
+
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "<= mdb_approx_candidates: (%s) "
+ "index_param failed (%d)\n",
+ ava->aa_desc->ad_cname.bv_val, rc, 0 );
+ return 0;
+ }
+
+ mr = ava->aa_desc->ad_type->sat_approx;
+ if( !mr ) {
+ /* no approx matching rule, try equality matching rule */
+ mr = ava->aa_desc->ad_type->sat_equality;
+ }
+
+ if( !mr ) {
+ return 0;
+ }
+
+ if( !mr->smr_filter ) {
+ return 0;
+ }
+
+ rc = (mr->smr_filter)(
+ LDAP_FILTER_APPROX,
+ mask,
+ ava->aa_desc->ad_type->sat_syntax,
+ mr,
+ &prefix,
+ &ava->aa_value,
+ &keys, op->o_tmpmemctx );
+
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= mdb_approx_candidates: (%s, %s) "
+ "MR filter failed (%d)\n",
+ prefix.bv_val, ava->aa_desc->ad_cname.bv_val, rc );
+ return 0;
+ }
+
+ if( keys == NULL ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= mdb_approx_candidates: (%s) no keys (%s)\n",
+ prefix.bv_val, ava->aa_desc->ad_cname.bv_val, 0 );
+ return 0;
+ }
+
+ for ( i= 0; keys[i].bv_val != NULL; i++ ) {
+ rc = mdb_key_read( op->o_bd, rtxn, dbi, &keys[i], tmp, NULL, 0 );
+
+ if( rc == MDB_NOTFOUND ) {
+ MDB_IDL_ZERO( ids );
+ rc = 0;
+ break;
+ } else if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= mdb_approx_candidates: (%s) "
+ "key read failed (%d)\n",
+ ava->aa_desc->ad_cname.bv_val, rc, 0 );
+ break;
+ }
+
+ if( MDB_IDL_IS_ZERO( tmp ) ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= mdb_approx_candidates: (%s) NULL\n",
+ ava->aa_desc->ad_cname.bv_val, 0, 0 );
+ MDB_IDL_ZERO( ids );
+ break;
+ }
+
+ if ( i == 0 ) {
+ MDB_IDL_CPY( ids, tmp );
+ } else {
+ mdb_idl_intersection( ids, tmp );
+ }
+
+ if( MDB_IDL_IS_ZERO( ids ) )
+ break;
+ }
+
+ ber_bvarray_free_x( keys, op->o_tmpmemctx );
+
+ Debug( LDAP_DEBUG_TRACE, "<= mdb_approx_candidates %ld, first=%ld, last=%ld\n",
+ (long) ids[0],
+ (long) MDB_IDL_FIRST(ids),
+ (long) MDB_IDL_LAST(ids) );
+ return( rc );
+}
+
+static int
+substring_candidates(
+ Operation *op,
+ MDB_txn *rtxn,
+ SubstringsAssertion *sub,
+ ID *ids,
+ ID *tmp )
+{
+ MDB_dbi dbi;
+ int i;
+ int rc;
+ slap_mask_t mask;
+ struct berval prefix = {0, NULL};
+ struct berval *keys = NULL;
+ MatchingRule *mr;
+
+ Debug( LDAP_DEBUG_TRACE, "=> mdb_substring_candidates (%s)\n",
+ sub->sa_desc->ad_cname.bv_val, 0, 0 );
+
+ MDB_IDL_ALL( ids );
+
+ rc = mdb_index_param( op->o_bd, sub->sa_desc, LDAP_FILTER_SUBSTRINGS,
+ &dbi, &mask, &prefix );
+
+ if ( rc == LDAP_INAPPROPRIATE_MATCHING ) {
+ Debug( LDAP_DEBUG_ANY,
+ "<= mdb_substring_candidates: (%s) not indexed\n",
+ sub->sa_desc->ad_cname.bv_val, 0, 0 );
+ return 0;
+ }
+
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "<= mdb_substring_candidates: (%s) "
+ "index_param failed (%d)\n",
+ sub->sa_desc->ad_cname.bv_val, rc, 0 );
+ return 0;
+ }
+
+ mr = sub->sa_desc->ad_type->sat_substr;
+
+ if( !mr ) {
+ return 0;
+ }
+
+ if( !mr->smr_filter ) {
+ return 0;
+ }
+
+ rc = (mr->smr_filter)(
+ LDAP_FILTER_SUBSTRINGS,
+ mask,
+ sub->sa_desc->ad_type->sat_syntax,
+ mr,
+ &prefix,
+ sub,
+ &keys, op->o_tmpmemctx );
+
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= mdb_substring_candidates: (%s) "
+ "MR filter failed (%d)\n",
+ sub->sa_desc->ad_cname.bv_val, rc, 0 );
+ return 0;
+ }
+
+ if( keys == NULL ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= mdb_substring_candidates: (0x%04lx) no keys (%s)\n",
+ mask, sub->sa_desc->ad_cname.bv_val, 0 );
+ return 0;
+ }
+
+ for ( i= 0; keys[i].bv_val != NULL; i++ ) {
+ rc = mdb_key_read( op->o_bd, rtxn, dbi, &keys[i], tmp, NULL, 0 );
+
+ if( rc == MDB_NOTFOUND ) {
+ MDB_IDL_ZERO( ids );
+ rc = 0;
+ break;
+ } else if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= mdb_substring_candidates: (%s) "
+ "key read failed (%d)\n",
+ sub->sa_desc->ad_cname.bv_val, rc, 0 );
+ break;
+ }
+
+ if( MDB_IDL_IS_ZERO( tmp ) ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= mdb_substring_candidates: (%s) NULL\n",
+ sub->sa_desc->ad_cname.bv_val, 0, 0 );
+ MDB_IDL_ZERO( ids );
+ break;
+ }
+
+ if ( i == 0 ) {
+ MDB_IDL_CPY( ids, tmp );
+ } else {
+ mdb_idl_intersection( ids, tmp );
+ }
+
+ if( MDB_IDL_IS_ZERO( ids ) )
+ break;
+ }
+
+ ber_bvarray_free_x( keys, op->o_tmpmemctx );
+
+ Debug( LDAP_DEBUG_TRACE, "<= mdb_substring_candidates: %ld, first=%ld, last=%ld\n",
+ (long) ids[0],
+ (long) MDB_IDL_FIRST(ids),
+ (long) MDB_IDL_LAST(ids) );
+ return( rc );
+}
+
+static int
+inequality_candidates(
+ Operation *op,
+ MDB_txn *rtxn,
+ AttributeAssertion *ava,
+ ID *ids,
+ ID *tmp,
+ int gtorlt )
+{
+ MDB_dbi dbi;
+ int rc;
+ slap_mask_t mask;
+ struct berval prefix = {0, NULL};
+ struct berval *keys = NULL;
+ MatchingRule *mr;
+ MDB_cursor *cursor = NULL;
+
+ Debug( LDAP_DEBUG_TRACE, "=> mdb_inequality_candidates (%s)\n",
+ ava->aa_desc->ad_cname.bv_val, 0, 0 );
+
+ MDB_IDL_ALL( ids );
+
+ rc = mdb_index_param( op->o_bd, ava->aa_desc, LDAP_FILTER_EQUALITY,
+ &dbi, &mask, &prefix );
+
+ if ( rc == LDAP_INAPPROPRIATE_MATCHING ) {
+ Debug( LDAP_DEBUG_ANY,
+ "<= mdb_inequality_candidates: (%s) not indexed\n",
+ ava->aa_desc->ad_cname.bv_val, 0, 0 );
+ return 0;
+ }
+
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "<= mdb_inequality_candidates: (%s) "
+ "index_param failed (%d)\n",
+ ava->aa_desc->ad_cname.bv_val, rc, 0 );
+ return 0;
+ }
+
+ mr = ava->aa_desc->ad_type->sat_equality;
+ if( !mr ) {
+ return 0;
+ }
+
+ if( !mr->smr_filter ) {
+ return 0;
+ }
+
+ rc = (mr->smr_filter)(
+ LDAP_FILTER_EQUALITY,
+ mask,
+ ava->aa_desc->ad_type->sat_syntax,
+ mr,
+ &prefix,
+ &ava->aa_value,
+ &keys, op->o_tmpmemctx );
+
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= mdb_inequality_candidates: (%s, %s) "
+ "MR filter failed (%d)\n",
+ prefix.bv_val, ava->aa_desc->ad_cname.bv_val, rc );
+ return 0;
+ }
+
+ if( keys == NULL ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= mdb_inequality_candidates: (%s) no keys\n",
+ ava->aa_desc->ad_cname.bv_val, 0, 0 );
+ return 0;
+ }
+
+ MDB_IDL_ZERO( ids );
+ while(1) {
+ rc = mdb_key_read( op->o_bd, rtxn, dbi, &keys[0], tmp, &cursor, gtorlt );
+
+ if( rc == MDB_NOTFOUND ) {
+ rc = 0;
+ break;
+ } else if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= mdb_inequality_candidates: (%s) "
+ "key read failed (%d)\n",
+ ava->aa_desc->ad_cname.bv_val, rc, 0 );
+ break;
+ }
+
+ if( MDB_IDL_IS_ZERO( tmp ) ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= mdb_inequality_candidates: (%s) NULL\n",
+ ava->aa_desc->ad_cname.bv_val, 0, 0 );
+ break;
+ }
+
+ mdb_idl_union( ids, tmp );
+
+ if( op->ors_limit && op->ors_limit->lms_s_unchecked != -1 &&
+ MDB_IDL_N( ids ) >= (unsigned) op->ors_limit->lms_s_unchecked ) {
+ mdb_cursor_close( cursor );
+ break;
+ }
+ }
+ ber_bvarray_free_x( keys, op->o_tmpmemctx );
+
+ Debug( LDAP_DEBUG_TRACE,
+ "<= mdb_inequality_candidates: id=%ld, first=%ld, last=%ld\n",
+ (long) ids[0],
+ (long) MDB_IDL_FIRST(ids),
+ (long) MDB_IDL_LAST(ids) );
+ return( rc );
+}
--- /dev/null
+/* id2entry.c - routines to deal with the id2entry database */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2011 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include <ac/errno.h>
+
+#include "back-mdb.h"
+
+static int mdb_id2entry_put(
+ Operation *op,
+ MDB_txn *tid,
+ Entry *e,
+ int flag )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ MDB_dbi dbi = mdb->mi_id2entry;
+ MDB_val key, data;
+ struct berval bv;
+ int rc;
+ struct berval odn, ondn;
+
+ /* We only store rdns, and they go in the dn2id database. */
+
+ odn = e->e_name; ondn = e->e_nname;
+
+ e->e_name = slap_empty_bv;
+ e->e_nname = slap_empty_bv;
+
+ key.mv_data = &e->e_id;
+ key.mv_size = sizeof(ID);
+
+ rc = entry_encode( e, &bv );
+ e->e_name = odn; e->e_nname = ondn;
+ if( rc != LDAP_SUCCESS ) {
+ return -1;
+ }
+
+ data.mv_size = bv.bv_len;
+ data.mv_data = bv.bv_val;
+
+ rc = mdb_put( tid, dbi, &key, &data, flag );
+
+ op->o_tmpfree( bv.bv_val, op->o_tmpmemctx );
+ return rc;
+}
+
+/*
+ * This routine adds (or updates) an entry on disk.
+ * The cache should be already be updated.
+ */
+
+
+int mdb_id2entry_add(
+ Operation *op,
+ MDB_txn *tid,
+ Entry *e )
+{
+ return mdb_id2entry_put(op, tid, e, MDB_NOOVERWRITE);
+}
+
+int mdb_id2entry_update(
+ Operation *op,
+ MDB_txn *tid,
+ Entry *e )
+{
+ return mdb_id2entry_put(op, tid, e, 0);
+}
+
+int mdb_id2entry(
+ Operation *op,
+ MDB_txn *tid,
+ ID id,
+ Entry **e )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ MDB_dbi dbi = mdb->mi_id2entry;
+ MDB_val key, data;
+ EntryHeader eh;
+ int rc = 0;
+
+ *e = NULL;
+
+ key.mv_data = &id;
+ key.mv_size = sizeof(ID);
+
+ /* fetch it */
+ rc = mdb_get( tid, dbi, &key, &data );
+ if ( rc == MDB_NOTFOUND ) {
+ /* Looking for root entry on an empty-dn suffix? */
+ if ( !id && BER_BVISEMPTY( &op->o_bd->be_nsuffix[0] )) {
+ struct berval gluebv = BER_BVC("glue");
+ Entry *r = entry_alloc();
+
+ r->e_id = 0;
+ attr_merge_one( r, slap_schema.si_ad_objectClass, &gluebv, NULL );
+ attr_merge_one( r, slap_schema.si_ad_structuralObjectClass, &gluebv, NULL );
+ r->e_ocflags = SLAP_OC_GLUE|SLAP_OC__END;
+ *e = r;
+ return MDB_SUCCESS;
+ }
+ }
+ if ( rc ) return rc;
+
+ eh.bv.bv_val = data.mv_data;
+ eh.bv.bv_len = data.mv_size;
+ rc = entry_header( &eh );
+ if ( rc ) return rc;
+
+ if ( eh.nvals ) {
+ eh.bv.bv_len = eh.nvals * sizeof( struct berval );
+ eh.bv.bv_val = ch_malloc( eh.bv.bv_len );
+ rc = entry_decode(&eh, e);
+ } else {
+ *e = entry_alloc();
+ }
+
+ if( rc == 0 ) {
+ (*e)->e_id = id;
+ (*e)->e_name.bv_val = NULL;
+ (*e)->e_nname.bv_val = NULL;
+ } else {
+ ch_free( eh.bv.bv_val );
+ }
+
+ return rc;
+}
+
+int mdb_id2entry_delete(
+ BackendDB *be,
+ MDB_txn *tid,
+ Entry *e )
+{
+ struct mdb_info *mdb = (struct mdb_info *) be->be_private;
+ MDB_dbi dbi = mdb->mi_id2entry;
+ MDB_val key;
+ int rc;
+
+ key.mv_data = &e->e_id;
+ key.mv_size = sizeof(ID);
+
+ /* delete from database */
+ rc = mdb_del( tid, dbi, &key, NULL );
+
+ return rc;
+}
+
+int mdb_entry_return(
+ Entry *e
+)
+{
+ entry_free( e );
+ return 0;
+}
+
+int mdb_entry_release(
+ Operation *op,
+ Entry *e,
+ int rw )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ struct mdb_op_info *moi = NULL;
+ int rc;
+
+ /* slapMode : SLAP_SERVER_MODE, SLAP_TOOL_MODE,
+ SLAP_TRUNCATE_MODE, SLAP_UNDEFINED_MODE */
+
+ mdb_entry_return ( e );
+ if ( slapMode == SLAP_SERVER_MODE ) {
+ OpExtra *oex;
+ LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
+ if ( oex->oe_key == mdb ) {
+ moi = (mdb_op_info *)oex;
+ /* If it was setup by entry_get we should probably free it */
+ if ( moi->moi_flag & MOI_FREEIT ) {
+ moi->moi_ref--;
+ if ( moi->moi_ref < 1 ) {
+ mdb_txn_reset( moi->moi_txn );
+ moi->moi_ref = 0;
+ LDAP_SLIST_REMOVE( &op->o_extra, &moi->moi_oe, OpExtra, oe_next );
+ op->o_tmpfree( moi, op->o_tmpmemctx );
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* return LDAP_SUCCESS IFF we can retrieve the specified entry.
+ */
+int mdb_entry_get(
+ Operation *op,
+ struct berval *ndn,
+ ObjectClass *oc,
+ AttributeDescription *at,
+ int rw,
+ Entry **ent )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ struct mdb_op_info *moi = NULL;
+ MDB_txn *txn = NULL;
+ Entry *e = NULL;
+ int rc;
+ const char *at_name = at ? at->ad_cname.bv_val : "(null)";
+
+ Debug( LDAP_DEBUG_ARGS,
+ "=> mdb_entry_get: ndn: \"%s\"\n", ndn->bv_val, 0, 0 );
+ Debug( LDAP_DEBUG_ARGS,
+ "=> mdb_entry_get: oc: \"%s\", at: \"%s\"\n",
+ oc ? oc->soc_cname.bv_val : "(null)", at_name, 0);
+
+ rc = mdb_opinfo_get( op, mdb, rw == 0, &moi );
+ if ( rc )
+ return LDAP_OTHER;
+ txn = moi->moi_txn;
+
+ /* can we find entry */
+ rc = mdb_dn2entry( op, txn, ndn, &e, 0 );
+ switch( rc ) {
+ case MDB_NOTFOUND:
+ case 0:
+ break;
+ default:
+ return (rc != LDAP_BUSY) ? LDAP_OTHER : LDAP_BUSY;
+ }
+ if (e == NULL) {
+ Debug( LDAP_DEBUG_ACL,
+ "=> mdb_entry_get: cannot find entry: \"%s\"\n",
+ ndn->bv_val, 0, 0 );
+ rc = LDAP_NO_SUCH_OBJECT;
+ goto return_results;
+ }
+
+ Debug( LDAP_DEBUG_ACL,
+ "=> mdb_entry_get: found entry: \"%s\"\n",
+ ndn->bv_val, 0, 0 );
+
+ if ( oc && !is_entry_objectclass( e, oc, 0 )) {
+ Debug( LDAP_DEBUG_ACL,
+ "<= mdb_entry_get: failed to find objectClass %s\n",
+ oc->soc_cname.bv_val, 0, 0 );
+ rc = LDAP_NO_SUCH_ATTRIBUTE;
+ goto return_results;
+ }
+
+ /* NOTE: attr_find() or attrs_find()? */
+ if ( at && attr_find( e->e_attrs, at ) == NULL ) {
+ Debug( LDAP_DEBUG_ACL,
+ "<= mdb_entry_get: failed to find attribute %s\n",
+ at->ad_cname.bv_val, 0, 0 );
+ rc = LDAP_NO_SUCH_ATTRIBUTE;
+ goto return_results;
+ }
+
+return_results:
+ if( rc != LDAP_SUCCESS ) {
+ /* free entry */
+ if ( e )
+ mdb_entry_return( e );
+
+ if (moi->moi_ref == 1) {
+ LDAP_SLIST_REMOVE( &op->o_extra, &moi->moi_oe, OpExtra, oe_next );
+ mdb_txn_reset( txn );
+ op->o_tmpfree( moi, op->o_tmpmemctx );
+ }
+ } else {
+ *ent = e;
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ "mdb_entry_get: rc=%d\n",
+ rc, 0, 0 );
+ return(rc);
+}
+
+static void
+mdb_reader_free( void *key, void *data )
+{
+ MDB_txn *txn = data;
+
+ if ( txn ) mdb_txn_abort( txn );
+}
+
+/* free up any keys used by the main thread */
+void
+mdb_reader_flush( MDB_env *env )
+{
+ void *data;
+ void *ctx = ldap_pvt_thread_pool_context();
+
+ if ( !ldap_pvt_thread_pool_getkey( ctx, env, &data, NULL ) ) {
+ ldap_pvt_thread_pool_setkey( ctx, env, NULL, 0, NULL, NULL );
+ mdb_reader_free( env, data );
+ }
+}
+
+int
+mdb_opinfo_get( Operation *op, struct mdb_info *mdb, int rdonly, mdb_op_info **moip )
+{
+ int rc, renew = 0;
+ void *data;
+ void *ctx;
+ mdb_op_info *moi = NULL;
+ OpExtra *oex;
+
+ assert( op != NULL );
+
+ if ( !mdb || !moip ) return -1;
+
+ /* If no op was provided, try to find the ctx anyway... */
+ if ( op ) {
+ ctx = op->o_threadctx;
+ } else {
+ ctx = ldap_pvt_thread_pool_context();
+ }
+
+ if ( op ) {
+ LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
+ if ( oex->oe_key == mdb ) break;
+ }
+ moi = (mdb_op_info *)oex;
+ }
+
+ if ( !moi ) {
+ moi = *moip;
+
+ if ( !moi ) {
+ if ( op ) {
+ moi = op->o_tmpalloc(sizeof(struct mdb_op_info),op->o_tmpmemctx);
+ } else {
+ moi = ch_malloc(sizeof(mdb_op_info));
+ }
+ moi->moi_flag = MOI_FREEIT;
+ *moip = moi;
+ }
+ LDAP_SLIST_INSERT_HEAD( &op->o_extra, &moi->moi_oe, oe_next );
+ moi->moi_oe.oe_key = mdb;
+ moi->moi_ref = 0;
+ moi->moi_txn = NULL;
+ }
+
+ if ( !rdonly ) {
+ /* This op started as a reader, but now wants to write. */
+ if ( moi->moi_flag & MOI_READER ) {
+ moi = *moip;
+ LDAP_SLIST_INSERT_HEAD( &op->o_extra, &moi->moi_oe, oe_next );
+ } else {
+ /* This op is continuing an existing write txn */
+ *moip = moi;
+ }
+ moi->moi_ref++;
+ if ( !moi->moi_txn ) {
+ rc = mdb_txn_begin( mdb->mi_dbenv, 0, &moi->moi_txn );
+ if (rc) {
+ Debug( LDAP_DEBUG_ANY, "mdb_opinfo_get: err %s(%d)\n",
+ mdb_strerror(rc), rc, 0 );
+ }
+ return rc;
+ }
+ return 0;
+ }
+
+ /* OK, this is a reader */
+ if ( !moi->moi_txn ) {
+ if ( !ctx ) {
+ /* Shouldn't happen unless we're single-threaded */
+ rc = mdb_txn_begin( mdb->mi_dbenv, MDB_RDONLY, &moi->moi_txn );
+ if (rc) {
+ Debug( LDAP_DEBUG_ANY, "mdb_opinfo_get: err %s(%d)\n",
+ mdb_strerror(rc), rc, 0 );
+ }
+ return rc;
+ }
+ if ( ldap_pvt_thread_pool_getkey( ctx, mdb->mi_dbenv, &data, NULL ) ) {
+ rc = mdb_txn_begin( mdb->mi_dbenv, MDB_RDONLY, &moi->moi_txn );
+ if (rc) {
+ Debug( LDAP_DEBUG_ANY, "mdb_opinfo_get: err %s(%d)\n",
+ mdb_strerror(rc), rc, 0 );
+ return rc;
+ }
+ data = moi->moi_txn;
+ if ( ( rc = ldap_pvt_thread_pool_setkey( ctx, mdb->mi_dbenv,
+ data, mdb_reader_free, NULL, NULL ) ) ) {
+ mdb_txn_abort( moi->moi_txn );
+ moi->moi_txn = NULL;
+ Debug( LDAP_DEBUG_ANY, "mdb_opinfo_get: thread_pool_setkey failed err (%d)\n",
+ rc, 0, 0 );
+ return rc;
+ }
+ } else {
+ moi->moi_txn = data;
+ renew = 1;
+ }
+ moi->moi_flag |= MOI_READER;
+ }
+ if ( moi->moi_ref < 1 ) {
+ moi->moi_ref = 0;
+ }
+ if ( renew ) {
+ mdb_txn_renew( moi->moi_txn );
+ }
+ moi->moi_ref++;
+ if ( *moip != moi )
+ *moip = moi;
+
+ return 0;
+}
--- /dev/null
+/* idl.c - ldap id list handling routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2011 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-mdb.h"
+#include "idl.h"
+
+#define IDL_MAX(x,y) ( x > y ? x : y )
+#define IDL_MIN(x,y) ( x < y ? x : y )
+
+#define IDL_CMP(x,y) ( x < y ? -1 : ( x > y ? 1 : 0 ) )
+
+#if IDL_DEBUG > 0
+static void idl_check( ID *ids )
+{
+ if( MDB_IDL_IS_RANGE( ids ) ) {
+ assert( MDB_IDL_RANGE_FIRST(ids) <= MDB_IDL_RANGE_LAST(ids) );
+ } else {
+ ID i;
+ for( i=1; i < ids[0]; i++ ) {
+ assert( ids[i+1] > ids[i] );
+ }
+ }
+}
+
+#if IDL_DEBUG > 1
+static void idl_dump( ID *ids )
+{
+ if( MDB_IDL_IS_RANGE( ids ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "IDL: range ( %ld - %ld )\n",
+ (long) MDB_IDL_RANGE_FIRST( ids ),
+ (long) MDB_IDL_RANGE_LAST( ids ) );
+
+ } else {
+ ID i;
+ Debug( LDAP_DEBUG_ANY, "IDL: size %ld", (long) ids[0], 0, 0 );
+
+ for( i=1; i<=ids[0]; i++ ) {
+ if( i % 16 == 1 ) {
+ Debug( LDAP_DEBUG_ANY, "\n", 0, 0, 0 );
+ }
+ Debug( LDAP_DEBUG_ANY, " %02lx", (long) ids[i], 0, 0 );
+ }
+
+ Debug( LDAP_DEBUG_ANY, "\n", 0, 0, 0 );
+ }
+
+ idl_check( ids );
+}
+#endif /* IDL_DEBUG > 1 */
+#endif /* IDL_DEBUG > 0 */
+
+unsigned mdb_idl_search( ID *ids, ID id )
+{
+#define IDL_BINARY_SEARCH 1
+#ifdef IDL_BINARY_SEARCH
+ /*
+ * binary search of id in ids
+ * if found, returns position of id
+ * if not found, returns first postion greater than id
+ */
+ unsigned base = 0;
+ unsigned cursor = 0;
+ int val = 0;
+ unsigned n = ids[0];
+
+#if IDL_DEBUG > 0
+ idl_check( ids );
+#endif
+
+ while( 0 < n ) {
+ int pivot = n >> 1;
+ cursor = base + pivot;
+ val = IDL_CMP( id, ids[cursor + 1] );
+
+ if( val < 0 ) {
+ n = pivot;
+
+ } else if ( val > 0 ) {
+ base = cursor + 1;
+ n -= pivot + 1;
+
+ } else {
+ return cursor + 1;
+ }
+ }
+
+ if( val > 0 ) {
+ return cursor + 2;
+ } else {
+ return cursor + 1;
+ }
+
+#else
+ /* (reverse) linear search */
+ int i;
+
+#if IDL_DEBUG > 0
+ idl_check( ids );
+#endif
+
+ for( i=ids[0]; i; i-- ) {
+ if( id > ids[i] ) {
+ break;
+ }
+ }
+
+ return i+1;
+#endif
+}
+
+int mdb_idl_insert( ID *ids, ID id )
+{
+ unsigned x;
+
+#if IDL_DEBUG > 1
+ Debug( LDAP_DEBUG_ANY, "insert: %04lx at %d\n", (long) id, x, 0 );
+ idl_dump( ids );
+#elif IDL_DEBUG > 0
+ idl_check( ids );
+#endif
+
+ if (MDB_IDL_IS_RANGE( ids )) {
+ /* if already in range, treat as a dup */
+ if (id >= MDB_IDL_FIRST(ids) && id <= MDB_IDL_LAST(ids))
+ return -1;
+ if (id < MDB_IDL_FIRST(ids))
+ ids[1] = id;
+ else if (id > MDB_IDL_LAST(ids))
+ ids[2] = id;
+ return 0;
+ }
+
+ x = mdb_idl_search( ids, id );
+ assert( x > 0 );
+
+ if( x < 1 ) {
+ /* internal error */
+ return -2;
+ }
+
+ if ( x <= ids[0] && ids[x] == id ) {
+ /* duplicate */
+ return -1;
+ }
+
+ if ( ++ids[0] >= MDB_IDL_DB_MAX ) {
+ if( id < ids[1] ) {
+ ids[1] = id;
+ ids[2] = ids[ids[0]-1];
+ } else if ( ids[ids[0]-1] < id ) {
+ ids[2] = id;
+ } else {
+ ids[2] = ids[ids[0]-1];
+ }
+ ids[0] = NOID;
+
+ } else {
+ /* insert id */
+ AC_MEMCPY( &ids[x+1], &ids[x], (ids[0]-x) * sizeof(ID) );
+ ids[x] = id;
+ }
+
+#if IDL_DEBUG > 1
+ idl_dump( ids );
+#elif IDL_DEBUG > 0
+ idl_check( ids );
+#endif
+
+ return 0;
+}
+
+static int mdb_idl_delete( ID *ids, ID id )
+{
+ unsigned x;
+
+#if IDL_DEBUG > 1
+ Debug( LDAP_DEBUG_ANY, "delete: %04lx at %d\n", (long) id, x, 0 );
+ idl_dump( ids );
+#elif IDL_DEBUG > 0
+ idl_check( ids );
+#endif
+
+ if (MDB_IDL_IS_RANGE( ids )) {
+ /* If deleting a range boundary, adjust */
+ if ( ids[1] == id )
+ ids[1]++;
+ else if ( ids[2] == id )
+ ids[2]--;
+ /* deleting from inside a range is a no-op */
+
+ /* If the range has collapsed, re-adjust */
+ if ( ids[1] > ids[2] )
+ ids[0] = 0;
+ else if ( ids[1] == ids[2] )
+ ids[1] = 1;
+ return 0;
+ }
+
+ x = mdb_idl_search( ids, id );
+ assert( x > 0 );
+
+ if( x <= 0 ) {
+ /* internal error */
+ return -2;
+ }
+
+ if( x > ids[0] || ids[x] != id ) {
+ /* not found */
+ return -1;
+
+ } else if ( --ids[0] == 0 ) {
+ if( x != 1 ) {
+ return -3;
+ }
+
+ } else {
+ AC_MEMCPY( &ids[x], &ids[x+1], (1+ids[0]-x) * sizeof(ID) );
+ }
+
+#if IDL_DEBUG > 1
+ idl_dump( ids );
+#elif IDL_DEBUG > 0
+ idl_check( ids );
+#endif
+
+ return 0;
+}
+
+static char *
+mdb_show_key(
+ MDB_val *key,
+ char *buf )
+{
+ if ( key->mv_size == 4 /* LUTIL_HASH_BYTES */ ) {
+ unsigned char *c = key->mv_data;
+ sprintf( buf, "[%02x%02x%02x%02x]", c[0], c[1], c[2], c[3] );
+ return buf;
+ } else {
+ return key->mv_data;
+ }
+}
+
+int
+mdb_idl_fetch_key(
+ BackendDB *be,
+ MDB_txn *txn,
+ MDB_dbi dbi,
+ MDB_val *key,
+ ID *ids,
+ MDB_cursor **saved_cursor,
+ int get_flag )
+{
+ MDB_val data, key2, *kptr;
+ MDB_cursor *cursor;
+ ID *i;
+ size_t len;
+ int rc;
+ MDB_cursor_op opflag;
+
+ char keybuf[16];
+
+ Debug( LDAP_DEBUG_ARGS,
+ "mdb_idl_fetch_key: %s\n",
+ mdb_show_key( key, keybuf ), 0, 0 );
+
+ assert( ids != NULL );
+
+ if ( saved_cursor && *saved_cursor ) {
+ opflag = MDB_NEXT;
+ } else if ( get_flag == LDAP_FILTER_GE ) {
+ opflag = MDB_SET_RANGE;
+ } else if ( get_flag == LDAP_FILTER_LE ) {
+ opflag = MDB_FIRST;
+ } else {
+ opflag = MDB_SET;
+ }
+
+ /* If we're not reusing an existing cursor, get a new one */
+ if( opflag != MDB_NEXT ) {
+ rc = mdb_cursor_open( txn, dbi, &cursor );
+ if( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "=> mdb_idl_fetch_key: "
+ "cursor failed: %s (%d)\n", mdb_strerror(rc), rc, 0 );
+ return rc;
+ }
+ } else {
+ cursor = *saved_cursor;
+ }
+
+ /* If this is a LE lookup, save original key so we can determine
+ * when to stop. If this is a GE lookup, save the key since it
+ * will be overwritten.
+ */
+ if ( get_flag == LDAP_FILTER_LE || get_flag == LDAP_FILTER_GE ) {
+ key2.mv_data = keybuf;
+ key2.mv_size = key->mv_size;
+ AC_MEMCPY( keybuf, key->mv_data, key->mv_size );
+ kptr = &key2;
+ } else {
+ kptr = key;
+ }
+ len = key->mv_size;
+ rc = mdb_cursor_get( cursor, kptr, &data, opflag );
+
+ /* skip presence key on range inequality lookups */
+ while (rc == 0 && kptr->mv_size != len) {
+ rc = mdb_cursor_get( cursor, kptr, &data, MDB_NEXT_NODUP );
+ }
+ /* If we're doing a LE compare and the new key is greater than
+ * our search key, we're done
+ */
+ if (rc == 0 && get_flag == LDAP_FILTER_LE && memcmp( kptr->mv_data,
+ key->mv_data, key->mv_size ) > 0 ) {
+ rc = MDB_NOTFOUND;
+ }
+ if (rc == 0) {
+ i = ids+1;
+ rc = mdb_cursor_get( cursor, key, &data, MDB_GET_MULTIPLE );
+ while (rc == 0) {
+ memcpy( i, data.mv_data, data.mv_size );
+ i += data.mv_size / sizeof(ID);
+ rc = mdb_cursor_get( cursor, key, &data, MDB_NEXT_MULTIPLE );
+ }
+ if ( rc == MDB_NOTFOUND ) rc = 0;
+ ids[0] = i - &ids[1];
+ /* On disk, a range is denoted by 0 in the first element */
+ if (ids[1] == 0) {
+ if (ids[0] != MDB_IDL_RANGE_SIZE) {
+ Debug( LDAP_DEBUG_ANY, "=> mdb_idl_fetch_key: "
+ "range size mismatch: expected %d, got %ld\n",
+ MDB_IDL_RANGE_SIZE, ids[0], 0 );
+ mdb_cursor_close( cursor );
+ return -1;
+ }
+ MDB_IDL_RANGE( ids, ids[2], ids[3] );
+ }
+ data.mv_size = MDB_IDL_SIZEOF(ids);
+ }
+
+ if ( saved_cursor && rc == 0 ) {
+ if ( !*saved_cursor )
+ *saved_cursor = cursor;
+ }
+ else
+ mdb_cursor_close( cursor );
+
+ if( rc == MDB_NOTFOUND ) {
+ return rc;
+
+ } else if( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "=> mdb_idl_fetch_key: "
+ "get failed: %s (%d)\n",
+ mdb_strerror(rc), rc, 0 );
+ return rc;
+
+ } else if ( data.mv_size == 0 || data.mv_size % sizeof( ID ) ) {
+ /* size not multiple of ID size */
+ Debug( LDAP_DEBUG_ANY, "=> mdb_idl_fetch_key: "
+ "odd size: expected %ld multiple, got %ld\n",
+ (long) sizeof( ID ), (long) data.mv_size, 0 );
+ return -1;
+
+ } else if ( data.mv_size != MDB_IDL_SIZEOF(ids) ) {
+ /* size mismatch */
+ Debug( LDAP_DEBUG_ANY, "=> mdb_idl_fetch_key: "
+ "get size mismatch: expected %ld, got %ld\n",
+ (long) ((1 + ids[0]) * sizeof( ID )), (long) data.mv_size, 0 );
+ return -1;
+ }
+
+ return rc;
+}
+
+int
+mdb_idl_insert_key(
+ BackendDB *be,
+ MDB_txn *txn,
+ MDB_dbi dbi,
+ MDB_val *key,
+ ID id )
+{
+ MDB_cursor *cursor;
+ MDB_val data;
+ ID lo, hi, *i;
+ char *err;
+ int rc;
+
+ {
+ char buf[16];
+ Debug( LDAP_DEBUG_ARGS,
+ "mdb_idl_insert_key: %lx %s\n",
+ (long) id, mdb_show_key( key, buf ), 0 );
+ }
+
+ assert( id != NOID );
+
+ rc = mdb_cursor_open( txn, dbi, &cursor );
+ if ( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "=> mdb_idl_insert_key: "
+ "cursor failed: %s (%d)\n", mdb_strerror(rc), rc, 0 );
+ return rc;
+ }
+ /* Fetch the first data item for this key, to see if it
+ * exists and if it's a range.
+ */
+ rc = mdb_cursor_get( cursor, key, &data, MDB_SET );
+ err = "c_get";
+ if ( rc == 0 ) {
+ i = data.mv_data;
+ memcpy(&lo, data.mv_data, sizeof(ID));
+ if ( lo != 0 ) {
+ /* not a range, count the number of items */
+ unsigned long count;
+ rc = mdb_cursor_count( cursor, &count );
+ if ( rc != 0 ) {
+ err = "c_count";
+ goto fail;
+ }
+ if ( count >= MDB_IDL_DB_MAX ) {
+ /* No room, convert to a range */
+ MDB_val key2;
+
+ lo = *i;
+ rc = mdb_cursor_get( cursor, &key2, &data, MDB_NEXT_NODUP );
+ if ( rc != 0 && rc != MDB_NOTFOUND ) {
+ err = "c_get next_nodup";
+ goto fail;
+ }
+ if ( rc == MDB_NOTFOUND ) {
+ rc = mdb_cursor_get( cursor, key, &data, MDB_LAST );
+ if ( rc != 0 ) {
+ err = "c_get last";
+ goto fail;
+ }
+ } else {
+ rc = mdb_cursor_get( cursor, key, &data, MDB_PREV );
+ if ( rc != 0 ) {
+ err = "c_get prev";
+ goto fail;
+ }
+ }
+ i = data.mv_data;
+ hi = *i;
+ /* Update hi/lo if needed */
+ if ( id < lo ) {
+ lo = id;
+ } else if ( id > hi ) {
+ hi = id;
+ }
+ /* delete the old key */
+ rc = mdb_del( txn, dbi, key, NULL );
+ if ( rc != 0 ) {
+ err = "mdb_del";
+ goto fail;
+ }
+ /* Store the range */
+ data.mv_size = sizeof(ID);
+ data.mv_data = &id;
+ id = 0;
+ rc = mdb_put( txn, dbi, key, &data, 0 );
+ if ( rc != 0 ) {
+ err = "mdb_put range";
+ goto fail;
+ }
+ id = lo;
+ rc = mdb_put( txn, dbi, key, &data, 0 );
+ if ( rc != 0 ) {
+ err = "mdb_put lo";
+ goto fail;
+ }
+ id = hi;
+ rc = mdb_put( txn, dbi, key, &data, 0 );
+ if ( rc != 0 ) {
+ err = "mdb_put hi";
+ goto fail;
+ }
+ } else {
+ /* There's room, just store it */
+ goto put1;
+ }
+ } else {
+ /* It's a range, see if we need to rewrite
+ * the boundaries
+ */
+ lo = i[1];
+ hi = i[2];
+ if ( id < lo || id > hi ) {
+ if ( id < lo )
+ data.mv_data = &lo;
+ else
+ data.mv_data = &hi;
+ data.mv_size = sizeof(ID);
+ /* Delete the current lo/hi */
+ rc = mdb_del( txn, dbi, key, &data );
+ if ( rc != 0 ) {
+ err = "mdb_del lo/hi";
+ goto fail;
+ }
+ goto put1;
+ }
+ }
+ } else if ( rc == MDB_NOTFOUND ) {
+put1: data.mv_data = &id;
+ data.mv_size = sizeof(ID);
+ rc = mdb_put( txn, dbi, key, &data, MDB_NODUPDATA );
+ /* Don't worry if it's already there */
+ if ( rc != 0 && rc != MDB_KEYEXIST ) {
+ err = "mdb_put id";
+ goto fail;
+ }
+ } else {
+ /* initial c_get failed, nothing was done */
+fail:
+ Debug( LDAP_DEBUG_ANY, "=> mdb_idl_insert_key: "
+ "%s failed: %s (%d)\n", err, mdb_strerror(rc), rc );
+ }
+ mdb_cursor_close( cursor );
+ return rc;
+}
+
+int
+mdb_idl_delete_key(
+ BackendDB *be,
+ MDB_txn *txn,
+ MDB_dbi dbi,
+ MDB_val *key,
+ ID id )
+{
+ int rc;
+ MDB_val data;
+ MDB_cursor *cursor;
+ ID lo, hi, tmp, *i;
+ char *err;
+
+ {
+ char buf[16];
+ Debug( LDAP_DEBUG_ARGS,
+ "mdb_idl_delete_key: %lx %s\n",
+ (long) id, mdb_show_key( key, buf ), 0 );
+ }
+ assert( id != NOID );
+
+ rc = mdb_cursor_open( txn, dbi, &cursor );
+ if ( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "=> mdb_idl_delete_key: "
+ "cursor failed: %s (%d)\n", mdb_strerror(rc), rc, 0 );
+ return rc;
+ }
+ /* Fetch the first data item for this key, to see if it
+ * exists and if it's a range.
+ */
+ rc = mdb_cursor_get( cursor, key, &data, MDB_SET );
+ err = "c_get";
+ if ( rc == 0 ) {
+ memcpy( &tmp, data.mv_data, sizeof(ID) );
+ i = data.mv_data;
+ if ( tmp != 0 ) {
+ /* Not a range, just delete it */
+ data.mv_data = &id;
+ rc = mdb_del( txn, dbi, key, &data );
+ if ( rc != 0 ) {
+ err = "mdb_del id";
+ goto fail;
+ }
+ } else {
+ /* It's a range, see if we need to rewrite
+ * the boundaries
+ */
+ lo = i[1];
+ hi = i[2];
+ if ( id == lo || id == hi ) {
+ ID lo2 = lo, hi2 = hi;
+ if ( id == lo ) {
+ lo2++;
+ } else if ( id == hi ) {
+ hi2--;
+ }
+ if ( lo2 >= hi2 ) {
+ /* The range has collapsed... */
+ rc = mdb_del( txn, dbi, key, NULL );
+ if ( rc != 0 ) {
+ err = "mdb_del";
+ goto fail;
+ }
+ } else {
+ data.mv_size = sizeof(ID);
+ if ( id == lo )
+ data.mv_data = &lo;
+ else
+ data.mv_data = &hi;
+ rc = mdb_del( txn, dbi, key, &data );
+ if ( rc != 0 ) {
+ err = "c_del";
+ goto fail;
+ }
+ if ( id == lo )
+ data.mv_data = &lo2;
+ else
+ data.mv_data = &hi2;
+ rc = mdb_put( txn, dbi, key, &data, 0 );
+ if ( rc != 0 ) {
+ err = "mdb_put lo/hi";
+ goto fail;
+ }
+ }
+ }
+ }
+ } else {
+ /* initial c_get failed, nothing was done */
+fail:
+ if ( rc != MDB_NOTFOUND ) {
+ Debug( LDAP_DEBUG_ANY, "=> mdb_idl_delete_key: "
+ "%s failed: %s (%d)\n", err, mdb_strerror(rc), rc );
+ }
+ }
+ mdb_cursor_close( cursor );
+ return rc;
+}
+
+
+/*
+ * idl_intersection - return a = a intersection b
+ */
+int
+mdb_idl_intersection(
+ ID *a,
+ ID *b )
+{
+ ID ida, idb;
+ ID idmax, idmin;
+ ID cursora = 0, cursorb = 0, cursorc;
+ int swap = 0;
+
+ if ( MDB_IDL_IS_ZERO( a ) || MDB_IDL_IS_ZERO( b ) ) {
+ a[0] = 0;
+ return 0;
+ }
+
+ idmin = IDL_MAX( MDB_IDL_FIRST(a), MDB_IDL_FIRST(b) );
+ idmax = IDL_MIN( MDB_IDL_LAST(a), MDB_IDL_LAST(b) );
+ if ( idmin > idmax ) {
+ a[0] = 0;
+ return 0;
+ } else if ( idmin == idmax ) {
+ a[0] = 1;
+ a[1] = idmin;
+ return 0;
+ }
+
+ if ( MDB_IDL_IS_RANGE( a ) ) {
+ if ( MDB_IDL_IS_RANGE(b) ) {
+ /* If both are ranges, just shrink the boundaries */
+ a[1] = idmin;
+ a[2] = idmax;
+ return 0;
+ } else {
+ /* Else swap so that b is the range, a is a list */
+ ID *tmp = a;
+ a = b;
+ b = tmp;
+ swap = 1;
+ }
+ }
+
+ /* If a range completely covers the list, the result is
+ * just the list. If idmin to idmax is contiguous, just
+ * turn it into a range.
+ */
+ if ( MDB_IDL_IS_RANGE( b )
+ && MDB_IDL_FIRST( b ) <= MDB_IDL_FIRST( a )
+ && MDB_IDL_LAST( b ) >= MDB_IDL_LAST( a ) ) {
+ if (idmax - idmin + 1 == a[0])
+ {
+ a[0] = NOID;
+ a[1] = idmin;
+ a[2] = idmax;
+ }
+ goto done;
+ }
+
+ /* Fine, do the intersection one element at a time.
+ * First advance to idmin in both IDLs.
+ */
+ cursora = cursorb = idmin;
+ ida = mdb_idl_first( a, &cursora );
+ idb = mdb_idl_first( b, &cursorb );
+ cursorc = 0;
+
+ while( ida <= idmax || idb <= idmax ) {
+ if( ida == idb ) {
+ a[++cursorc] = ida;
+ ida = mdb_idl_next( a, &cursora );
+ idb = mdb_idl_next( b, &cursorb );
+ } else if ( ida < idb ) {
+ ida = mdb_idl_next( a, &cursora );
+ } else {
+ idb = mdb_idl_next( b, &cursorb );
+ }
+ }
+ a[0] = cursorc;
+done:
+ if (swap)
+ MDB_IDL_CPY( b, a );
+
+ return 0;
+}
+
+
+/*
+ * idl_union - return a = a union b
+ */
+int
+mdb_idl_union(
+ ID *a,
+ ID *b )
+{
+ ID ida, idb;
+ ID cursora = 0, cursorb = 0, cursorc;
+
+ if ( MDB_IDL_IS_ZERO( b ) ) {
+ return 0;
+ }
+
+ if ( MDB_IDL_IS_ZERO( a ) ) {
+ MDB_IDL_CPY( a, b );
+ return 0;
+ }
+
+ if ( MDB_IDL_IS_RANGE( a ) || MDB_IDL_IS_RANGE(b) ) {
+over: ida = IDL_MIN( MDB_IDL_FIRST(a), MDB_IDL_FIRST(b) );
+ idb = IDL_MAX( MDB_IDL_LAST(a), MDB_IDL_LAST(b) );
+ a[0] = NOID;
+ a[1] = ida;
+ a[2] = idb;
+ return 0;
+ }
+
+ ida = mdb_idl_first( a, &cursora );
+ idb = mdb_idl_first( b, &cursorb );
+
+ cursorc = b[0];
+
+ /* The distinct elements of a are cat'd to b */
+ while( ida != NOID || idb != NOID ) {
+ if ( ida < idb ) {
+ if( ++cursorc > MDB_IDL_UM_MAX ) {
+ goto over;
+ }
+ b[cursorc] = ida;
+ ida = mdb_idl_next( a, &cursora );
+
+ } else {
+ if ( ida == idb )
+ ida = mdb_idl_next( a, &cursora );
+ idb = mdb_idl_next( b, &cursorb );
+ }
+ }
+
+ /* b is copied back to a in sorted order */
+ a[0] = cursorc;
+ cursora = 1;
+ cursorb = 1;
+ cursorc = b[0]+1;
+ while (cursorb <= b[0] || cursorc <= a[0]) {
+ if (cursorc > a[0])
+ idb = NOID;
+ else
+ idb = b[cursorc];
+ if (cursorb <= b[0] && b[cursorb] < idb)
+ a[cursora++] = b[cursorb++];
+ else {
+ a[cursora++] = idb;
+ cursorc++;
+ }
+ }
+
+ return 0;
+}
+
+
+#if 0
+/*
+ * mdb_idl_notin - return a intersection ~b (or a minus b)
+ */
+int
+mdb_idl_notin(
+ ID *a,
+ ID *b,
+ ID *ids )
+{
+ ID ida, idb;
+ ID cursora = 0, cursorb = 0;
+
+ if( MDB_IDL_IS_ZERO( a ) ||
+ MDB_IDL_IS_ZERO( b ) ||
+ MDB_IDL_IS_RANGE( b ) )
+ {
+ MDB_IDL_CPY( ids, a );
+ return 0;
+ }
+
+ if( MDB_IDL_IS_RANGE( a ) ) {
+ MDB_IDL_CPY( ids, a );
+ return 0;
+ }
+
+ ida = mdb_idl_first( a, &cursora ),
+ idb = mdb_idl_first( b, &cursorb );
+
+ ids[0] = 0;
+
+ while( ida != NOID ) {
+ if ( idb == NOID ) {
+ /* we could shortcut this */
+ ids[++ids[0]] = ida;
+ ida = mdb_idl_next( a, &cursora );
+
+ } else if ( ida < idb ) {
+ ids[++ids[0]] = ida;
+ ida = mdb_idl_next( a, &cursora );
+
+ } else if ( ida > idb ) {
+ idb = mdb_idl_next( b, &cursorb );
+
+ } else {
+ ida = mdb_idl_next( a, &cursora );
+ idb = mdb_idl_next( b, &cursorb );
+ }
+ }
+
+ return 0;
+}
+#endif
+
+ID mdb_idl_first( ID *ids, ID *cursor )
+{
+ ID pos;
+
+ if ( ids[0] == 0 ) {
+ *cursor = NOID;
+ return NOID;
+ }
+
+ if ( MDB_IDL_IS_RANGE( ids ) ) {
+ if( *cursor < ids[1] ) {
+ *cursor = ids[1];
+ }
+ return *cursor;
+ }
+
+ if ( *cursor == 0 )
+ pos = 1;
+ else
+ pos = mdb_idl_search( ids, *cursor );
+
+ if( pos > ids[0] ) {
+ return NOID;
+ }
+
+ *cursor = pos;
+ return ids[pos];
+}
+
+ID mdb_idl_next( ID *ids, ID *cursor )
+{
+ if ( MDB_IDL_IS_RANGE( ids ) ) {
+ if( ids[2] < ++(*cursor) ) {
+ return NOID;
+ }
+ return *cursor;
+ }
+
+ if ( ++(*cursor) <= ids[0] ) {
+ return ids[*cursor];
+ }
+
+ return NOID;
+}
+
+/* Add one ID to an unsorted list. We ensure that the first element is the
+ * minimum and the last element is the maximum, for fast range compaction.
+ * this means IDLs up to length 3 are always sorted...
+ */
+int mdb_idl_append_one( ID *ids, ID id )
+{
+ if (MDB_IDL_IS_RANGE( ids )) {
+ /* if already in range, treat as a dup */
+ if (id >= MDB_IDL_FIRST(ids) && id <= MDB_IDL_LAST(ids))
+ return -1;
+ if (id < MDB_IDL_FIRST(ids))
+ ids[1] = id;
+ else if (id > MDB_IDL_LAST(ids))
+ ids[2] = id;
+ return 0;
+ }
+ if ( ids[0] ) {
+ ID tmp;
+
+ if (id < ids[1]) {
+ tmp = ids[1];
+ ids[1] = id;
+ id = tmp;
+ }
+ if ( ids[0] > 1 && id < ids[ids[0]] ) {
+ tmp = ids[ids[0]];
+ ids[ids[0]] = id;
+ id = tmp;
+ }
+ }
+ ids[0]++;
+ if ( ids[0] >= MDB_IDL_UM_MAX ) {
+ ids[0] = NOID;
+ ids[2] = id;
+ } else {
+ ids[ids[0]] = id;
+ }
+ return 0;
+}
+
+/* Append sorted list b to sorted list a. The result is unsorted but
+ * a[1] is the min of the result and a[a[0]] is the max.
+ */
+int mdb_idl_append( ID *a, ID *b )
+{
+ ID ida, idb, tmp, swap = 0;
+
+ if ( MDB_IDL_IS_ZERO( b ) ) {
+ return 0;
+ }
+
+ if ( MDB_IDL_IS_ZERO( a ) ) {
+ MDB_IDL_CPY( a, b );
+ return 0;
+ }
+
+ ida = MDB_IDL_LAST( a );
+ idb = MDB_IDL_LAST( b );
+ if ( MDB_IDL_IS_RANGE( a ) || MDB_IDL_IS_RANGE(b) ||
+ a[0] + b[0] >= MDB_IDL_UM_MAX ) {
+ a[2] = IDL_MAX( ida, idb );
+ a[1] = IDL_MIN( a[1], b[1] );
+ a[0] = NOID;
+ return 0;
+ }
+
+ if ( b[0] > 1 && ida > idb ) {
+ swap = idb;
+ a[a[0]] = idb;
+ b[b[0]] = ida;
+ }
+
+ if ( b[1] < a[1] ) {
+ tmp = a[1];
+ a[1] = b[1];
+ } else {
+ tmp = b[1];
+ }
+ a[0]++;
+ a[a[0]] = tmp;
+
+ if ( b[0] > 1 ) {
+ int i = b[0] - 1;
+ AC_MEMCPY(a+a[0]+1, b+2, i * sizeof(ID));
+ a[0] += i;
+ }
+ if ( swap ) {
+ b[b[0]] = swap;
+ }
+ return 0;
+}
+
+#if 1
+
+/* Quicksort + Insertion sort for small arrays */
+
+#define SMALL 8
+#define SWAP(a,b) itmp=(a);(a)=(b);(b)=itmp
+
+void
+mdb_idl_sort( ID *ids, ID *tmp )
+{
+ int *istack = (int *)tmp;
+ int i,j,k,l,ir,jstack;
+ ID a, itmp;
+
+ if ( MDB_IDL_IS_RANGE( ids ))
+ return;
+
+ ir = ids[0];
+ l = 1;
+ jstack = 0;
+ for(;;) {
+ if (ir - l < SMALL) { /* Insertion sort */
+ for (j=l+1;j<=ir;j++) {
+ a = ids[j];
+ for (i=j-1;i>=1;i--) {
+ if (ids[i] <= a) break;
+ ids[i+1] = ids[i];
+ }
+ ids[i+1] = a;
+ }
+ if (jstack == 0) break;
+ ir = istack[jstack--];
+ l = istack[jstack--];
+ } else {
+ k = (l + ir) >> 1; /* Choose median of left, center, right */
+ SWAP(ids[k], ids[l+1]);
+ if (ids[l] > ids[ir]) {
+ SWAP(ids[l], ids[ir]);
+ }
+ if (ids[l+1] > ids[ir]) {
+ SWAP(ids[l+1], ids[ir]);
+ }
+ if (ids[l] > ids[l+1]) {
+ SWAP(ids[l], ids[l+1]);
+ }
+ i = l+1;
+ j = ir;
+ a = ids[l+1];
+ for(;;) {
+ do i++; while(ids[i] < a);
+ do j--; while(ids[j] > a);
+ if (j < i) break;
+ SWAP(ids[i],ids[j]);
+ }
+ ids[l+1] = ids[j];
+ ids[j] = a;
+ jstack += 2;
+ if (ir-i+1 >= j-1) {
+ istack[jstack] = ir;
+ istack[jstack-1] = i;
+ ir = j-1;
+ } else {
+ istack[jstack] = j-1;
+ istack[jstack-1] = l;
+ l = i;
+ }
+ }
+ }
+}
+
+#else
+
+/* 8 bit Radix sort + insertion sort
+ *
+ * based on code from http://www.cubic.org/docs/radix.htm
+ * with improvements by mbackes@symas.com and hyc@symas.com
+ *
+ * This code is O(n) but has a relatively high constant factor. For lists
+ * up to ~50 Quicksort is slightly faster; up to ~100 they are even.
+ * Much faster than quicksort for lists longer than ~100. Insertion
+ * sort is actually superior for lists <50.
+ */
+
+#define BUCKETS (1<<8)
+#define SMALL 50
+
+void
+mdb_idl_sort( ID *ids, ID *tmp )
+{
+ int count, soft_limit, phase = 0, size = ids[0];
+ ID *idls[2];
+ unsigned char *maxv = (unsigned char *)&ids[size];
+
+ if ( MDB_IDL_IS_RANGE( ids ))
+ return;
+
+ /* Use insertion sort for small lists */
+ if ( size <= SMALL ) {
+ int i,j;
+ ID a;
+
+ for (j=1;j<=size;j++) {
+ a = ids[j];
+ for (i=j-1;i>=1;i--) {
+ if (ids[i] <= a) break;
+ ids[i+1] = ids[i];
+ }
+ ids[i+1] = a;
+ }
+ return;
+ }
+
+ tmp[0] = size;
+ idls[0] = ids;
+ idls[1] = tmp;
+
+#if BYTE_ORDER == BIG_ENDIAN
+ for (soft_limit = 0; !maxv[soft_limit]; soft_limit++);
+#else
+ for (soft_limit = sizeof(ID)-1; !maxv[soft_limit]; soft_limit--);
+#endif
+
+ for (
+#if BYTE_ORDER == BIG_ENDIAN
+ count = sizeof(ID)-1; count >= soft_limit; --count
+#else
+ count = 0; count <= soft_limit; ++count
+#endif
+ ) {
+ unsigned int num[BUCKETS], * np, n, sum;
+ int i;
+ ID *sp, *source, *dest;
+ unsigned char *bp, *source_start;
+
+ source = idls[phase]+1;
+ dest = idls[phase^1]+1;
+ source_start = ((unsigned char *) source) + count;
+
+ np = num;
+ for ( i = BUCKETS; i > 0; --i ) *np++ = 0;
+
+ /* count occurences of every byte value */
+ bp = source_start;
+ for ( i = size; i > 0; --i, bp += sizeof(ID) )
+ num[*bp]++;
+
+ /* transform count into index by summing elements and storing
+ * into same array
+ */
+ sum = 0;
+ np = num;
+ for ( i = BUCKETS; i > 0; --i ) {
+ n = *np;
+ *np++ = sum;
+ sum += n;
+ }
+
+ /* fill dest with the right values in the right place */
+ bp = source_start;
+ sp = source;
+ for ( i = size; i > 0; --i, bp += sizeof(ID) ) {
+ np = num + *bp;
+ dest[*np] = *sp++;
+ ++(*np);
+ }
+ phase ^= 1;
+ }
+
+ /* copy back from temp if needed */
+ if ( phase ) {
+ ids++; tmp++;
+ for ( count = 0; count < size; ++count )
+ *ids++ = *tmp++;
+ }
+}
+#endif /* Quick vs Radix */
--- /dev/null
+/* idl.h - ldap mdb back-end ID list header file */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2011 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef _MDB_IDL_H_
+#define _MDB_IDL_H_
+
+/* IDL sizes - likely should be even bigger
+ * limiting factors: sizeof(ID), thread stack size
+ */
+#define MDB_IDL_LOGN 16 /* DB_SIZE is 2^16, UM_SIZE is 2^17 */
+#define MDB_IDL_DB_SIZE (1<<MDB_IDL_LOGN)
+#define MDB_IDL_UM_SIZE (1<<(MDB_IDL_LOGN+1))
+#define MDB_IDL_UM_SIZEOF (MDB_IDL_UM_SIZE * sizeof(ID))
+
+#define MDB_IDL_DB_MAX (MDB_IDL_DB_SIZE-1)
+
+#define MDB_IDL_UM_MAX (MDB_IDL_UM_SIZE-1)
+
+#define MDB_IDL_IS_RANGE(ids) ((ids)[0] == NOID)
+#define MDB_IDL_RANGE_SIZE (3)
+#define MDB_IDL_RANGE_SIZEOF (MDB_IDL_RANGE_SIZE * sizeof(ID))
+#define MDB_IDL_SIZEOF(ids) ((MDB_IDL_IS_RANGE(ids) \
+ ? MDB_IDL_RANGE_SIZE : ((ids)[0]+1)) * sizeof(ID))
+
+#define MDB_IDL_RANGE_FIRST(ids) ((ids)[1])
+#define MDB_IDL_RANGE_LAST(ids) ((ids)[2])
+
+#define MDB_IDL_RANGE( ids, f, l ) \
+ do { \
+ (ids)[0] = NOID; \
+ (ids)[1] = (f); \
+ (ids)[2] = (l); \
+ } while(0)
+
+#define MDB_IDL_ZERO(ids) \
+ do { \
+ (ids)[0] = 0; \
+ (ids)[1] = 0; \
+ (ids)[2] = 0; \
+ } while(0)
+
+#define MDB_IDL_IS_ZERO(ids) ( (ids)[0] == 0 )
+#define MDB_IDL_IS_ALL( range, ids ) ( (ids)[0] == NOID \
+ && (ids)[1] <= (range)[1] && (range)[2] <= (ids)[2] )
+
+#define MDB_IDL_CPY( dst, src ) (AC_MEMCPY( dst, src, MDB_IDL_SIZEOF( src ) ))
+
+#define MDB_IDL_ID( mdb, ids, id ) MDB_IDL_RANGE( ids, id, NOID )
+#define MDB_IDL_ALL( ids ) MDB_IDL_RANGE( ids, 1, NOID )
+
+#define MDB_IDL_FIRST( ids ) ( ids[1] )
+#define MDB_IDL_LAST( ids ) ( MDB_IDL_IS_RANGE(ids) \
+ ? ids[2] : ids[ids[0]] )
+
+#define MDB_IDL_N( ids ) ( MDB_IDL_IS_RANGE(ids) \
+ ? (ids[2]-ids[1])+1 : ids[0] )
+
+LDAP_BEGIN_DECL
+LDAP_END_DECL
+
+#endif
--- /dev/null
+/* index.c - routines for dealing with attribute indexes */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2011 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "back-mdb.h"
+#include "lutil_hash.h"
+
+static char presence_keyval[] = {0,0};
+static struct berval presence_key = BER_BVC(presence_keyval);
+
+AttrInfo *mdb_index_mask(
+ Backend *be,
+ AttributeDescription *desc,
+ struct berval *atname )
+{
+ AttributeType *at;
+ AttrInfo *ai = mdb_attr_mask( be->be_private, desc );
+
+ if( ai ) {
+ *atname = desc->ad_cname;
+ return ai;
+ }
+
+ /* If there is a tagging option, did we ever index the base
+ * type? If so, check for mask, otherwise it's not there.
+ */
+ if( slap_ad_is_tagged( desc ) && desc != desc->ad_type->sat_ad ) {
+ /* has tagging option */
+ ai = mdb_attr_mask( be->be_private, desc->ad_type->sat_ad );
+
+ if ( ai && !( ai->ai_indexmask & SLAP_INDEX_NOTAGS ) ) {
+ *atname = desc->ad_type->sat_cname;
+ return ai;
+ }
+ }
+
+ /* see if supertype defined mask for its subtypes */
+ for( at = desc->ad_type; at != NULL ; at = at->sat_sup ) {
+ /* If no AD, we've never indexed this type */
+ if ( !at->sat_ad ) continue;
+
+ ai = mdb_attr_mask( be->be_private, at->sat_ad );
+
+ if ( ai && !( ai->ai_indexmask & SLAP_INDEX_NOSUBTYPES ) ) {
+ *atname = at->sat_cname;
+ return ai;
+ }
+ }
+
+ return 0;
+}
+
+/* This function is only called when evaluating search filters.
+ */
+int mdb_index_param(
+ Backend *be,
+ AttributeDescription *desc,
+ int ftype,
+ MDB_dbi *dbip,
+ slap_mask_t *maskp,
+ struct berval *prefixp )
+{
+ AttrInfo *ai;
+ slap_mask_t mask, type = 0;
+
+ ai = mdb_index_mask( be, desc, prefixp );
+
+ if ( !ai ) {
+#ifdef MDB_MONITOR_IDX
+ switch ( ftype ) {
+ case LDAP_FILTER_PRESENT:
+ type = SLAP_INDEX_PRESENT;
+ break;
+ case LDAP_FILTER_APPROX:
+ type = SLAP_INDEX_APPROX;
+ break;
+ case LDAP_FILTER_EQUALITY:
+ type = SLAP_INDEX_EQUALITY;
+ break;
+ case LDAP_FILTER_SUBSTRINGS:
+ type = SLAP_INDEX_SUBSTR;
+ break;
+ default:
+ return LDAP_INAPPROPRIATE_MATCHING;
+ }
+ mdb_monitor_idx_add( be->be_private, desc, type );
+#endif /* MDB_MONITOR_IDX */
+
+ return LDAP_INAPPROPRIATE_MATCHING;
+ }
+ mask = ai->ai_indexmask;
+
+ switch( ftype ) {
+ case LDAP_FILTER_PRESENT:
+ type = SLAP_INDEX_PRESENT;
+ if( IS_SLAP_INDEX( mask, SLAP_INDEX_PRESENT ) ) {
+ *prefixp = presence_key;
+ goto done;
+ }
+ break;
+
+ case LDAP_FILTER_APPROX:
+ type = SLAP_INDEX_APPROX;
+ if ( desc->ad_type->sat_approx ) {
+ if( IS_SLAP_INDEX( mask, SLAP_INDEX_APPROX ) ) {
+ goto done;
+ }
+ break;
+ }
+
+ /* Use EQUALITY rule and index for approximate match */
+ /* fall thru */
+
+ case LDAP_FILTER_EQUALITY:
+ type = SLAP_INDEX_EQUALITY;
+ if( IS_SLAP_INDEX( mask, SLAP_INDEX_EQUALITY ) ) {
+ goto done;
+ }
+ break;
+
+ case LDAP_FILTER_SUBSTRINGS:
+ type = SLAP_INDEX_SUBSTR;
+ if( IS_SLAP_INDEX( mask, SLAP_INDEX_SUBSTR ) ) {
+ goto done;
+ }
+ break;
+
+ default:
+ return LDAP_OTHER;
+ }
+
+#ifdef MDB_MONITOR_IDX
+ mdb_monitor_idx_add( be->be_private, desc, type );
+#endif /* MDB_MONITOR_IDX */
+
+ return LDAP_INAPPROPRIATE_MATCHING;
+
+done:
+ *dbip = ai->ai_dbi;
+ *maskp = mask;
+ return LDAP_SUCCESS;
+}
+
+static int indexer(
+ Operation *op,
+ MDB_txn *txn,
+ MDB_dbi dbi,
+ AttributeDescription *ad,
+ struct berval *atname,
+ BerVarray vals,
+ ID id,
+ int opid,
+ slap_mask_t mask )
+{
+ int rc, i;
+ struct berval *keys;
+
+ assert( mask != 0 );
+
+ if( IS_SLAP_INDEX( mask, SLAP_INDEX_PRESENT ) ) {
+ rc = mdb_key_change( op->o_bd, txn, dbi, &presence_key, id, opid );
+ if( rc ) {
+ goto done;
+ }
+ }
+
+ if( IS_SLAP_INDEX( mask, SLAP_INDEX_EQUALITY ) ) {
+ rc = ad->ad_type->sat_equality->smr_indexer(
+ LDAP_FILTER_EQUALITY,
+ mask,
+ ad->ad_type->sat_syntax,
+ ad->ad_type->sat_equality,
+ atname, vals, &keys, op->o_tmpmemctx );
+
+ if( rc == LDAP_SUCCESS && keys != NULL ) {
+ for( i=0; keys[i].bv_val != NULL; i++ ) {
+ rc = mdb_key_change( op->o_bd, txn, dbi, &keys[i], id, opid );
+ if( rc ) {
+ ber_bvarray_free_x( keys, op->o_tmpmemctx );
+ goto done;
+ }
+ }
+ ber_bvarray_free_x( keys, op->o_tmpmemctx );
+ }
+ rc = LDAP_SUCCESS;
+ }
+
+ if( IS_SLAP_INDEX( mask, SLAP_INDEX_APPROX ) ) {
+ rc = ad->ad_type->sat_approx->smr_indexer(
+ LDAP_FILTER_APPROX,
+ mask,
+ ad->ad_type->sat_syntax,
+ ad->ad_type->sat_approx,
+ atname, vals, &keys, op->o_tmpmemctx );
+
+ if( rc == LDAP_SUCCESS && keys != NULL ) {
+ for( i=0; keys[i].bv_val != NULL; i++ ) {
+ rc = mdb_key_change( op->o_bd, txn, dbi, &keys[i], id, opid );
+ if( rc ) {
+ ber_bvarray_free_x( keys, op->o_tmpmemctx );
+ goto done;
+ }
+ }
+ ber_bvarray_free_x( keys, op->o_tmpmemctx );
+ }
+
+ rc = LDAP_SUCCESS;
+ }
+
+ if( IS_SLAP_INDEX( mask, SLAP_INDEX_SUBSTR ) ) {
+ rc = ad->ad_type->sat_substr->smr_indexer(
+ LDAP_FILTER_SUBSTRINGS,
+ mask,
+ ad->ad_type->sat_syntax,
+ ad->ad_type->sat_substr,
+ atname, vals, &keys, op->o_tmpmemctx );
+
+ if( rc == LDAP_SUCCESS && keys != NULL ) {
+ for( i=0; keys[i].bv_val != NULL; i++ ) {
+ rc = mdb_key_change( op->o_bd, txn, dbi, &keys[i], id, opid );
+ if( rc ) {
+ ber_bvarray_free_x( keys, op->o_tmpmemctx );
+ goto done;
+ }
+ }
+ ber_bvarray_free_x( keys, op->o_tmpmemctx );
+ }
+
+ rc = LDAP_SUCCESS;
+ }
+
+done:
+ switch( rc ) {
+ /* The callers all know how to deal with these results */
+ case 0:
+ break;
+ /* Anything else is bad news */
+ default:
+ rc = LDAP_OTHER;
+ }
+ return rc;
+}
+
+static int index_at_values(
+ Operation *op,
+ MDB_txn *txn,
+ AttributeDescription *ad,
+ AttributeType *type,
+ struct berval *tags,
+ BerVarray vals,
+ ID id,
+ int opid )
+{
+ int rc;
+ slap_mask_t mask = 0;
+ int ixop = opid;
+ AttrInfo *ai = NULL;
+
+ if ( opid == MDB_INDEX_UPDATE_OP )
+ ixop = SLAP_INDEX_ADD_OP;
+
+ if( type->sat_sup ) {
+ /* recurse */
+ rc = index_at_values( op, txn, NULL,
+ type->sat_sup, tags,
+ vals, id, opid );
+
+ if( rc ) return rc;
+ }
+
+ /* If this type has no AD, we've never used it before */
+ if( type->sat_ad ) {
+ ai = mdb_attr_mask( op->o_bd->be_private, type->sat_ad );
+ if ( ai ) {
+#ifdef LDAP_COMP_MATCH
+ /* component indexing */
+ if ( ai->ai_cr ) {
+ ComponentReference *cr;
+ for( cr = ai->ai_cr ; cr ; cr = cr->cr_next ) {
+ rc = indexer( op, txn, ai->ai_dbi, cr->cr_ad, &type->sat_cname,
+ cr->cr_nvals, id, ixop,
+ cr->cr_indexmask );
+ }
+ }
+#endif
+ ad = type->sat_ad;
+ /* If we're updating the index, just set the new bits that aren't
+ * already in the old mask.
+ */
+ if ( opid == MDB_INDEX_UPDATE_OP )
+ mask = ai->ai_newmask & ~ai->ai_indexmask;
+ else
+ /* For regular updates, if there is a newmask use it. Otherwise
+ * just use the old mask.
+ */
+ mask = ai->ai_newmask ? ai->ai_newmask : ai->ai_indexmask;
+ if( mask ) {
+ rc = indexer( op, txn, ai->ai_dbi, ad, &type->sat_cname,
+ vals, id, ixop, mask );
+
+ if( rc ) return rc;
+ }
+ }
+ }
+
+ if( tags->bv_len ) {
+ AttributeDescription *desc;
+
+ desc = ad_find_tags( type, tags );
+ if( desc ) {
+ ai = mdb_attr_mask( op->o_bd->be_private, desc );
+
+ if( ai ) {
+ if ( opid == MDB_INDEX_UPDATE_OP )
+ mask = ai->ai_newmask & ~ai->ai_indexmask;
+ else
+ mask = ai->ai_newmask ? ai->ai_newmask : ai->ai_indexmask;
+ if ( mask ) {
+ rc = indexer( op, txn, ai->ai_dbi, desc, &desc->ad_cname,
+ vals, id, ixop, mask );
+
+ if( rc ) {
+ return rc;
+ }
+ }
+ }
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
+int mdb_index_values(
+ Operation *op,
+ MDB_txn *txn,
+ AttributeDescription *desc,
+ BerVarray vals,
+ ID id,
+ int opid )
+{
+ int rc;
+
+ /* Never index ID 0 */
+ if ( id == 0 )
+ return 0;
+
+ rc = index_at_values( op, txn, desc,
+ desc->ad_type, &desc->ad_tags,
+ vals, id, opid );
+
+ return rc;
+}
+
+/* Get the list of which indices apply to this attr */
+int
+mdb_index_recset(
+ struct mdb_info *mdb,
+ Attribute *a,
+ AttributeType *type,
+ struct berval *tags,
+ IndexRec *ir )
+{
+ int rc, slot;
+ AttrList *al;
+
+ if( type->sat_sup ) {
+ /* recurse */
+ rc = mdb_index_recset( mdb, a, type->sat_sup, tags, ir );
+ if( rc ) return rc;
+ }
+ /* If this type has no AD, we've never used it before */
+ if( type->sat_ad ) {
+ slot = mdb_attr_slot( mdb, type->sat_ad, NULL );
+ if ( slot >= 0 ) {
+ ir[slot].ai = mdb->mi_attrs[slot];
+ al = ch_malloc( sizeof( AttrList ));
+ al->attr = a;
+ al->next = ir[slot].attrs;
+ ir[slot].attrs = al;
+ }
+ }
+ if( tags->bv_len ) {
+ AttributeDescription *desc;
+
+ desc = ad_find_tags( type, tags );
+ if( desc ) {
+ slot = mdb_attr_slot( mdb, desc, NULL );
+ if ( slot >= 0 ) {
+ ir[slot].ai = mdb->mi_attrs[slot];
+ al = ch_malloc( sizeof( AttrList ));
+ al->attr = a;
+ al->next = ir[slot].attrs;
+ ir[slot].attrs = al;
+ }
+ }
+ }
+ return LDAP_SUCCESS;
+}
+
+/* Apply the indices for the recset */
+int mdb_index_recrun(
+ Operation *op,
+ struct mdb_info *mdb,
+ IndexRec *ir0,
+ ID id,
+ int base )
+{
+ IndexRec *ir;
+ AttrList *al;
+ int i, rc = 0;
+
+ /* Never index ID 0 */
+ if ( id == 0 )
+ return 0;
+
+ for (i=base; i<mdb->mi_nattrs; i+=slap_tool_thread_max) {
+ ir = ir0 + i;
+ if ( !ir->ai ) continue;
+ while (( al = ir->attrs )) {
+ ir->attrs = al->next;
+ rc = indexer( op, NULL, ir->ai->ai_dbi, ir->ai->ai_desc,
+ &ir->ai->ai_desc->ad_type->sat_cname,
+ al->attr->a_nvals, id, SLAP_INDEX_ADD_OP,
+ ir->ai->ai_indexmask );
+ free( al );
+ if ( rc ) break;
+ }
+ }
+ return rc;
+}
+
+int
+mdb_index_entry(
+ Operation *op,
+ MDB_txn *txn,
+ int opid,
+ Entry *e )
+{
+ int rc;
+ Attribute *ap = e->e_attrs;
+#if 0 /* ifdef LDAP_COMP_MATCH */
+ ComponentReference *cr_list = NULL;
+ ComponentReference *cr = NULL, *dupped_cr = NULL;
+ void* decoded_comp;
+ ComponentSyntaxInfo* csi_attr;
+ Syntax* syn;
+ AttributeType* at;
+ int i, num_attr;
+ void* mem_op;
+ struct berval value = {0};
+#endif
+
+ /* Never index ID 0 */
+ if ( e->e_id == 0 )
+ return 0;
+
+ Debug( LDAP_DEBUG_TRACE, "=> index_entry_%s( %ld, \"%s\" )\n",
+ opid == SLAP_INDEX_DELETE_OP ? "del" : "add",
+ (long) e->e_id, e->e_dn );
+
+ /* add each attribute to the indexes */
+ for ( ; ap != NULL; ap = ap->a_next ) {
+#if 0 /* ifdef LDAP_COMP_MATCH */
+ AttrInfo *ai;
+ /* see if attribute has components to be indexed */
+ ai = mdb_attr_mask( op->o_bd->be_private, ap->a_desc->ad_type->sat_ad );
+ if ( !ai ) continue;
+ cr_list = ai->ai_cr;
+ if ( attr_converter && cr_list ) {
+ syn = ap->a_desc->ad_type->sat_syntax;
+ ap->a_comp_data = op->o_tmpalloc( sizeof( ComponentData ), op->o_tmpmemctx );
+ /* Memory chunk(nibble) pre-allocation for decoders */
+ mem_op = nibble_mem_allocator ( 1024*16, 1024*4 );
+ ap->a_comp_data->cd_mem_op = mem_op;
+ for( cr = cr_list ; cr ; cr = cr->cr_next ) {
+ /* count how many values in an attribute */
+ for( num_attr=0; ap->a_vals[num_attr].bv_val != NULL; num_attr++ );
+ num_attr++;
+ cr->cr_nvals = (BerVarray)op->o_tmpalloc( sizeof( struct berval )*num_attr, op->o_tmpmemctx );
+ for( i=0; ap->a_vals[i].bv_val != NULL; i++ ) {
+ /* decoding attribute value */
+ decoded_comp = attr_converter ( ap, syn, &ap->a_vals[i] );
+ if ( !decoded_comp )
+ return LDAP_DECODING_ERROR;
+ /* extracting the referenced component */
+ dupped_cr = dup_comp_ref( op, cr );
+ csi_attr = ((ComponentSyntaxInfo*)decoded_comp)->csi_comp_desc->cd_extract_i( mem_op, dupped_cr, decoded_comp );
+ if ( !csi_attr )
+ return LDAP_DECODING_ERROR;
+ cr->cr_asn_type_id = csi_attr->csi_comp_desc->cd_type_id;
+ cr->cr_ad = (AttributeDescription*)get_component_description ( cr->cr_asn_type_id );
+ if ( !cr->cr_ad )
+ return LDAP_INVALID_SYNTAX;
+ at = cr->cr_ad->ad_type;
+ /* encoding the value of component in GSER */
+ rc = component_encoder( mem_op, csi_attr, &value );
+ if ( rc != LDAP_SUCCESS )
+ return LDAP_ENCODING_ERROR;
+ /* Normalize the encoded component values */
+ if ( at->sat_equality && at->sat_equality->smr_normalize ) {
+ rc = at->sat_equality->smr_normalize (
+ SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
+ at->sat_syntax, at->sat_equality,
+ &value, &cr->cr_nvals[i], op->o_tmpmemctx );
+ } else {
+ cr->cr_nvals[i] = value;
+ }
+ }
+ /* The end of BerVarray */
+ cr->cr_nvals[num_attr-1].bv_val = NULL;
+ cr->cr_nvals[num_attr-1].bv_len = 0;
+ }
+ op->o_tmpfree( ap->a_comp_data, op->o_tmpmemctx );
+ nibble_mem_free ( mem_op );
+ ap->a_comp_data = NULL;
+ }
+#endif
+ rc = mdb_index_values( op, txn, ap->a_desc,
+ ap->a_nvals, e->e_id, opid );
+
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= index_entry_%s( %ld, \"%s\" ) failure\n",
+ opid == SLAP_INDEX_ADD_OP ? "add" : "del",
+ (long) e->e_id, e->e_dn );
+ return rc;
+ }
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "<= index_entry_%s( %ld, \"%s\" ) success\n",
+ opid == SLAP_INDEX_DELETE_OP ? "del" : "add",
+ (long) e->e_id, e->e_dn );
+
+ return LDAP_SUCCESS;
+}
--- /dev/null
+/* init.c - initialize mdb backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2011 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+#include <ac/stdlib.h>
+#include <ac/errno.h>
+#include <sys/stat.h>
+#include "back-mdb.h"
+#include <lutil.h>
+#include <ldap_rq.h>
+#include "config.h"
+
+static const struct berval mdmi_databases[] = {
+ BER_BVC("ad2i"),
+ BER_BVC("dn2i"),
+ BER_BVC("id2e"),
+ BER_BVNULL
+};
+
+static int
+mdb_db_init( BackendDB *be, ConfigReply *cr )
+{
+ struct mdb_info *mdb;
+ int rc;
+
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_db_init) ": Initializing mdb database\n",
+ 0, 0, 0 );
+
+ /* allocate backend-database-specific stuff */
+ mdb = (struct mdb_info *) ch_calloc( 1, sizeof(struct mdb_info) );
+
+ /* DBEnv parameters */
+ mdb->mi_dbenv_home = ch_strdup( SLAPD_DEFAULT_DB_DIR );
+ mdb->mi_dbenv_flags = 0;
+ mdb->mi_dbenv_mode = SLAPD_DEFAULT_DB_MODE;
+
+ mdb->mi_search_stack_depth = DEFAULT_SEARCH_STACK_DEPTH;
+ mdb->mi_search_stack = NULL;
+
+ mdb->mi_mapsize = DEFAULT_MAPSIZE;
+
+ be->be_private = mdb;
+ be->be_cf_ocs = be->bd_info->bi_cf_ocs;
+
+#ifndef MDB_MULTIPLE_SUFFIXES
+ SLAP_DBFLAGS( be ) |= SLAP_DBFLAG_ONE_SUFFIX;
+#endif
+
+ rc = mdb_monitor_db_init( be );
+
+ return rc;
+}
+
+static int
+mdb_db_close( BackendDB *be, ConfigReply *cr );
+
+static int
+mdb_db_open( BackendDB *be, ConfigReply *cr )
+{
+ int rc, i;
+ struct mdb_info *mdb = (struct mdb_info *) be->be_private;
+ struct stat stat1;
+ u_int32_t flags;
+ char *dbhome;
+ MDB_txn *txn;
+
+ if ( be->be_suffix == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ LDAP_XSTRING(mdb_db_open) ": need suffix.\n",
+ 1, 0, 0 );
+ return -1;
+ }
+
+ Debug( LDAP_DEBUG_ARGS,
+ LDAP_XSTRING(mdb_db_open) ": \"%s\"\n",
+ be->be_suffix[0].bv_val, 0, 0 );
+
+ /* Check existence of dbenv_home. Any error means trouble */
+ rc = stat( mdb->mi_dbenv_home, &stat1 );
+ if( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ LDAP_XSTRING(mdb_db_open) ": database \"%s\": "
+ "cannot access database directory \"%s\" (%d).\n",
+ be->be_suffix[0].bv_val, mdb->mi_dbenv_home, errno );
+ return -1;
+ }
+
+ /* mdb is always clean */
+ be->be_flags |= SLAP_DBFLAG_CLEAN;
+
+ rc = mdb_env_create( &mdb->mi_dbenv );
+ if( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ LDAP_XSTRING(mdb_db_open) ": database \"%s\": "
+ "mdb_env_create failed: %s (%d).\n",
+ be->be_suffix[0].bv_val, mdb_strerror(rc), rc );
+ goto fail;
+ }
+
+ if ( mdb->mi_readers ) {
+ rc = mdb_env_set_maxreaders( mdb->mi_dbenv, mdb->mi_readers );
+ if( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ LDAP_XSTRING(mdb_db_open) ": database \"%s\": "
+ "mdb_env_set_maxreaders failed: %s (%d).\n",
+ be->be_suffix[0].bv_val, mdb_strerror(rc), rc );
+ goto fail;
+ }
+ }
+
+ rc = mdb_env_set_mapsize( mdb->mi_dbenv, mdb->mi_mapsize );
+ if( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ LDAP_XSTRING(mdb_db_open) ": database \"%s\": "
+ "mdb_env_set_mapsize failed: %s (%d).\n",
+ be->be_suffix[0].bv_val, mdb_strerror(rc), rc );
+ goto fail;
+ }
+
+ rc = mdb_env_set_maxdbs( mdb->mi_dbenv, MDB_INDICES );
+ if( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ LDAP_XSTRING(mdb_db_open) ": database \"%s\": "
+ "mdb_env_set_maxdbs failed: %s (%d).\n",
+ be->be_suffix[0].bv_val, mdb_strerror(rc), rc );
+ goto fail;
+ }
+
+#ifdef HAVE_EBCDIC
+ strcpy( path, mdb->mi_dbenv_home );
+ __atoe( path );
+ dbhome = path;
+#else
+ dbhome = mdb->mi_dbenv_home;
+#endif
+
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_db_open) ": database \"%s\": "
+ "dbenv_open(%s).\n",
+ be->be_suffix[0].bv_val, mdb->mi_dbenv_home, 0);
+
+ flags = mdb->mi_dbenv_flags;
+
+ if ( slapMode & SLAP_TOOL_QUICK )
+ flags |= MDB_NOSYNC;
+
+ if ( slapMode & SLAP_TOOL_READONLY)
+ flags |= MDB_RDONLY;
+
+ rc = mdb_env_open( mdb->mi_dbenv, dbhome,
+ flags, mdb->mi_dbenv_mode );
+
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ LDAP_XSTRING(mdb_db_open) ": database \"%s\" cannot be opened, err %d. "
+ "Restore from backup!\n",
+ be->be_suffix[0].bv_val, rc, 0 );
+ goto fail;
+ }
+
+ rc = mdb_txn_begin( mdb->mi_dbenv, 0, &txn );
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ LDAP_XSTRING(mdb_db_open) ": database \"%s\" cannot be opened, err %d. "
+ "Restore from backup!\n",
+ be->be_suffix[0].bv_val, rc, 0 );
+ goto fail;
+ }
+
+ /* open (and create) main databases */
+ for( i = 0; mdmi_databases[i].bv_val; i++ ) {
+ flags = MDB_INTEGERKEY;
+ if( i == MDB_ID2ENTRY ) {
+ if ( !(slapMode & (SLAP_TOOL_READMAIN|SLAP_TOOL_READONLY) ))
+ flags |= MDB_CREATE;
+ } else {
+ if ( i == MDB_DN2ID )
+ flags |= MDB_DUPSORT;
+ if ( !(slapMode & SLAP_TOOL_READONLY) )
+ flags |= MDB_CREATE;
+ }
+
+ rc = mdb_open( txn,
+ mdmi_databases[i].bv_val,
+ flags,
+ &mdb->mi_dbis[i] );
+
+ if ( rc != 0 ) {
+ snprintf( cr->msg, sizeof(cr->msg), "database \"%s\": "
+ "mdb_open(%s/%s) failed: %s (%d).",
+ be->be_suffix[0].bv_val,
+ mdb->mi_dbenv_home, mdmi_databases[i].bv_val,
+ mdb_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ LDAP_XSTRING(mdb_db_open) ": %s\n",
+ cr->msg, 0, 0 );
+ goto fail;
+ }
+
+ if ( i == MDB_DN2ID )
+ mdb_set_dupsort( txn, mdb->mi_dbis[i], mdb_dup_compare );
+
+ }
+
+ rc = mdb_attr_dbs_open( be, txn, cr );
+ if ( rc ) {
+ mdb_txn_abort( txn );
+ goto fail;
+ }
+
+ rc = mdb_txn_commit(txn);
+ if ( rc != 0 ) {
+ goto fail;
+ }
+
+ /* monitor setup */
+ rc = mdb_monitor_db_open( be );
+ if ( rc != 0 ) {
+ goto fail;
+ }
+
+ mdb->mi_flags |= MDB_IS_OPEN;
+
+ return 0;
+
+fail:
+ mdb_db_close( be, NULL );
+ return rc;
+}
+
+static int
+mdb_db_close( BackendDB *be, ConfigReply *cr )
+{
+ int rc;
+ struct mdb_info *mdb = (struct mdb_info *) be->be_private;
+ MDB_txn *txn;
+
+ /* monitor handling */
+ (void)mdb_monitor_db_close( be );
+
+ mdb->mi_flags &= ~MDB_IS_OPEN;
+
+ if( mdb->mi_dbenv ) {
+ mdb_reader_flush( mdb->mi_dbenv );
+ }
+
+ if ( mdb->mi_dbenv ) {
+ if ( mdb->mi_dbis[0] ) {
+ int i;
+ rc = mdb_txn_begin( mdb->mi_dbenv, MDB_RDONLY, &txn );
+
+ mdb_attr_dbs_close( mdb, txn );
+ for ( i=0; i<MDB_NDB; i++ )
+ mdb_close( txn, mdb->mi_dbis[i] );
+
+ mdb_txn_abort( txn );
+
+ /* force a sync */
+ rc = mdb_env_sync( mdb->mi_dbenv, 1 );
+ if( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "mdb_db_close: database \"%s\": "
+ "mdb_env_sync failed: %s (%d).\n",
+ be->be_suffix[0].bv_val, mdb_strerror(rc), rc );
+ }
+ }
+
+ mdb_env_close( mdb->mi_dbenv );
+ mdb->mi_dbenv = NULL;
+ }
+
+ return 0;
+}
+
+static int
+mdb_db_destroy( BackendDB *be, ConfigReply *cr )
+{
+ struct mdb_info *mdb = (struct mdb_info *) be->be_private;
+
+ /* stop and remove checkpoint task */
+ if ( mdb->mi_txn_cp_task ) {
+ struct re_s *re = mdb->mi_txn_cp_task;
+ mdb->mi_txn_cp_task = NULL;
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ if ( ldap_pvt_runqueue_isrunning( &slapd_rq, re ) )
+ ldap_pvt_runqueue_stoptask( &slapd_rq, re );
+ ldap_pvt_runqueue_remove( &slapd_rq, re );
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+ }
+
+ /* monitor handling */
+ (void)mdb_monitor_db_destroy( be );
+
+ if( mdb->mi_dbenv_home ) ch_free( mdb->mi_dbenv_home );
+
+ mdb_attr_index_destroy( mdb );
+
+ ch_free( mdb );
+ be->be_private = NULL;
+
+ return 0;
+}
+
+int
+mdb_back_initialize(
+ BackendInfo *bi )
+{
+ int rc;
+
+ static char *controls[] = {
+ LDAP_CONTROL_ASSERT,
+ LDAP_CONTROL_MANAGEDSAIT,
+ LDAP_CONTROL_NOOP,
+ LDAP_CONTROL_PAGEDRESULTS,
+ LDAP_CONTROL_PRE_READ,
+ LDAP_CONTROL_POST_READ,
+ LDAP_CONTROL_SUBENTRIES,
+ LDAP_CONTROL_X_PERMISSIVE_MODIFY,
+#ifdef LDAP_X_TXN
+ LDAP_CONTROL_X_TXN_SPEC,
+#endif
+ NULL
+ };
+
+ /* initialize the underlying database system */
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_back_initialize) ": initialize "
+ MDB_UCTYPE " backend\n", 0, 0, 0 );
+
+ bi->bi_flags |=
+ SLAP_BFLAG_INCREMENT |
+ SLAP_BFLAG_SUBENTRIES |
+ SLAP_BFLAG_ALIASES |
+ SLAP_BFLAG_REFERRALS;
+
+ bi->bi_controls = controls;
+
+ { /* version check */
+ int major, minor, patch, ver;
+ char *version = mdb_version( &major, &minor, &patch );
+#ifdef HAVE_EBCDIC
+ char v2[1024];
+
+ /* All our stdio does an ASCII to EBCDIC conversion on
+ * the output. Strings from the MDB library are already
+ * in EBCDIC; we have to go back and forth...
+ */
+ strcpy( v2, version );
+ __etoa( v2 );
+ version = v2;
+#endif
+ ver = (major << 24) | (minor << 16) | patch;
+ if( ver != MDB_VERSION_FULL ) {
+ /* fail if a versions don't match */
+ Debug( LDAP_DEBUG_ANY,
+ LDAP_XSTRING(mdb_back_initialize) ": "
+ "MDB library version mismatch:"
+ " expected " MDB_VERSION_STRING ","
+ " got %s\n", version, 0, 0 );
+ return -1;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(mdb_back_initialize)
+ ": %s\n", version, 0, 0 );
+ }
+
+ bi->bi_open = 0;
+ bi->bi_close = 0;
+ bi->bi_config = 0;
+ bi->bi_destroy = 0;
+
+ bi->bi_db_init = mdb_db_init;
+ bi->bi_db_config = config_generic_wrapper;
+ bi->bi_db_open = mdb_db_open;
+ bi->bi_db_close = mdb_db_close;
+ bi->bi_db_destroy = mdb_db_destroy;
+
+ bi->bi_op_add = mdb_add;
+ bi->bi_op_bind = mdb_bind;
+ bi->bi_op_compare = mdb_compare;
+ bi->bi_op_delete = mdb_delete;
+ bi->bi_op_modify = mdb_modify;
+ bi->bi_op_modrdn = mdb_modrdn;
+ bi->bi_op_search = mdb_search;
+
+ bi->bi_op_unbind = 0;
+
+ bi->bi_extended = mdb_extended;
+
+#if 0 /* FIXME: Redundant, why does this exist? */
+ bi->bi_chk_referrals = mdb_referrals;
+#endif
+ bi->bi_operational = mdb_operational;
+
+ bi->bi_has_subordinates = mdb_hasSubordinates;
+ bi->bi_entry_release_rw = mdb_entry_release;
+ bi->bi_entry_get_rw = mdb_entry_get;
+
+ /*
+ * hooks for slap tools
+ */
+ bi->bi_tool_entry_open = mdb_tool_entry_open;
+ bi->bi_tool_entry_close = mdb_tool_entry_close;
+ bi->bi_tool_entry_first = backend_tool_entry_first;
+ bi->bi_tool_entry_first_x = mdb_tool_entry_first_x;
+ bi->bi_tool_entry_next = mdb_tool_entry_next;
+ bi->bi_tool_entry_get = mdb_tool_entry_get;
+ bi->bi_tool_entry_put = mdb_tool_entry_put;
+ bi->bi_tool_entry_reindex = mdb_tool_entry_reindex;
+ bi->bi_tool_sync = 0;
+ bi->bi_tool_dn2id_get = mdb_tool_dn2id_get;
+ bi->bi_tool_entry_modify = mdb_tool_entry_modify;
+
+ bi->bi_connection_init = 0;
+ bi->bi_connection_destroy = 0;
+
+ rc = mdb_back_init_cf( bi );
+
+ return rc;
+}
+
+#if (SLAPD_MDB == SLAPD_MOD_DYNAMIC)
+
+SLAP_BACKEND_INIT_MODULE( mdb )
+
+#endif /* SLAPD_MDB == SLAPD_MOD_DYNAMIC */
+
--- /dev/null
+/* index.c - routines for dealing with attribute indexes */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2011 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "back-mdb.h"
+#include "idl.h"
+
+/* read a key */
+int
+mdb_key_read(
+ Backend *be,
+ MDB_txn *txn,
+ MDB_dbi dbi,
+ struct berval *k,
+ ID *ids,
+ MDB_cursor **saved_cursor,
+ int get_flag
+)
+{
+ int rc;
+ MDB_val key;
+
+ Debug( LDAP_DEBUG_TRACE, "=> key_read\n", 0, 0, 0 );
+
+ key.mv_size = k->bv_len;
+ key.mv_data = k->bv_val;
+
+ rc = mdb_idl_fetch_key( be, txn, dbi, &key, ids, saved_cursor, get_flag );
+
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "<= mdb_index_read: failed (%d)\n",
+ rc, 0, 0 );
+ } else {
+ Debug( LDAP_DEBUG_TRACE, "<= mdb_index_read %ld candidates\n",
+ (long) MDB_IDL_N(ids), 0, 0 );
+ }
+
+ return rc;
+}
+
+/* Add or remove stuff from index files */
+int
+mdb_key_change(
+ Backend *be,
+ MDB_txn *txn,
+ MDB_dbi dbi,
+ struct berval *k,
+ ID id,
+ int op
+)
+{
+ int rc;
+ MDB_val key;
+
+ Debug( LDAP_DEBUG_TRACE, "=> key_change(%s,%lx)\n",
+ op == SLAP_INDEX_ADD_OP ? "ADD":"DELETE", (long) id, 0 );
+
+ key.mv_size = k->bv_len;
+ key.mv_data = k->bv_val;
+
+ if (op == SLAP_INDEX_ADD_OP) {
+ /* Add values */
+ rc = mdb_idl_insert_key( be, txn, dbi, &key, id );
+ if ( rc == MDB_KEYEXIST ) rc = 0;
+ } else {
+ /* Delete values */
+ rc = mdb_idl_delete_key( be, txn, dbi, &key, id );
+ if ( rc == MDB_NOTFOUND ) rc = 0;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "<= key_change %d\n", rc, 0, 0 );
+
+ return rc;
+}
--- /dev/null
+/* modify.c - mdb backend modify routine */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2011 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "back-mdb.h"
+
+static struct berval scbva[] = {
+ BER_BVC("glue"),
+ BER_BVNULL
+};
+
+static void
+mdb_modify_idxflags(
+ Operation *op,
+ AttributeDescription *desc,
+ int got_delete,
+ Attribute *newattrs,
+ Attribute *oldattrs )
+{
+ struct berval ix_at;
+ AttrInfo *ai;
+
+ /* check if modified attribute was indexed
+ * but not in case of NOOP... */
+ ai = mdb_index_mask( op->o_bd, desc, &ix_at );
+ if ( ai ) {
+ if ( got_delete ) {
+ Attribute *ap;
+ struct berval ix2;
+
+ ap = attr_find( oldattrs, desc );
+ if ( ap ) ap->a_flags |= SLAP_ATTR_IXDEL;
+
+ /* Find all other attrs that index to same slot */
+ for ( ap = newattrs; ap; ap = ap->a_next ) {
+ ai = mdb_index_mask( op->o_bd, ap->a_desc, &ix2 );
+ if ( ai && ix2.bv_val == ix_at.bv_val )
+ ap->a_flags |= SLAP_ATTR_IXADD;
+ }
+
+ } else {
+ Attribute *ap;
+
+ ap = attr_find( newattrs, desc );
+ if ( ap ) ap->a_flags |= SLAP_ATTR_IXADD;
+ }
+ }
+}
+
+int mdb_modify_internal(
+ Operation *op,
+ MDB_txn *tid,
+ Modifications *modlist,
+ Entry *e,
+ const char **text,
+ char *textbuf,
+ size_t textlen )
+{
+ int rc, err;
+ Modification *mod;
+ Modifications *ml;
+ Attribute *save_attrs;
+ Attribute *ap;
+ int glue_attr_delete = 0;
+ int got_delete;
+
+ Debug( LDAP_DEBUG_TRACE, "mdb_modify_internal: 0x%08lx: %s\n",
+ e->e_id, e->e_dn, 0);
+
+ if ( !acl_check_modlist( op, e, modlist )) {
+ return LDAP_INSUFFICIENT_ACCESS;
+ }
+
+ /* save_attrs will be disposed of by mdb_cache_modify */
+ save_attrs = e->e_attrs;
+ e->e_attrs = attrs_dup( e->e_attrs );
+
+ for ( ml = modlist; ml != NULL; ml = ml->sml_next ) {
+ int match;
+ mod = &ml->sml_mod;
+ switch( mod->sm_op ) {
+ case LDAP_MOD_ADD:
+ case LDAP_MOD_REPLACE:
+ if ( mod->sm_desc == slap_schema.si_ad_structuralObjectClass ) {
+ value_match( &match, slap_schema.si_ad_structuralObjectClass,
+ slap_schema.si_ad_structuralObjectClass->
+ ad_type->sat_equality,
+ SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
+ &mod->sm_values[0], &scbva[0], text );
+ if ( !match ) glue_attr_delete = 1;
+ }
+ }
+ if ( glue_attr_delete )
+ break;
+ }
+
+ if ( glue_attr_delete ) {
+ Attribute **app = &e->e_attrs;
+ while ( *app != NULL ) {
+ if ( !is_at_operational( (*app)->a_desc->ad_type )) {
+ Attribute *save = *app;
+ *app = (*app)->a_next;
+ attr_free( save );
+ continue;
+ }
+ app = &(*app)->a_next;
+ }
+ }
+
+ for ( ml = modlist; ml != NULL; ml = ml->sml_next ) {
+ mod = &ml->sml_mod;
+ got_delete = 0;
+
+ switch ( mod->sm_op ) {
+ case LDAP_MOD_ADD:
+ Debug(LDAP_DEBUG_ARGS,
+ "mdb_modify_internal: add %s\n",
+ mod->sm_desc->ad_cname.bv_val, 0, 0);
+ err = modify_add_values( e, mod, get_permissiveModify(op),
+ text, textbuf, textlen );
+ if( err != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_ARGS, "mdb_modify_internal: %d %s\n",
+ err, *text, 0);
+ }
+ break;
+
+ case LDAP_MOD_DELETE:
+ if ( glue_attr_delete ) {
+ err = LDAP_SUCCESS;
+ break;
+ }
+
+ Debug(LDAP_DEBUG_ARGS,
+ "mdb_modify_internal: delete %s\n",
+ mod->sm_desc->ad_cname.bv_val, 0, 0);
+ err = modify_delete_values( e, mod, get_permissiveModify(op),
+ text, textbuf, textlen );
+ if( err != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_ARGS, "mdb_modify_internal: %d %s\n",
+ err, *text, 0);
+ } else {
+ got_delete = 1;
+ }
+ break;
+
+ case LDAP_MOD_REPLACE:
+ Debug(LDAP_DEBUG_ARGS,
+ "mdb_modify_internal: replace %s\n",
+ mod->sm_desc->ad_cname.bv_val, 0, 0);
+ err = modify_replace_values( e, mod, get_permissiveModify(op),
+ text, textbuf, textlen );
+ if( err != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_ARGS, "mdb_modify_internal: %d %s\n",
+ err, *text, 0);
+ } else {
+ got_delete = 1;
+ }
+ break;
+
+ case LDAP_MOD_INCREMENT:
+ Debug(LDAP_DEBUG_ARGS,
+ "mdb_modify_internal: increment %s\n",
+ mod->sm_desc->ad_cname.bv_val, 0, 0);
+ err = modify_increment_values( e, mod, get_permissiveModify(op),
+ text, textbuf, textlen );
+ if( err != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_ARGS,
+ "mdb_modify_internal: %d %s\n",
+ err, *text, 0);
+ } else {
+ got_delete = 1;
+ }
+ break;
+
+ case SLAP_MOD_SOFTADD:
+ Debug(LDAP_DEBUG_ARGS,
+ "mdb_modify_internal: softadd %s\n",
+ mod->sm_desc->ad_cname.bv_val, 0, 0);
+ /* Avoid problems in index_add_mods()
+ * We need to add index if necessary.
+ */
+ mod->sm_op = LDAP_MOD_ADD;
+
+ err = modify_add_values( e, mod, get_permissiveModify(op),
+ text, textbuf, textlen );
+
+ mod->sm_op = SLAP_MOD_SOFTADD;
+
+ if ( err == LDAP_TYPE_OR_VALUE_EXISTS ) {
+ err = LDAP_SUCCESS;
+ }
+
+ if( err != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_ARGS, "mdb_modify_internal: %d %s\n",
+ err, *text, 0);
+ }
+ break;
+
+ case SLAP_MOD_SOFTDEL:
+ Debug(LDAP_DEBUG_ARGS,
+ "mdb_modify_internal: softdel %s\n",
+ mod->sm_desc->ad_cname.bv_val, 0, 0);
+ /* Avoid problems in index_delete_mods()
+ * We need to add index if necessary.
+ */
+ mod->sm_op = LDAP_MOD_DELETE;
+
+ err = modify_delete_values( e, mod, get_permissiveModify(op),
+ text, textbuf, textlen );
+
+ mod->sm_op = SLAP_MOD_SOFTDEL;
+
+ if ( err == LDAP_NO_SUCH_ATTRIBUTE ) {
+ err = LDAP_SUCCESS;
+ }
+
+ if( err != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_ARGS, "mdb_modify_internal: %d %s\n",
+ err, *text, 0);
+ }
+ break;
+
+ case SLAP_MOD_ADD_IF_NOT_PRESENT:
+ if ( attr_find( e->e_attrs, mod->sm_desc ) != NULL ) {
+ /* skip */
+ err = LDAP_SUCCESS;
+ break;
+ }
+
+ Debug(LDAP_DEBUG_ARGS,
+ "mdb_modify_internal: add_if_not_present %s\n",
+ mod->sm_desc->ad_cname.bv_val, 0, 0);
+ /* Avoid problems in index_add_mods()
+ * We need to add index if necessary.
+ */
+ mod->sm_op = LDAP_MOD_ADD;
+
+ err = modify_add_values( e, mod, get_permissiveModify(op),
+ text, textbuf, textlen );
+
+ mod->sm_op = SLAP_MOD_ADD_IF_NOT_PRESENT;
+
+ if( err != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_ARGS, "mdb_modify_internal: %d %s\n",
+ err, *text, 0);
+ }
+ break;
+
+ default:
+ Debug(LDAP_DEBUG_ANY, "mdb_modify_internal: invalid op %d\n",
+ mod->sm_op, 0, 0);
+ *text = "Invalid modify operation";
+ err = LDAP_OTHER;
+ Debug(LDAP_DEBUG_ARGS, "mdb_modify_internal: %d %s\n",
+ err, *text, 0);
+ }
+
+ if ( err != LDAP_SUCCESS ) {
+ attrs_free( e->e_attrs );
+ e->e_attrs = save_attrs;
+ /* unlock entry, delete from cache */
+ return err;
+ }
+
+ /* If objectClass was modified, reset the flags */
+ if ( mod->sm_desc == slap_schema.si_ad_objectClass ) {
+ e->e_ocflags = 0;
+ }
+
+ if ( glue_attr_delete ) e->e_ocflags = 0;
+
+
+ /* check if modified attribute was indexed
+ * but not in case of NOOP... */
+ if ( !op->o_noop ) {
+ mdb_modify_idxflags( op, mod->sm_desc, got_delete, e->e_attrs, save_attrs );
+ }
+ }
+
+ /* check that the entry still obeys the schema */
+ ap = NULL;
+ rc = entry_schema_check( op, e, save_attrs, get_relax(op), 0, &ap,
+ text, textbuf, textlen );
+ if ( rc != LDAP_SUCCESS || op->o_noop ) {
+ attrs_free( e->e_attrs );
+ /* clear the indexing flags */
+ for ( ap = save_attrs; ap != NULL; ap = ap->a_next ) {
+ ap->a_flags &= ~(SLAP_ATTR_IXADD|SLAP_ATTR_IXDEL);
+ }
+ e->e_attrs = save_attrs;
+
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "entry failed schema check: %s\n",
+ *text, 0, 0 );
+ }
+
+ /* if NOOP then silently revert to saved attrs */
+ return rc;
+ }
+
+ /* structuralObjectClass modified! */
+ if ( ap ) {
+ assert( ap->a_desc == slap_schema.si_ad_structuralObjectClass );
+ if ( !op->o_noop ) {
+ mdb_modify_idxflags( op, slap_schema.si_ad_structuralObjectClass,
+ 1, e->e_attrs, save_attrs );
+ }
+ }
+
+ /* update the indices of the modified attributes */
+
+ /* start with deleting the old index entries */
+ for ( ap = save_attrs; ap != NULL; ap = ap->a_next ) {
+ if ( ap->a_flags & SLAP_ATTR_IXDEL ) {
+ struct berval *vals;
+ Attribute *a2;
+ ap->a_flags &= ~SLAP_ATTR_IXDEL;
+ a2 = attr_find( e->e_attrs, ap->a_desc );
+ if ( a2 ) {
+ /* need to detect which values were deleted */
+ int i, j;
+ vals = op->o_tmpalloc( (ap->a_numvals + 1) *
+ sizeof(struct berval), op->o_tmpmemctx );
+ j = 0;
+ for ( i=0; i < ap->a_numvals; i++ ) {
+ rc = attr_valfind( a2, SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
+ &ap->a_nvals[i], NULL, op->o_tmpmemctx );
+ /* Save deleted values */
+ if ( rc == LDAP_NO_SUCH_ATTRIBUTE )
+ vals[j++] = ap->a_nvals[i];
+ }
+ BER_BVZERO(vals+j);
+ } else {
+ /* attribute was completely deleted */
+ vals = ap->a_nvals;
+ }
+ rc = 0;
+ if ( !BER_BVISNULL( vals )) {
+ rc = mdb_index_values( op, tid, ap->a_desc,
+ vals, e->e_id, SLAP_INDEX_DELETE_OP );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: attribute \"%s\" index delete failure\n",
+ op->o_log_prefix, ap->a_desc->ad_cname.bv_val, 0 );
+ attrs_free( e->e_attrs );
+ e->e_attrs = save_attrs;
+ }
+ }
+ if ( vals != ap->a_nvals )
+ op->o_tmpfree( vals, op->o_tmpmemctx );
+ if ( rc ) return rc;
+ }
+ }
+
+ /* add the new index entries */
+ for ( ap = e->e_attrs; ap != NULL; ap = ap->a_next ) {
+ if (ap->a_flags & SLAP_ATTR_IXADD) {
+ ap->a_flags &= ~SLAP_ATTR_IXADD;
+ rc = mdb_index_values( op, tid, ap->a_desc,
+ ap->a_nvals,
+ e->e_id, SLAP_INDEX_ADD_OP );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: attribute \"%s\" index add failure\n",
+ op->o_log_prefix, ap->a_desc->ad_cname.bv_val, 0 );
+ attrs_free( e->e_attrs );
+ e->e_attrs = save_attrs;
+ return rc;
+ }
+ }
+ }
+
+ return rc;
+}
+
+
+int
+mdb_modify( Operation *op, SlapReply *rs )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ Entry *e = NULL;
+ int manageDSAit = get_manageDSAit( op );
+ char textbuf[SLAP_TEXT_BUFLEN];
+ size_t textlen = sizeof textbuf;
+ MDB_txn *txn = NULL;
+ mdb_op_info opinfo = {{{ 0 }}}, *moi = &opinfo;
+ Entry dummy = {0};
+
+ LDAPControl **preread_ctrl = NULL;
+ LDAPControl **postread_ctrl = NULL;
+ LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
+ int num_ctrls = 0;
+
+#ifdef LDAP_X_TXN
+ int settle = 0;
+#endif
+
+ Debug( LDAP_DEBUG_ARGS, LDAP_XSTRING(mdb_modify) ": %s\n",
+ op->o_req_dn.bv_val, 0, 0 );
+
+#ifdef LDAP_X_TXN
+ if( op->o_txnSpec ) {
+ /* acquire connection lock */
+ ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
+ if( op->o_conn->c_txn == CONN_TXN_INACTIVE ) {
+ rs->sr_text = "invalid transaction identifier";
+ rs->sr_err = LDAP_X_TXN_ID_INVALID;
+ goto txnReturn;
+ } else if( op->o_conn->c_txn == CONN_TXN_SETTLE ) {
+ settle=1;
+ goto txnReturn;
+ }
+
+ if( op->o_conn->c_txn_backend == NULL ) {
+ op->o_conn->c_txn_backend = op->o_bd;
+
+ } else if( op->o_conn->c_txn_backend != op->o_bd ) {
+ rs->sr_text = "transaction cannot span multiple database contexts";
+ rs->sr_err = LDAP_AFFECTS_MULTIPLE_DSAS;
+ goto txnReturn;
+ }
+
+ /* insert operation into transaction */
+
+ rs->sr_text = "transaction specified";
+ rs->sr_err = LDAP_X_TXN_SPECIFY_OKAY;
+
+txnReturn:
+ /* release connection lock */
+ ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
+
+ if( !settle ) {
+ send_ldap_result( op, rs );
+ return rs->sr_err;
+ }
+ }
+#endif
+
+ ctrls[num_ctrls] = NULL;
+
+ /* Don't touch the opattrs, if this is a contextCSN update
+ * initiated from updatedn */
+ if ( !be_isupdate(op) || !op->orm_modlist || op->orm_modlist->sml_next ||
+ op->orm_modlist->sml_desc != slap_schema.si_ad_contextCSN ) {
+
+ slap_mods_opattrs( op, &op->orm_modlist, 1 );
+ }
+
+ /* begin transaction */
+ rs->sr_err = mdb_opinfo_get( op, mdb, 0, &moi );
+ rs->sr_text = NULL;
+ if( rs->sr_err != 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_modify) ": txn_begin failed: "
+ "%s (%d)\n", mdb_strerror(rs->sr_err), rs->sr_err, 0 );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ txn = moi->moi_txn;
+
+ /* get entry or ancestor */
+ rs->sr_err = mdb_dn2entry( op, txn, &op->o_req_ndn, &e, 1 );
+
+ if ( rs->sr_err != 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_modify) ": dn2entry failed (%d)\n",
+ rs->sr_err, 0, 0 );
+ switch( rs->sr_err ) {
+ case MDB_NOTFOUND:
+ break;
+ case LDAP_BUSY:
+ rs->sr_text = "ldap server busy";
+ goto return_results;
+ default:
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+ }
+
+ /* acquire and lock entry */
+ /* FIXME: dn2entry() should return non-glue entry */
+ if (( rs->sr_err == MDB_NOTFOUND ) ||
+ ( !manageDSAit && e && is_entry_glue( e )))
+ {
+ if ( e != NULL ) {
+ rs->sr_matched = ch_strdup( e->e_dn );
+ if ( is_entry_referral( e )) {
+ BerVarray ref = get_entry_referrals( op, e );
+ rs->sr_ref = referral_rewrite( ref, &e->e_name,
+ &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+ ber_bvarray_free( ref );
+ } else {
+ rs->sr_ref = NULL;
+ }
+ mdb_entry_return( e );
+ e = NULL;
+
+ } else {
+ rs->sr_ref = referral_rewrite( default_referral, NULL,
+ &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+ }
+
+ rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
+ rs->sr_err = LDAP_REFERRAL;
+ send_ldap_result( op, rs );
+ goto done;
+ }
+
+ if ( !manageDSAit && is_entry_referral( e ) ) {
+ /* entry is a referral, don't allow modify */
+ rs->sr_ref = get_entry_referrals( op, e );
+
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_modify) ": entry is referral\n",
+ 0, 0, 0 );
+
+ rs->sr_err = LDAP_REFERRAL;
+ rs->sr_matched = e->e_name.bv_val;
+ rs->sr_flags = REP_REF_MUSTBEFREED;
+ send_ldap_result( op, rs );
+ rs->sr_matched = NULL;
+ goto done;
+ }
+
+ if ( get_assert( op ) &&
+ ( test_filter( op, e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
+ {
+ rs->sr_err = LDAP_ASSERTION_FAILED;
+ goto return_results;
+ }
+
+ if( op->o_preread ) {
+ if( preread_ctrl == NULL ) {
+ preread_ctrl = &ctrls[num_ctrls++];
+ ctrls[num_ctrls] = NULL;
+ }
+ if ( slap_read_controls( op, rs, e,
+ &slap_pre_read_bv, preread_ctrl ) )
+ {
+ Debug( LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(mdb_modify) ": pre-read "
+ "failed!\n", 0, 0, 0 );
+ if ( op->o_preread & SLAP_CONTROL_CRITICAL ) {
+ /* FIXME: is it correct to abort
+ * operation if control fails? */
+ goto return_results;
+ }
+ }
+ }
+
+ /* Modify the entry */
+ dummy = *e;
+ rs->sr_err = mdb_modify_internal( op, txn, op->orm_modlist,
+ &dummy, &rs->sr_text, textbuf, textlen );
+
+ if( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_modify) ": modify failed (%d)\n",
+ rs->sr_err, 0, 0 );
+ /* Only free attrs if they were dup'd. */
+ if ( dummy.e_attrs == e->e_attrs ) dummy.e_attrs = NULL;
+ goto return_results;
+ }
+
+ /* change the entry itself */
+ rs->sr_err = mdb_id2entry_update( op, txn, &dummy );
+ if ( rs->sr_err != 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_modify) ": id2entry update failed " "(%d)\n",
+ rs->sr_err, 0, 0 );
+ rs->sr_text = "entry update failed";
+ goto return_results;
+ }
+
+ if( op->o_postread ) {
+ if( postread_ctrl == NULL ) {
+ postread_ctrl = &ctrls[num_ctrls++];
+ ctrls[num_ctrls] = NULL;
+ }
+ if( slap_read_controls( op, rs, &dummy,
+ &slap_post_read_bv, postread_ctrl ) )
+ {
+ Debug( LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(mdb_modify)
+ ": post-read failed!\n", 0, 0, 0 );
+ if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
+ /* FIXME: is it correct to abort
+ * operation if control fails? */
+ goto return_results;
+ }
+ }
+ }
+
+ /* Only free attrs if they were dup'd. */
+ if ( dummy.e_attrs == e->e_attrs ) dummy.e_attrs = NULL;
+ if( moi == &opinfo ) {
+ LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.moi_oe, OpExtra, oe_next );
+ opinfo.moi_oe.oe_key = NULL;
+ if( op->o_noop ) {
+ mdb_txn_abort( txn );
+ rs->sr_err = LDAP_X_NO_OPERATION;
+ txn = NULL;
+ goto return_results;
+ } else {
+ rs->sr_err = mdb_txn_commit( txn );
+ txn = NULL;
+ }
+ }
+
+ if( rs->sr_err != 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_modify) ": txn_%s failed: %s (%d)\n",
+ op->o_noop ? "abort (no-op)" : "commit",
+ mdb_strerror(rs->sr_err), rs->sr_err );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "commit failed";
+
+ goto return_results;
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_modify) ": updated%s id=%08lx dn=\"%s\"\n",
+ op->o_noop ? " (no-op)" : "",
+ dummy.e_id, op->o_req_dn.bv_val );
+
+ rs->sr_err = LDAP_SUCCESS;
+ rs->sr_text = NULL;
+ if( num_ctrls ) rs->sr_ctrls = ctrls;
+
+return_results:
+ if( dummy.e_attrs ) {
+ attrs_free( dummy.e_attrs );
+ }
+ send_ldap_result( op, rs );
+
+#if 0
+ if( rs->sr_err == LDAP_SUCCESS && mdb->bi_txn_cp_kbyte ) {
+ TXN_CHECKPOINT( mdb->bi_dbenv,
+ mdb->bi_txn_cp_kbyte, mdb->bi_txn_cp_min, 0 );
+ }
+#endif
+
+done:
+ slap_graduate_commit_csn( op );
+
+ if( moi == &opinfo ) {
+ if( txn != NULL ) {
+ mdb_txn_abort( txn );
+ }
+ if ( opinfo.moi_oe.oe_key ) {
+ LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.moi_oe, OpExtra, oe_next );
+ }
+ }
+
+ if( e != NULL ) {
+ mdb_entry_return( e );
+ }
+
+ if( preread_ctrl != NULL && (*preread_ctrl) != NULL ) {
+ slap_sl_free( (*preread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
+ slap_sl_free( *preread_ctrl, op->o_tmpmemctx );
+ }
+ if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) {
+ slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
+ slap_sl_free( *postread_ctrl, op->o_tmpmemctx );
+ }
+
+ rs->sr_text = NULL;
+
+ return rs->sr_err;
+}
--- /dev/null
+/* modrdn.c - mdb backend modrdn routine */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2011 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-mdb.h"
+
+int
+mdb_modrdn( Operation *op, SlapReply *rs )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ AttributeDescription *children = slap_schema.si_ad_children;
+ AttributeDescription *entry = slap_schema.si_ad_entry;
+ struct berval p_dn, p_ndn;
+ struct berval new_dn = {0, NULL}, new_ndn = {0, NULL};
+ Entry *e = NULL;
+ Entry *p = NULL;
+ /* LDAP v2 supporting correct attribute handling. */
+ char textbuf[SLAP_TEXT_BUFLEN];
+ size_t textlen = sizeof textbuf;
+ MDB_txn *txn = NULL;
+ struct mdb_op_info opinfo = {{{ 0 }}}, *moi = &opinfo;
+ Entry dummy = {0};
+
+ Entry *np = NULL; /* newSuperior Entry */
+ struct berval *np_dn = NULL; /* newSuperior dn */
+ struct berval *np_ndn = NULL; /* newSuperior ndn */
+ struct berval *new_parent_dn = NULL; /* np_dn, p_dn, or NULL */
+
+ int manageDSAit = get_manageDSAit( op );
+
+ ID nid;
+ LDAPControl **preread_ctrl = NULL;
+ LDAPControl **postread_ctrl = NULL;
+ LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
+ int num_ctrls = 0;
+
+ int parent_is_glue = 0;
+ int parent_is_leaf = 0;
+
+#ifdef LDAP_X_TXN
+ int settle = 0;
+#endif
+
+ Debug( LDAP_DEBUG_TRACE, "==>" LDAP_XSTRING(mdb_modrdn) "(%s,%s,%s)\n",
+ op->o_req_dn.bv_val,op->oq_modrdn.rs_newrdn.bv_val,
+ op->oq_modrdn.rs_newSup ? op->oq_modrdn.rs_newSup->bv_val : "NULL" );
+
+#ifdef LDAP_X_TXN
+ if( op->o_txnSpec ) {
+ /* acquire connection lock */
+ ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
+ if( op->o_conn->c_txn == CONN_TXN_INACTIVE ) {
+ rs->sr_text = "invalid transaction identifier";
+ rs->sr_err = LDAP_X_TXN_ID_INVALID;
+ goto txnReturn;
+ } else if( op->o_conn->c_txn == CONN_TXN_SETTLE ) {
+ settle=1;
+ goto txnReturn;
+ }
+
+ if( op->o_conn->c_txn_backend == NULL ) {
+ op->o_conn->c_txn_backend = op->o_bd;
+
+ } else if( op->o_conn->c_txn_backend != op->o_bd ) {
+ rs->sr_text = "transaction cannot span multiple database contexts";
+ rs->sr_err = LDAP_AFFECTS_MULTIPLE_DSAS;
+ goto txnReturn;
+ }
+
+ /* insert operation into transaction */
+
+ rs->sr_text = "transaction specified";
+ rs->sr_err = LDAP_X_TXN_SPECIFY_OKAY;
+
+txnReturn:
+ /* release connection lock */
+ ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
+
+ if( !settle ) {
+ send_ldap_result( op, rs );
+ return rs->sr_err;
+ }
+ }
+#endif
+
+ ctrls[num_ctrls] = NULL;
+
+ slap_mods_opattrs( op, &op->orr_modlist, 1 );
+
+ /* begin transaction */
+ rs->sr_err = mdb_opinfo_get( op, mdb, 0, &moi );
+ rs->sr_text = NULL;
+ if( rs->sr_err != 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_modrdn) ": txn_begin failed: "
+ "%s (%d)\n", mdb_strerror(rs->sr_err), rs->sr_err, 0 );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ txn = moi->moi_txn;
+
+ if ( be_issuffix( op->o_bd, &op->o_req_ndn ) ) {
+#ifdef MDB_MULTIPLE_SUFFIXES
+ /* Allow renaming one suffix entry to another */
+ p_ndn = slap_empty_bv;
+#else
+ /* There can only be one suffix entry */
+ rs->sr_err = LDAP_NAMING_VIOLATION;
+ rs->sr_text = "cannot rename suffix entry";
+ goto return_results;
+#endif
+ } else {
+ dnParent( &op->o_req_ndn, &p_ndn );
+ }
+ np_ndn = &p_ndn;
+ /* Make sure parent entry exist and we can write its
+ * children.
+ */
+ rs->sr_err = mdb_dn2entry( op, txn, &p_ndn, &p, 0 );
+ switch( rs->sr_err ) {
+ case MDB_NOTFOUND:
+ Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(mdb_modrdn)
+ ": parent does not exist\n", 0, 0, 0);
+ rs->sr_ref = referral_rewrite( default_referral, NULL,
+ &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+ rs->sr_err = LDAP_REFERRAL;
+
+ send_ldap_result( op, rs );
+
+ ber_bvarray_free( rs->sr_ref );
+ goto done;
+ case 0:
+ break;
+ case LDAP_BUSY:
+ rs->sr_text = "ldap server busy";
+ goto return_results;
+ default:
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ /* check parent for "children" acl */
+ rs->sr_err = access_allowed( op, p,
+ children, NULL,
+ op->oq_modrdn.rs_newSup == NULL ?
+ ACL_WRITE : ACL_WDEL,
+ NULL );
+
+ if ( ! rs->sr_err ) {
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ Debug( LDAP_DEBUG_TRACE, "no access to parent\n", 0,
+ 0, 0 );
+ rs->sr_text = "no write access to parent's children";
+ goto return_results;
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_modrdn) ": wr to children "
+ "of entry %s OK\n", p_ndn.bv_val, 0, 0 );
+
+ if ( p_ndn.bv_val == slap_empty_bv.bv_val ) {
+ p_dn = slap_empty_bv;
+ } else {
+ dnParent( &op->o_req_dn, &p_dn );
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_modrdn) ": parent dn=%s\n",
+ p_dn.bv_val, 0, 0 );
+
+ /* get entry */
+ rs->sr_err = mdb_dn2entry( op, txn, &op->o_req_ndn, &e, 0 );
+ switch( rs->sr_err ) {
+ case MDB_NOTFOUND:
+ e = p;
+ p = NULL;
+ case 0:
+ break;
+ case LDAP_BUSY:
+ rs->sr_text = "ldap server busy";
+ goto return_results;
+ default:
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ /* FIXME: dn2entry() should return non-glue entry */
+ if (( rs->sr_err == MDB_NOTFOUND ) ||
+ ( !manageDSAit && e && is_entry_glue( e )))
+ {
+ if( e != NULL ) {
+ rs->sr_matched = ch_strdup( e->e_dn );
+ if ( is_entry_referral( e )) {
+ BerVarray ref = get_entry_referrals( op, e );
+ rs->sr_ref = referral_rewrite( ref, &e->e_name,
+ &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+ ber_bvarray_free( ref );
+ } else {
+ rs->sr_ref = NULL;
+ }
+ mdb_entry_return( e );
+ e = NULL;
+
+ } else {
+ rs->sr_ref = referral_rewrite( default_referral, NULL,
+ &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+ }
+
+ rs->sr_err = LDAP_REFERRAL;
+ send_ldap_result( op, rs );
+
+ ber_bvarray_free( rs->sr_ref );
+ free( (char *)rs->sr_matched );
+ rs->sr_ref = NULL;
+ rs->sr_matched = NULL;
+
+ goto done;
+ }
+
+ if ( get_assert( op ) &&
+ ( test_filter( op, e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
+ {
+ rs->sr_err = LDAP_ASSERTION_FAILED;
+ goto return_results;
+ }
+
+ /* check write on old entry */
+ rs->sr_err = access_allowed( op, e, entry, NULL, ACL_WRITE, NULL );
+ if ( ! rs->sr_err ) {
+ Debug( LDAP_DEBUG_TRACE, "no access to entry\n", 0,
+ 0, 0 );
+ rs->sr_text = "no write access to old entry";
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ goto return_results;
+ }
+
+ if (!manageDSAit && is_entry_referral( e ) ) {
+ /* entry is a referral, don't allow rename */
+ rs->sr_ref = get_entry_referrals( op, e );
+
+ Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(mdb_modrdn)
+ ": entry %s is referral\n", e->e_dn, 0, 0 );
+
+ rs->sr_err = LDAP_REFERRAL,
+ rs->sr_matched = e->e_name.bv_val;
+ send_ldap_result( op, rs );
+
+ ber_bvarray_free( rs->sr_ref );
+ rs->sr_ref = NULL;
+ rs->sr_matched = NULL;
+ goto done;
+ }
+
+ new_parent_dn = &p_dn; /* New Parent unless newSuperior given */
+
+ if ( op->oq_modrdn.rs_newSup != NULL ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_modrdn)
+ ": new parent \"%s\" requested...\n",
+ op->oq_modrdn.rs_newSup->bv_val, 0, 0 );
+
+ /* newSuperior == oldParent? */
+ if( dn_match( &p_ndn, op->oq_modrdn.rs_nnewSup ) ) {
+ Debug( LDAP_DEBUG_TRACE, "mdb_back_modrdn: "
+ "new parent \"%s\" same as the old parent \"%s\"\n",
+ op->oq_modrdn.rs_newSup->bv_val, p_dn.bv_val, 0 );
+ op->oq_modrdn.rs_newSup = NULL; /* ignore newSuperior */
+ }
+ }
+
+ /* There's a MDB_MULTIPLE_SUFFIXES case here that this code doesn't
+ * support. E.g., two suffixes dc=foo,dc=com and dc=bar,dc=net.
+ * We do not allow modDN
+ * dc=foo,dc=com
+ * newrdn dc=bar
+ * newsup dc=net
+ * and we probably should. But since MULTIPLE_SUFFIXES is deprecated
+ * I'm ignoring this problem for now.
+ */
+ if ( op->oq_modrdn.rs_newSup != NULL ) {
+ if ( op->oq_modrdn.rs_newSup->bv_len ) {
+ np_dn = op->oq_modrdn.rs_newSup;
+ np_ndn = op->oq_modrdn.rs_nnewSup;
+
+ /* newSuperior == oldParent? - checked above */
+ /* newSuperior == entry being moved?, if so ==> ERROR */
+ if ( dnIsSuffix( np_ndn, &e->e_nname )) {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ rs->sr_text = "new superior not found";
+ goto return_results;
+ }
+ /* Get Entry with dn=newSuperior. Does newSuperior exist? */
+
+ rs->sr_err = mdb_dn2entry( op, txn, np_ndn, &np, 0 );
+
+ switch( rs->sr_err ) {
+ case 0:
+ break;
+ case MDB_NOTFOUND:
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_modrdn)
+ ": newSup(ndn=%s) not here!\n",
+ np_ndn->bv_val, 0, 0);
+ rs->sr_text = "new superior not found";
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ goto return_results;
+ case LDAP_BUSY:
+ rs->sr_text = "ldap server busy";
+ goto return_results;
+ default:
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ /* check newSuperior for "children" acl */
+ rs->sr_err = access_allowed( op, np, children,
+ NULL, ACL_WADD, NULL );
+
+ if( ! rs->sr_err ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_modrdn)
+ ": no wr to newSup children\n",
+ 0, 0, 0 );
+ rs->sr_text = "no write access to new superior's children";
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ goto return_results;
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_modrdn)
+ ": wr to new parent OK np=%p, id=%ld\n",
+ (void *) np, (long) np->e_id, 0 );
+
+ if ( is_entry_alias( np ) ) {
+ /* parent is an alias, don't allow add */
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_modrdn)
+ ": entry is alias\n",
+ 0, 0, 0 );
+ rs->sr_text = "new superior is an alias";
+ rs->sr_err = LDAP_ALIAS_PROBLEM;
+ goto return_results;
+ }
+
+ if ( is_entry_referral( np ) ) {
+ /* parent is a referral, don't allow add */
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_modrdn)
+ ": entry is referral\n",
+ 0, 0, 0 );
+ rs->sr_text = "new superior is a referral";
+ rs->sr_err = LDAP_OTHER;
+ goto return_results;
+ }
+ new_parent_dn = &np->e_name;
+
+ } else {
+ np_dn = NULL;
+
+ /* no parent, modrdn entry directly under root */
+ if ( be_issuffix( op->o_bd, (struct berval *)&slap_empty_bv )
+ || be_isupdate( op ) ) {
+ np = (Entry *)&slap_entry_root;
+
+ /* check parent for "children" acl */
+ rs->sr_err = access_allowed( op, np,
+ children, NULL, ACL_WADD, NULL );
+
+ np = NULL;
+
+ if ( ! rs->sr_err ) {
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ Debug( LDAP_DEBUG_TRACE,
+ "no access to new superior\n",
+ 0, 0, 0 );
+ rs->sr_text =
+ "no write access to new superior's children";
+ goto return_results;
+ }
+ }
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_modrdn)
+ ": wr to new parent's children OK\n",
+ 0, 0, 0 );
+
+ new_parent_dn = np_dn;
+ }
+
+ /* Build target dn and make sure target entry doesn't exist already. */
+ if (!new_dn.bv_val) {
+ build_new_dn( &new_dn, new_parent_dn, &op->oq_modrdn.rs_newrdn, op->o_tmpmemctx );
+ }
+
+ if (!new_ndn.bv_val) {
+ dnNormalize( 0, NULL, NULL, &new_dn, &new_ndn, op->o_tmpmemctx );
+ }
+
+ Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(mdb_modrdn) ": new ndn=%s\n",
+ new_ndn.bv_val, 0, 0 );
+
+ /* Shortcut the search */
+ rs->sr_err = mdb_dn2id ( op, txn, &new_ndn, &nid, NULL, NULL );
+ switch( rs->sr_err ) {
+ case MDB_NOTFOUND:
+ break;
+ case 0:
+ /* Allow rename to same DN */
+ if ( nid == e->e_id )
+ break;
+ rs->sr_err = LDAP_ALREADY_EXISTS;
+ goto return_results;
+ default:
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ assert( op->orr_modlist != NULL );
+
+ if( op->o_preread ) {
+ if( preread_ctrl == NULL ) {
+ preread_ctrl = &ctrls[num_ctrls++];
+ ctrls[num_ctrls] = NULL;
+ }
+ if( slap_read_controls( op, rs, e,
+ &slap_pre_read_bv, preread_ctrl ) )
+ {
+ Debug( LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(mdb_modrdn)
+ ": pre-read failed!\n", 0, 0, 0 );
+ if ( op->o_preread & SLAP_CONTROL_CRITICAL ) {
+ /* FIXME: is it correct to abort
+ * operation if control fails? */
+ goto return_results;
+ }
+ }
+ }
+
+ /* delete old DN */
+ rs->sr_err = mdb_dn2id_delete( op, txn, p->e_id, e );
+ if ( rs->sr_err != 0 ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(mdb_modrdn)
+ ": dn2id del failed: %s (%d)\n",
+ mdb_strerror(rs->sr_err), rs->sr_err, 0 );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "DN index delete fail";
+ goto return_results;
+ }
+
+ /* copy the entry, then override some fields */
+ dummy = *e;
+ dummy.e_name = new_dn;
+ dummy.e_nname = new_ndn;
+ dummy.e_attrs = NULL;
+
+ /* add new DN */
+ rs->sr_err = mdb_dn2id_add( op, txn, np ? np->e_id : p->e_id, &dummy );
+ if ( rs->sr_err != 0 ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(mdb_modrdn)
+ ": dn2id add failed: %s (%d)\n",
+ mdb_strerror(rs->sr_err), rs->sr_err, 0 );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "DN index add failed";
+ goto return_results;
+ }
+
+ dummy.e_attrs = e->e_attrs;
+
+ /* modify entry */
+ rs->sr_err = mdb_modify_internal( op, txn, op->orr_modlist, &dummy,
+ &rs->sr_text, textbuf, textlen );
+ if( rs->sr_err != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(mdb_modrdn)
+ ": modify failed: %s (%d)\n",
+ mdb_strerror(rs->sr_err), rs->sr_err, 0 );
+ if ( dummy.e_attrs == e->e_attrs ) dummy.e_attrs = NULL;
+ goto return_results;
+ }
+
+ /* id2entry index */
+ rs->sr_err = mdb_id2entry_update( op, txn, &dummy );
+ if ( rs->sr_err != 0 ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(mdb_modrdn)
+ ": id2entry failed: %s (%d)\n",
+ mdb_strerror(rs->sr_err), rs->sr_err, 0 );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "entry update failed";
+ goto return_results;
+ }
+
+ if ( p_ndn.bv_len != 0 ) {
+ parent_is_glue = is_entry_glue(p);
+ rs->sr_err = mdb_dn2id_children( op, txn, p );
+ if ( rs->sr_err != MDB_NOTFOUND ) {
+ switch( rs->sr_err ) {
+ case 0:
+ break;
+ default:
+ Debug(LDAP_DEBUG_ARGS,
+ "<=- " LDAP_XSTRING(mdb_modrdn)
+ ": has_children failed: %s (%d)\n",
+ mdb_strerror(rs->sr_err), rs->sr_err, 0 );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+ parent_is_leaf = 1;
+ }
+ mdb_entry_return( p );
+ p = NULL;
+ }
+
+ if( op->o_postread ) {
+ if( postread_ctrl == NULL ) {
+ postread_ctrl = &ctrls[num_ctrls++];
+ ctrls[num_ctrls] = NULL;
+ }
+ if( slap_read_controls( op, rs, &dummy,
+ &slap_post_read_bv, postread_ctrl ) )
+ {
+ Debug( LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(mdb_modrdn)
+ ": post-read failed!\n", 0, 0, 0 );
+ if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
+ /* FIXME: is it correct to abort
+ * operation if control fails? */
+ goto return_results;
+ }
+ }
+ }
+
+ if( moi == &opinfo ) {
+ LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.moi_oe, OpExtra, oe_next );
+ opinfo.moi_oe.oe_key = NULL;
+ if( op->o_noop ) {
+ mdb_txn_abort( txn );
+ rs->sr_err = LDAP_X_NO_OPERATION;
+ txn = NULL;
+ /* Only free attrs if they were dup'd. */
+ if ( dummy.e_attrs == e->e_attrs ) dummy.e_attrs = NULL;
+ goto return_results;
+
+ } else {
+ if(( rs->sr_err=mdb_txn_commit( txn )) != 0 ) {
+ rs->sr_text = "txn_commit failed";
+ } else {
+ rs->sr_err = LDAP_SUCCESS;
+ }
+ txn = NULL;
+ }
+ }
+
+ if( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_modrdn) ": %s : %s (%d)\n",
+ rs->sr_text, mdb_strerror(rs->sr_err), rs->sr_err );
+ rs->sr_err = LDAP_OTHER;
+
+ goto return_results;
+ }
+
+ Debug(LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_modrdn)
+ ": rdn modified%s id=%08lx dn=\"%s\"\n",
+ op->o_noop ? " (no-op)" : "",
+ dummy.e_id, op->o_req_dn.bv_val );
+ rs->sr_text = NULL;
+ if( num_ctrls ) rs->sr_ctrls = ctrls;
+
+return_results:
+ if ( dummy.e_attrs ) {
+ attrs_free( dummy.e_attrs );
+ }
+ send_ldap_result( op, rs );
+
+#if 0
+ if( rs->sr_err == LDAP_SUCCESS && mdb->bi_txn_cp_kbyte ) {
+ TXN_CHECKPOINT( mdb->bi_dbenv,
+ mdb->bi_txn_cp_kbyte, mdb->bi_txn_cp_min, 0 );
+ }
+#endif
+
+ if ( rs->sr_err == LDAP_SUCCESS && parent_is_glue && parent_is_leaf ) {
+ op->o_delete_glue_parent = 1;
+ }
+
+done:
+ slap_graduate_commit_csn( op );
+
+ if( new_ndn.bv_val != NULL ) op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
+ if( new_dn.bv_val != NULL ) op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
+
+ /* LDAP v3 Support */
+ if( np != NULL ) {
+ /* free new parent */
+ mdb_entry_return( np );
+ }
+
+ if( p != NULL ) {
+ /* free parent */
+ mdb_entry_return( p );
+ }
+
+ /* free entry */
+ if( e != NULL ) {
+ mdb_entry_return( e );
+ }
+
+ if( moi == &opinfo ) {
+ if( txn != NULL ) {
+ mdb_txn_abort( txn );
+ }
+ if ( opinfo.moi_oe.oe_key ) {
+ LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.moi_oe, OpExtra, oe_next );
+ }
+ }
+
+ if( preread_ctrl != NULL && (*preread_ctrl) != NULL ) {
+ slap_sl_free( (*preread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
+ slap_sl_free( *preread_ctrl, op->o_tmpmemctx );
+ }
+ if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) {
+ slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
+ slap_sl_free( *postread_ctrl, op->o_tmpmemctx );
+ }
+ return rs->sr_err;
+}
--- /dev/null
+/* monitor.c - monitor mdb backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2011 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+#include <ac/stdlib.h>
+#include <ac/errno.h>
+#include <sys/stat.h>
+#include "lutil.h"
+#include "back-mdb.h"
+
+#include "../back-monitor/back-monitor.h"
+
+#include "config.h"
+
+static ObjectClass *oc_olmMDBDatabase;
+
+static AttributeDescription *ad_olmDbDirectory;
+
+#ifdef MDB_MONITOR_IDX
+static int
+mdb_monitor_idx_entry_add(
+ struct mdb_info *mdb,
+ Entry *e );
+
+static AttributeDescription *ad_olmMDBNotIndexed;
+#endif /* MDB_MONITOR_IDX */
+
+/*
+ * NOTE: there's some confusion in monitor OID arc;
+ * by now, let's consider:
+ *
+ * Subsystems monitor attributes 1.3.6.1.4.1.4203.666.1.55.0
+ * Databases monitor attributes 1.3.6.1.4.1.4203.666.1.55.0.1
+ * MDB database monitor attributes 1.3.6.1.4.1.4203.666.1.55.0.1.1
+ *
+ * Subsystems monitor objectclasses 1.3.6.1.4.1.4203.666.3.16.0
+ * Databases monitor objectclasses 1.3.6.1.4.1.4203.666.3.16.0.1
+ * MDB database monitor objectclasses 1.3.6.1.4.1.4203.666.3.16.0.1.1
+ */
+
+static struct {
+ char *name;
+ char *oid;
+} s_oid[] = {
+ { "olmMDBAttributes", "olmDatabaseAttributes:1" },
+ { "olmMDBObjectClasses", "olmDatabaseObjectClasses:1" },
+
+ { NULL }
+};
+
+static struct {
+ char *desc;
+ AttributeDescription **ad;
+} s_at[] = {
+ { "( olmMDBAttributes:4 "
+ "NAME ( 'olmDbDirectory' ) "
+ "DESC 'Path name of the directory "
+ "where the database environment resides' "
+ "SUP monitoredInfo "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ &ad_olmDbDirectory },
+
+#ifdef MDB_MONITOR_IDX
+ { "( olmMDBAttributes:5 "
+ "NAME ( 'olmMDBNotIndexed' ) "
+ "DESC 'Missing indexes resulting from candidate selection' "
+ "SUP monitoredInfo "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ &ad_olmMDBNotIndexed },
+#endif /* MDB_MONITOR_IDX */
+
+ { NULL }
+};
+
+static struct {
+ char *desc;
+ ObjectClass **oc;
+} s_oc[] = {
+ /* augments an existing object, so it must be AUXILIARY
+ * FIXME: derive from some ABSTRACT "monitoredEntity"? */
+ { "( olmMDBObjectClasses:2 "
+ "NAME ( 'olmMDBDatabase' ) "
+ "SUP top AUXILIARY "
+ "MAY ( "
+ "olmDbDirectory "
+#ifdef MDB_MONITOR_IDX
+ "$ olmMDBNotIndexed "
+#endif /* MDB_MONITOR_IDX */
+ ") )",
+ &oc_olmMDBDatabase },
+
+ { NULL }
+};
+
+static int
+mdb_monitor_update(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e,
+ void *priv )
+{
+ struct mdb_info *mdb = (struct mdb_info *) priv;
+
+#ifdef MDB_MONITOR_IDX
+ mdb_monitor_idx_entry_add( mdb, e );
+#endif /* MDB_MONITOR_IDX */
+
+ return SLAP_CB_CONTINUE;
+}
+
+#if 0 /* uncomment if required */
+static int
+mdb_monitor_modify(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e,
+ void *priv )
+{
+ return SLAP_CB_CONTINUE;
+}
+#endif
+
+static int
+mdb_monitor_free(
+ Entry *e,
+ void **priv )
+{
+ struct berval values[ 2 ];
+ Modification mod = { 0 };
+
+ const char *text;
+ char textbuf[ SLAP_TEXT_BUFLEN ];
+
+ int i, rc;
+
+ /* NOTE: if slap_shutdown != 0, priv might have already been freed */
+ *priv = NULL;
+
+ /* Remove objectClass */
+ mod.sm_op = LDAP_MOD_DELETE;
+ mod.sm_desc = slap_schema.si_ad_objectClass;
+ mod.sm_values = values;
+ mod.sm_numvals = 1;
+ values[ 0 ] = oc_olmMDBDatabase->soc_cname;
+ BER_BVZERO( &values[ 1 ] );
+
+ rc = modify_delete_values( e, &mod, 1, &text,
+ textbuf, sizeof( textbuf ) );
+ /* don't care too much about return code... */
+
+ /* remove attrs */
+ mod.sm_values = NULL;
+ mod.sm_numvals = 0;
+ for ( i = 0; s_at[ i ].desc != NULL; i++ ) {
+ mod.sm_desc = *s_at[ i ].ad;
+ rc = modify_delete_values( e, &mod, 1, &text,
+ textbuf, sizeof( textbuf ) );
+ /* don't care too much about return code... */
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+/*
+ * call from within mdb_initialize()
+ */
+static int
+mdb_monitor_initialize( void )
+{
+ int i, code;
+ ConfigArgs c;
+ char *argv[ 3 ];
+
+ static int mdb_monitor_initialized = 0;
+
+ /* set to 0 when successfully initialized; otherwise, remember failure */
+ static int mdb_monitor_initialized_failure = 1;
+
+ if ( mdb_monitor_initialized++ ) {
+ return mdb_monitor_initialized_failure;
+ }
+
+ if ( backend_info( "monitor" ) == NULL ) {
+ return -1;
+ }
+
+ /* register schema here */
+
+ argv[ 0 ] = "back-mdb monitor";
+ c.argv = argv;
+ c.argc = 3;
+ c.fname = argv[0];
+
+ for ( i = 0; s_oid[ i ].name; i++ ) {
+ c.lineno = i;
+ argv[ 1 ] = s_oid[ i ].name;
+ argv[ 2 ] = s_oid[ i ].oid;
+
+ if ( parse_oidm( &c, 0, NULL ) != 0 ) {
+ Debug( LDAP_DEBUG_ANY, LDAP_XSTRING(mdb_monitor_initialize)
+ ": unable to add "
+ "objectIdentifier \"%s=%s\"\n",
+ s_oid[ i ].name, s_oid[ i ].oid, 0 );
+ return 2;
+ }
+ }
+
+ for ( i = 0; s_at[ i ].desc != NULL; i++ ) {
+ code = register_at( s_at[ i ].desc, s_at[ i ].ad, 1 );
+ if ( code != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, LDAP_XSTRING(mdb_monitor_initialize)
+ ": register_at failed for attributeType (%s)\n",
+ s_at[ i ].desc, 0, 0 );
+ return 3;
+
+ } else {
+ (*s_at[ i ].ad)->ad_type->sat_flags |= SLAP_AT_HIDE;
+ }
+ }
+
+ for ( i = 0; s_oc[ i ].desc != NULL; i++ ) {
+ code = register_oc( s_oc[ i ].desc, s_oc[ i ].oc, 1 );
+ if ( code != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, LDAP_XSTRING(mdb_monitor_initialize)
+ ": register_oc failed for objectClass (%s)\n",
+ s_oc[ i ].desc, 0, 0 );
+ return 4;
+
+ } else {
+ (*s_oc[ i ].oc)->soc_flags |= SLAP_OC_HIDE;
+ }
+ }
+
+ return ( mdb_monitor_initialized_failure = LDAP_SUCCESS );
+}
+
+/*
+ * call from within mdb_db_init()
+ */
+int
+mdb_monitor_db_init( BackendDB *be )
+{
+ struct mdb_info *mdb = (struct mdb_info *) be->be_private;
+
+ if ( mdb_monitor_initialize() == LDAP_SUCCESS ) {
+ /* monitoring in back-mdb is on by default */
+ SLAP_DBFLAGS( be ) |= SLAP_DBFLAG_MONITORING;
+ }
+
+#ifdef MDB_MONITOR_IDX
+ mdb->mi_idx = NULL;
+ ldap_pvt_thread_mutex_init( &mdb->mi_idx_mutex );
+#endif /* MDB_MONITOR_IDX */
+
+ return 0;
+}
+
+/*
+ * call from within mdb_db_open()
+ */
+int
+mdb_monitor_db_open( BackendDB *be )
+{
+ struct mdb_info *mdb = (struct mdb_info *) be->be_private;
+ Attribute *a, *next;
+ monitor_callback_t *cb = NULL;
+ int rc = 0;
+ BackendInfo *mi;
+ monitor_extra_t *mbe;
+ struct berval dummy = BER_BVC("");
+
+ if ( !SLAP_DBMONITORING( be ) ) {
+ return 0;
+ }
+
+ mi = backend_info( "monitor" );
+ if ( !mi || !mi->bi_extra ) {
+ SLAP_DBFLAGS( be ) ^= SLAP_DBFLAG_MONITORING;
+ return 0;
+ }
+ mbe = mi->bi_extra;
+
+ /* don't bother if monitor is not configured */
+ if ( !mbe->is_configured() ) {
+ static int warning = 0;
+
+ if ( warning++ == 0 ) {
+ Debug( LDAP_DEBUG_ANY, LDAP_XSTRING(mdb_monitor_db_open)
+ ": monitoring disabled; "
+ "configure monitor database to enable\n",
+ 0, 0, 0 );
+ }
+
+ return 0;
+ }
+
+ /* alloc as many as required (plus 1 for objectClass) */
+ a = attrs_alloc( 1 + 1 );
+ if ( a == NULL ) {
+ rc = 1;
+ goto cleanup;
+ }
+
+ a->a_desc = slap_schema.si_ad_objectClass;
+ attr_valadd( a, &oc_olmMDBDatabase->soc_cname, NULL, 1 );
+ next = a->a_next;
+
+ {
+ struct berval bv, nbv;
+ ber_len_t pathlen = 0, len = 0;
+ char path[ MAXPATHLEN ] = { '\0' };
+ char *fname = mdb->mi_dbenv_home,
+ *ptr;
+
+ len = strlen( fname );
+ if ( fname[ 0 ] != '/' ) {
+ /* get full path name */
+ getcwd( path, sizeof( path ) );
+ pathlen = strlen( path );
+
+ if ( fname[ 0 ] == '.' && fname[ 1 ] == '/' ) {
+ fname += 2;
+ len -= 2;
+ }
+ }
+
+ bv.bv_len = pathlen + STRLENOF( "/" ) + len;
+ ptr = bv.bv_val = ch_malloc( bv.bv_len + STRLENOF( "/" ) + 1 );
+ if ( pathlen ) {
+ ptr = lutil_strncopy( ptr, path, pathlen );
+ ptr[ 0 ] = '/';
+ ptr++;
+ }
+ ptr = lutil_strncopy( ptr, fname, len );
+ if ( ptr[ -1 ] != '/' ) {
+ ptr[ 0 ] = '/';
+ ptr++;
+ }
+ ptr[ 0 ] = '\0';
+
+ attr_normalize_one( ad_olmDbDirectory, &bv, &nbv, NULL );
+
+ next->a_desc = ad_olmDbDirectory;
+ next->a_vals = ch_calloc( sizeof( struct berval ), 2 );
+ next->a_vals[ 0 ] = bv;
+ next->a_numvals = 1;
+
+ if ( BER_BVISNULL( &nbv ) ) {
+ next->a_nvals = next->a_vals;
+
+ } else {
+ next->a_nvals = ch_calloc( sizeof( struct berval ), 2 );
+ next->a_nvals[ 0 ] = nbv;
+ }
+
+ next = next->a_next;
+ }
+
+ cb = ch_calloc( sizeof( monitor_callback_t ), 1 );
+ cb->mc_update = mdb_monitor_update;
+#if 0 /* uncomment if required */
+ cb->mc_modify = mdb_monitor_modify;
+#endif
+ cb->mc_free = mdb_monitor_free;
+ cb->mc_private = (void *)mdb;
+
+ /* make sure the database is registered; then add monitor attributes */
+ rc = mbe->register_database( be, &mdb->mi_monitor.mdm_ndn );
+ if ( rc == 0 ) {
+ rc = mbe->register_entry_attrs( &mdb->mi_monitor.mdm_ndn, a, cb,
+ &dummy, 0, &dummy );
+ }
+
+cleanup:;
+ if ( rc != 0 ) {
+ if ( cb != NULL ) {
+ ch_free( cb );
+ cb = NULL;
+ }
+
+ if ( a != NULL ) {
+ attrs_free( a );
+ a = NULL;
+ }
+ }
+
+ /* store for cleanup */
+ mdb->mi_monitor.mdm_cb = (void *)cb;
+
+ /* we don't need to keep track of the attributes, because
+ * mdb_monitor_free() takes care of everything */
+ if ( a != NULL ) {
+ attrs_free( a );
+ }
+
+ return rc;
+}
+
+/*
+ * call from within mdb_db_close()
+ */
+int
+mdb_monitor_db_close( BackendDB *be )
+{
+ struct mdb_info *mdb = (struct mdb_info *) be->be_private;
+
+ if ( !BER_BVISNULL( &mdb->mi_monitor.mdm_ndn ) ) {
+ BackendInfo *mi = backend_info( "monitor" );
+ monitor_extra_t *mbe;
+
+ if ( mi && &mi->bi_extra ) {
+ mbe = mi->bi_extra;
+ mbe->unregister_entry_callback( &mdb->mi_monitor.mdm_ndn,
+ (monitor_callback_t *)mdb->mi_monitor.mdm_cb,
+ NULL, 0, NULL );
+ }
+
+ memset( &mdb->mi_monitor, 0, sizeof( mdb->mi_monitor ) );
+ }
+
+ return 0;
+}
+
+/*
+ * call from within mdb_db_destroy()
+ */
+int
+mdb_monitor_db_destroy( BackendDB *be )
+{
+#ifdef MDB_MONITOR_IDX
+ struct mdb_info *mdb = (struct mdb_info *) be->be_private;
+
+ /* TODO: free tree */
+ ldap_pvt_thread_mutex_destroy( &mdb->mi_idx_mutex );
+ avl_free( mdb->mi_idx, ch_free );
+#endif /* MDB_MONITOR_IDX */
+
+ return 0;
+}
+
+#ifdef MDB_MONITOR_IDX
+
+#define MDB_MONITOR_IDX_TYPES (4)
+
+typedef struct monitor_idx_t monitor_idx_t;
+
+struct monitor_idx_t {
+ AttributeDescription *idx_ad;
+ unsigned long idx_count[MDB_MONITOR_IDX_TYPES];
+};
+
+static int
+mdb_monitor_bitmask2key( slap_mask_t bitmask )
+{
+ int key;
+
+ for ( key = 0; key < 8 * (int)sizeof(slap_mask_t) && !( bitmask & 0x1U );
+ key++ )
+ bitmask >>= 1;
+
+ return key;
+}
+
+static struct berval idxbv[] = {
+ BER_BVC( "present=" ),
+ BER_BVC( "equality=" ),
+ BER_BVC( "approx=" ),
+ BER_BVC( "substr=" ),
+ BER_BVNULL
+};
+
+static ber_len_t
+mdb_monitor_idx2len( monitor_idx_t *idx )
+{
+ int i;
+ ber_len_t len = 0;
+
+ for ( i = 0; i < MDB_MONITOR_IDX_TYPES; i++ ) {
+ if ( idx->idx_count[ i ] != 0 ) {
+ len += idxbv[i].bv_len;
+ }
+ }
+
+ return len;
+}
+
+static int
+monitor_idx_cmp( const void *p1, const void *p2 )
+{
+ const monitor_idx_t *idx1 = (const monitor_idx_t *)p1;
+ const monitor_idx_t *idx2 = (const monitor_idx_t *)p2;
+
+ return SLAP_PTRCMP( idx1->idx_ad, idx2->idx_ad );
+}
+
+static int
+monitor_idx_dup( void *p1, void *p2 )
+{
+ monitor_idx_t *idx1 = (monitor_idx_t *)p1;
+ monitor_idx_t *idx2 = (monitor_idx_t *)p2;
+
+ return SLAP_PTRCMP( idx1->idx_ad, idx2->idx_ad ) == 0 ? -1 : 0;
+}
+
+int
+mdb_monitor_idx_add(
+ struct mdb_info *mdb,
+ AttributeDescription *desc,
+ slap_mask_t type )
+{
+ monitor_idx_t idx_dummy = { 0 },
+ *idx;
+ int rc = 0, key;
+
+ idx_dummy.idx_ad = desc;
+ key = mdb_monitor_bitmask2key( type ) - 1;
+ if ( key >= MDB_MONITOR_IDX_TYPES ) {
+ /* invalid index type */
+ return -1;
+ }
+
+ ldap_pvt_thread_mutex_lock( &mdb->mi_idx_mutex );
+
+ idx = (monitor_idx_t *)avl_find( mdb->mi_idx,
+ (caddr_t)&idx_dummy, monitor_idx_cmp );
+ if ( idx == NULL ) {
+ idx = (monitor_idx_t *)ch_calloc( sizeof( monitor_idx_t ), 1 );
+ idx->idx_ad = desc;
+ idx->idx_count[ key ] = 1;
+
+ switch ( avl_insert( &mdb->mi_idx, (caddr_t)idx,
+ monitor_idx_cmp, monitor_idx_dup ) )
+ {
+ case 0:
+ break;
+
+ default:
+ ch_free( idx );
+ rc = -1;
+ }
+
+ } else {
+ idx->idx_count[ key ]++;
+ }
+
+ ldap_pvt_thread_mutex_unlock( &mdb->mi_idx_mutex );
+
+ return rc;
+}
+
+static int
+mdb_monitor_idx_apply( void *v_idx, void *v_valp )
+{
+ monitor_idx_t *idx = (monitor_idx_t *)v_idx;
+ BerVarray *valp = (BerVarray *)v_valp;
+
+ struct berval bv;
+ char *ptr;
+ char count_buf[ MDB_MONITOR_IDX_TYPES ][ SLAP_TEXT_BUFLEN ];
+ ber_len_t count_len[ MDB_MONITOR_IDX_TYPES ],
+ idx_len;
+ int i, num = 0;
+
+ idx_len = mdb_monitor_idx2len( idx );
+
+ bv.bv_len = 0;
+ for ( i = 0; i < MDB_MONITOR_IDX_TYPES; i++ ) {
+ if ( idx->idx_count[ i ] == 0 ) {
+ continue;
+ }
+
+ count_len[ i ] = snprintf( count_buf[ i ],
+ sizeof( count_buf[ i ] ), "%lu", idx->idx_count[ i ] );
+ bv.bv_len += count_len[ i ];
+ num++;
+ }
+
+ bv.bv_len += idx->idx_ad->ad_cname.bv_len
+ + num
+ + idx_len;
+ ptr = bv.bv_val = ch_malloc( bv.bv_len + 1 );
+ ptr = lutil_strcopy( ptr, idx->idx_ad->ad_cname.bv_val );
+ for ( i = 0; i < MDB_MONITOR_IDX_TYPES; i++ ) {
+ if ( idx->idx_count[ i ] == 0 ) {
+ continue;
+ }
+
+ ptr[ 0 ] = '#';
+ ++ptr;
+ ptr = lutil_strcopy( ptr, idxbv[ i ].bv_val );
+ ptr = lutil_strcopy( ptr, count_buf[ i ] );
+ }
+
+ ber_bvarray_add( valp, &bv );
+
+ return 0;
+}
+
+static int
+mdb_monitor_idx_entry_add(
+ struct mdb_info *mdb,
+ Entry *e )
+{
+ BerVarray vals = NULL;
+ Attribute *a;
+
+ a = attr_find( e->e_attrs, ad_olmMDBNotIndexed );
+
+ ldap_pvt_thread_mutex_lock( &mdb->mi_idx_mutex );
+
+ avl_apply( mdb->mi_idx, mdb_monitor_idx_apply,
+ &vals, -1, AVL_INORDER );
+
+ ldap_pvt_thread_mutex_unlock( &mdb->mi_idx_mutex );
+
+ if ( vals != NULL ) {
+ if ( a != NULL ) {
+ assert( a->a_nvals == a->a_vals );
+
+ ber_bvarray_free( a->a_vals );
+
+ } else {
+ Attribute **ap;
+
+ for ( ap = &e->e_attrs; *ap != NULL; ap = &(*ap)->a_next )
+ ;
+ *ap = attr_alloc( ad_olmMDBNotIndexed );
+ a = *ap;
+ }
+ a->a_vals = vals;
+ a->a_nvals = a->a_vals;
+ }
+
+ return 0;
+}
+
+#endif /* MDB_MONITOR_IDX */
--- /dev/null
+/* init.c - initialize mdb backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2011 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-mdb.h"
+
+int mdb_next_id( BackendDB *be, MDB_txn *tid, ID *out )
+{
+ struct mdb_info *mdb = (struct mdb_info *) be->be_private;
+ int rc;
+ ID id = 0;
+ MDB_val key;
+ MDB_cursor *cursor;
+
+ /* Get a read cursor */
+ rc = mdb_cursor_open( tid, mdb->mi_id2entry, &cursor );
+
+ if (rc == 0) {
+ rc = mdb_cursor_get(cursor, &key, NULL, MDB_LAST);
+ mdb_cursor_close(cursor);
+ }
+
+ switch(rc) {
+ case MDB_NOTFOUND:
+ rc = 0;
+ *out = 1;
+ break;
+ case 0:
+ memcpy( &id, key.mv_data, sizeof( id ));
+ *out = ++id;
+ break;
+
+ default:
+ Debug( LDAP_DEBUG_ANY,
+ "=> mdb_next_id: get failed: %s (%d)\n",
+ mdb_strerror(rc), rc, 0 );
+ goto done;
+ }
+
+done:
+ return rc;
+}
--- /dev/null
+/* operational.c - mdb backend operational attributes function */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2011 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "back-mdb.h"
+
+/*
+ * sets *hasSubordinates to LDAP_COMPARE_TRUE/LDAP_COMPARE_FALSE
+ * if the entry has children or not.
+ */
+int
+mdb_hasSubordinates(
+ Operation *op,
+ Entry *e,
+ int *hasSubordinates )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ MDB_txn *rtxn;
+ mdb_op_info opinfo = {0}, *moi = &opinfo;
+ int rc;
+
+ assert( e != NULL );
+
+ rc = mdb_opinfo_get(op, mdb, 1, &moi);
+ switch(rc) {
+ case 0:
+ break;
+ default:
+ rc = LDAP_OTHER;
+ goto done;
+ }
+
+ rtxn = moi->moi_txn;
+
+ rc = mdb_dn2id_children( op, rtxn, e );
+
+ switch( rc ) {
+ case 0:
+ *hasSubordinates = LDAP_COMPARE_TRUE;
+ break;
+
+ case MDB_NOTFOUND:
+ *hasSubordinates = LDAP_COMPARE_FALSE;
+ rc = LDAP_SUCCESS;
+ break;
+
+ default:
+ Debug(LDAP_DEBUG_ARGS,
+ "<=- " LDAP_XSTRING(mdb_hasSubordinates)
+ ": has_children failed: %s (%d)\n",
+ mdb_strerror(rc), rc, 0 );
+ rc = LDAP_OTHER;
+ }
+
+done:;
+ if ( moi == &opinfo ) {
+ mdb_txn_reset( moi->moi_txn );
+ LDAP_SLIST_REMOVE( &op->o_extra, &moi->moi_oe, OpExtra, oe_next );
+ }
+ return rc;
+}
+
+/*
+ * sets the supported operational attributes (if required)
+ */
+int
+mdb_operational(
+ Operation *op,
+ SlapReply *rs )
+{
+ Attribute **ap;
+
+ assert( rs->sr_entry != NULL );
+
+ for ( ap = &rs->sr_operational_attrs; *ap; ap = &(*ap)->a_next ) {
+ if ( (*ap)->a_desc == slap_schema.si_ad_hasSubordinates ) {
+ break;
+ }
+ }
+
+ if ( *ap == NULL &&
+ attr_find( rs->sr_entry->e_attrs, slap_schema.si_ad_hasSubordinates ) == NULL &&
+ ( SLAP_OPATTRS( rs->sr_attr_flags ) ||
+ ad_inlist( slap_schema.si_ad_hasSubordinates, rs->sr_attrs ) ) )
+ {
+ int hasSubordinates, rc;
+
+ rc = mdb_hasSubordinates( op, rs->sr_entry, &hasSubordinates );
+ if ( rc == LDAP_SUCCESS ) {
+ *ap = slap_operational_hasSubordinate( hasSubordinates == LDAP_COMPARE_TRUE );
+ assert( *ap != NULL );
+
+ ap = &(*ap)->a_next;
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
--- /dev/null
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2011 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef _PROTO_MDB_H
+#define _PROTO_MDB_H
+
+LDAP_BEGIN_DECL
+
+#define MDB_UCTYPE "MDB"
+
+/*
+ * attr.c
+ */
+
+AttrInfo *mdb_attr_mask( struct mdb_info *mdb,
+ AttributeDescription *desc );
+
+void mdb_attr_flush( struct mdb_info *mdb );
+
+int mdb_attr_slot( struct mdb_info *mdb,
+ AttributeDescription *desc, int *insert );
+
+int mdb_attr_dbs_open( BackendDB *be, MDB_txn *txn, struct config_reply_s *cr );
+void mdb_attr_dbs_close( struct mdb_info *mdb, MDB_txn *txn );
+
+int mdb_attr_index_config LDAP_P(( struct mdb_info *mdb,
+ const char *fname, int lineno,
+ int argc, char **argv, struct config_reply_s *cr ));
+
+void mdb_attr_index_unparse LDAP_P(( struct mdb_info *mdb, BerVarray *bva ));
+void mdb_attr_index_destroy LDAP_P(( struct mdb_info *mdb ));
+void mdb_attr_index_free LDAP_P(( struct mdb_info *mdb,
+ AttributeDescription *ad ));
+
+void mdb_attr_info_free( AttrInfo *ai );
+
+/*
+ * config.c
+ */
+
+int mdb_back_init_cf( BackendInfo *bi );
+
+/*
+ * dn2entry.c
+ */
+
+int mdb_dn2entry LDAP_P(( Operation *op, MDB_txn *tid,
+ struct berval *dn, Entry **e, int matched ));
+
+/*
+ * dn2id.c
+ */
+
+int mdb_dn2id(
+ Operation *op,
+ MDB_txn *txn,
+ struct berval *ndn,
+ ID *id,
+ struct berval *matched,
+ struct berval *nmatched );
+
+int mdb_dn2id_add(
+ Operation *op,
+ MDB_txn *tid,
+ ID pid,
+ Entry *e );
+
+int mdb_dn2id_delete(
+ Operation *op,
+ MDB_txn *tid,
+ ID pid,
+ Entry *e );
+
+int mdb_dn2id_children(
+ Operation *op,
+ MDB_txn *tid,
+ Entry *e );
+
+int mdb_dn2sups (
+ Operation *op,
+ MDB_txn *tid,
+ struct berval *dn,
+ ID *sups
+ );
+
+int mdb_dn2idl(
+ Operation *op,
+ MDB_txn *txn,
+ struct berval *ndn,
+ ID eid,
+ ID *ids,
+ ID *stack );
+
+int mdb_dn2id_parent(
+ Operation *op,
+ MDB_txn *txn,
+ ID eid,
+ ID *idp );
+
+int mdb_id2name(
+ Operation *op,
+ MDB_txn *txn,
+ MDB_cursor **cursp,
+ ID eid,
+ struct berval *name,
+ struct berval *nname);
+
+int mdb_idscope(
+ Operation *op,
+ MDB_txn *txn,
+ ID base,
+ ID *ids,
+ ID *res );
+
+int mdb_idscopes(
+ Operation *op,
+ MDB_txn *txn,
+ MDB_cursor **cursp,
+ ID base,
+ ID *scopes );
+
+MDB_cmp_func mdb_dup_compare;
+
+/*
+ * filterentry.c
+ */
+
+int mdb_filter_candidates(
+ Operation *op,
+ MDB_txn *txn,
+ Filter *f,
+ ID *ids,
+ ID *tmp,
+ ID *stack );
+
+/*
+ * id2entry.c
+ */
+
+int mdb_id2entry_add(
+ Operation *op,
+ MDB_txn *tid,
+ Entry *e );
+
+int mdb_id2entry_update(
+ Operation *op,
+ MDB_txn *tid,
+ Entry *e );
+
+int mdb_id2entry_delete(
+ BackendDB *be,
+ MDB_txn *tid,
+ Entry *e);
+
+int mdb_id2entry(
+ Operation *op,
+ MDB_txn *tid,
+ ID id,
+ Entry **e);
+
+void mdb_entry_free ( Entry *e );
+int mdb_entry_return( Entry *e );
+BI_entry_release_rw mdb_entry_release;
+BI_entry_get_rw mdb_entry_get;
+
+void mdb_reader_flush( MDB_env *env );
+int mdb_opinfo_get( Operation *op, struct mdb_info *mdb, int rdonly, mdb_op_info **moi );
+
+/*
+ * idl.c
+ */
+
+unsigned mdb_idl_search( ID *ids, ID id );
+
+int mdb_idl_fetch_key(
+ BackendDB *be,
+ MDB_txn *txn,
+ MDB_dbi dbi,
+ MDB_val *key,
+ ID *ids,
+ MDB_cursor **saved_cursor,
+ int get_flag );
+
+int mdb_idl_insert( ID *ids, ID id );
+
+int mdb_idl_insert_key(
+ BackendDB *be,
+ MDB_txn *txn,
+ MDB_dbi dbi,
+ MDB_val *key,
+ ID id );
+
+int mdb_idl_delete_key(
+ BackendDB *be,
+ MDB_txn *txn,
+ MDB_dbi dbi,
+ MDB_val *key,
+ ID id );
+
+int
+mdb_idl_intersection(
+ ID *a,
+ ID *b );
+
+int
+mdb_idl_union(
+ ID *a,
+ ID *b );
+
+ID mdb_idl_first( ID *ids, ID *cursor );
+ID mdb_idl_next( ID *ids, ID *cursor );
+
+void mdb_idl_sort( ID *ids, ID *tmp );
+int mdb_idl_append( ID *a, ID *b );
+int mdb_idl_append_one( ID *ids, ID id );
+
+
+/*
+ * index.c
+ */
+
+extern AttrInfo *
+mdb_index_mask LDAP_P((
+ Backend *be,
+ AttributeDescription *desc,
+ struct berval *name ));
+
+extern int
+mdb_index_param LDAP_P((
+ Backend *be,
+ AttributeDescription *desc,
+ int ftype,
+ MDB_dbi *dbi,
+ slap_mask_t *mask,
+ struct berval *prefix ));
+
+extern int
+mdb_index_values LDAP_P((
+ Operation *op,
+ MDB_txn *txn,
+ AttributeDescription *desc,
+ BerVarray vals,
+ ID id,
+ int opid ));
+
+extern int
+mdb_index_recset LDAP_P((
+ struct mdb_info *mdb,
+ Attribute *a,
+ AttributeType *type,
+ struct berval *tags,
+ IndexRec *ir ));
+
+extern int
+mdb_index_recrun LDAP_P((
+ Operation *op,
+ struct mdb_info *mdb,
+ IndexRec *ir,
+ ID id,
+ int base ));
+
+int mdb_index_entry LDAP_P(( Operation *op, MDB_txn *t, int r, Entry *e ));
+
+#define mdb_index_entry_add(op,t,e) \
+ mdb_index_entry((op),(t),SLAP_INDEX_ADD_OP,(e))
+#define mdb_index_entry_del(op,t,e) \
+ mdb_index_entry((op),(t),SLAP_INDEX_DELETE_OP,(e))
+
+/*
+ * key.c
+ */
+
+extern int
+mdb_key_read(
+ Backend *be,
+ MDB_txn *txn,
+ MDB_dbi dbi,
+ struct berval *k,
+ ID *ids,
+ MDB_cursor **saved_cursor,
+ int get_flags );
+
+extern int
+mdb_key_change(
+ Backend *be,
+ MDB_txn *txn,
+ MDB_dbi dbi,
+ struct berval *k,
+ ID id,
+ int op );
+
+/*
+ * nextid.c
+ */
+
+int mdb_next_id( BackendDB *be, MDB_txn *tid, ID *id );
+
+/*
+ * modify.c
+ */
+
+int mdb_modify_internal(
+ Operation *op,
+ MDB_txn *tid,
+ Modifications *modlist,
+ Entry *e,
+ const char **text,
+ char *textbuf,
+ size_t textlen );
+
+/*
+ * monitor.c
+ */
+
+int mdb_monitor_db_init( BackendDB *be );
+int mdb_monitor_db_open( BackendDB *be );
+int mdb_monitor_db_close( BackendDB *be );
+int mdb_monitor_db_destroy( BackendDB *be );
+
+#ifdef MDB_MONITOR_IDX
+int
+mdb_monitor_idx_add(
+ struct mdb_info *mdb,
+ AttributeDescription *desc,
+ slap_mask_t type );
+#endif /* MDB_MONITOR_IDX */
+
+/*
+ * former external.h
+ */
+
+extern BI_init mdb_back_initialize;
+
+extern BI_db_config mdb_db_config;
+
+extern BI_op_add mdb_add;
+extern BI_op_bind mdb_bind;
+extern BI_op_compare mdb_compare;
+extern BI_op_delete mdb_delete;
+extern BI_op_modify mdb_modify;
+extern BI_op_modrdn mdb_modrdn;
+extern BI_op_search mdb_search;
+extern BI_op_extended mdb_extended;
+
+extern BI_chk_referrals mdb_referrals;
+
+extern BI_operational mdb_operational;
+
+extern BI_has_subordinates mdb_hasSubordinates;
+
+/* tools.c */
+extern BI_tool_entry_open mdb_tool_entry_open;
+extern BI_tool_entry_close mdb_tool_entry_close;
+extern BI_tool_entry_first_x mdb_tool_entry_first_x;
+extern BI_tool_entry_next mdb_tool_entry_next;
+extern BI_tool_entry_get mdb_tool_entry_get;
+extern BI_tool_entry_put mdb_tool_entry_put;
+extern BI_tool_entry_reindex mdb_tool_entry_reindex;
+extern BI_tool_dn2id_get mdb_tool_dn2id_get;
+extern BI_tool_entry_modify mdb_tool_entry_modify;
+
+LDAP_END_DECL
+
+#endif /* _PROTO_MDB_H */
--- /dev/null
+/* referral.c - MDB backend referral handler */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2011 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-mdb.h"
+
+int
+mdb_referrals( Operation *op, SlapReply *rs )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ Entry *e = NULL;
+ int rc = LDAP_SUCCESS;
+
+ MDB_txn *rtxn;
+ mdb_op_info opinfo = {0}, *moi = &opinfo;
+
+ if( op->o_tag == LDAP_REQ_SEARCH ) {
+ /* let search take care of itself */
+ return rc;
+ }
+
+ if( get_manageDSAit( op ) ) {
+ /* let op take care of DSA management */
+ return rc;
+ }
+
+ rc = mdb_opinfo_get(op, mdb, 1, &moi);
+ switch(rc) {
+ case 0:
+ break;
+ default:
+ return LDAP_OTHER;
+ }
+
+ rtxn = moi->moi_txn;
+
+ /* get entry */
+ rc = mdb_dn2entry( op, rtxn, &op->o_req_ndn, &e, 1 );
+
+ switch(rc) {
+ case MDB_NOTFOUND:
+ case 0:
+ break;
+ case LDAP_BUSY:
+ rs->sr_text = "ldap server busy";
+ goto done;
+ default:
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_referrals)
+ ": dn2entry failed: %s (%d)\n",
+ mdb_strerror(rc), rc, 0 );
+ rs->sr_text = "internal error";
+ rc = LDAP_OTHER;
+ goto done;
+ }
+
+ if ( rc == MDB_NOTFOUND ) {
+ rc = LDAP_SUCCESS;
+ rs->sr_matched = NULL;
+ if ( e != NULL ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_referrals)
+ ": tag=%lu target=\"%s\" matched=\"%s\"\n",
+ (unsigned long)op->o_tag, op->o_req_dn.bv_val, e->e_name.bv_val );
+
+ if( is_entry_referral( e ) ) {
+ BerVarray ref = get_entry_referrals( op, e );
+ rc = LDAP_OTHER;
+ rs->sr_ref = referral_rewrite( ref, &e->e_name,
+ &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+ ber_bvarray_free( ref );
+ if ( rs->sr_ref ) {
+ rs->sr_matched = ber_strdup_x(
+ e->e_name.bv_val, op->o_tmpmemctx );
+ }
+ }
+
+ mdb_entry_return( e );
+ e = NULL;
+ }
+
+ if( rs->sr_ref != NULL ) {
+ /* send referrals */
+ rc = rs->sr_err = LDAP_REFERRAL;
+ send_ldap_result( op, rs );
+ ber_bvarray_free( rs->sr_ref );
+ rs->sr_ref = NULL;
+ } else if ( rc != LDAP_SUCCESS ) {
+ rs->sr_text = rs->sr_matched ? "bad referral object" : NULL;
+ }
+
+ if (rs->sr_matched) {
+ op->o_tmpfree( (char *)rs->sr_matched, op->o_tmpmemctx );
+ rs->sr_matched = NULL;
+ }
+ goto done;
+ }
+
+ if ( is_entry_referral( e ) ) {
+ /* entry is a referral */
+ BerVarray refs = get_entry_referrals( op, e );
+ rs->sr_ref = referral_rewrite(
+ refs, &e->e_name, &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_referrals)
+ ": tag=%lu target=\"%s\" matched=\"%s\"\n",
+ (unsigned long)op->o_tag, op->o_req_dn.bv_val, e->e_name.bv_val );
+
+ rs->sr_matched = e->e_name.bv_val;
+ if( rs->sr_ref != NULL ) {
+ rc = rs->sr_err = LDAP_REFERRAL;
+ send_ldap_result( op, rs );
+ ber_bvarray_free( rs->sr_ref );
+ rs->sr_ref = NULL;
+ } else {
+ rc = LDAP_OTHER;
+ rs->sr_text = "bad referral object";
+ }
+
+ rs->sr_matched = NULL;
+ ber_bvarray_free( refs );
+ }
+
+done:
+ if ( moi == &opinfo ) {
+ mdb_txn_reset( moi->moi_txn );
+ LDAP_SLIST_REMOVE( &op->o_extra, &moi->moi_oe, OpExtra, oe_next );
+ }
+ if ( e )
+ mdb_entry_return( e );
+ return rc;
+}
--- /dev/null
+/* search.c - search operation */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2011 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-mdb.h"
+#include "idl.h"
+
+static int base_candidate(
+ BackendDB *be,
+ Entry *e,
+ ID *ids );
+
+static int search_candidates(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e,
+ MDB_txn *txn,
+ ID *ids,
+ ID *scopes );
+
+static int parse_paged_cookie( Operation *op, SlapReply *rs );
+
+static void send_paged_response(
+ Operation *op,
+ SlapReply *rs,
+ ID *lastid,
+ int tentries );
+
+/* Dereference aliases for a single alias entry. Return the final
+ * dereferenced entry on success, NULL on any failure.
+ */
+static Entry * deref_base (
+ Operation *op,
+ SlapReply *rs,
+ Entry *e,
+ Entry **matched,
+ MDB_txn *txn,
+ ID *tmp,
+ ID *visited )
+{
+ struct berval ndn;
+
+ rs->sr_err = LDAP_ALIAS_DEREF_PROBLEM;
+ rs->sr_text = "maximum deref depth exceeded";
+
+ for (;;) {
+ /* Remember the last entry we looked at, so we can
+ * report broken links
+ */
+ *matched = e;
+
+ if (MDB_IDL_N(tmp) >= op->o_bd->be_max_deref_depth) {
+ e = NULL;
+ break;
+ }
+
+ /* If this is part of a subtree or onelevel search,
+ * have we seen this ID before? If so, quit.
+ */
+ if ( visited && mdb_idl_insert( visited, e->e_id ) ) {
+ e = NULL;
+ break;
+ }
+
+ /* If we've seen this ID during this deref iteration,
+ * we've hit a loop.
+ */
+ if ( mdb_idl_insert( tmp, e->e_id ) ) {
+ rs->sr_err = LDAP_ALIAS_PROBLEM;
+ rs->sr_text = "circular alias";
+ e = NULL;
+ break;
+ }
+
+ /* If there was a problem getting the aliasedObjectName,
+ * get_alias_dn will have set the error status.
+ */
+ if ( get_alias_dn(e, &ndn, &rs->sr_err, &rs->sr_text) ) {
+ e = NULL;
+ break;
+ }
+
+ rs->sr_err = mdb_dn2entry( op, txn, &ndn, &e, 0 );
+ if (rs->sr_err) {
+ rs->sr_err = LDAP_ALIAS_PROBLEM;
+ rs->sr_text = "aliasedObject not found";
+ break;
+ }
+
+ /* Free the previous entry, continue to work with the
+ * one we just retrieved.
+ */
+ mdb_entry_return( *matched );
+
+ /* We found a regular entry. Return this to the caller.
+ */
+ if (!is_entry_alias(e)) {
+ rs->sr_err = LDAP_SUCCESS;
+ rs->sr_text = NULL;
+ break;
+ }
+ }
+ return e;
+}
+
+/* Look for and dereference all aliases within the search scope.
+ * Requires "stack" to be able to hold 6 levels of DB_SIZE IDLs.
+ * Of course we're hardcoded to require a minimum of 8 UM_SIZE
+ * IDLs so this is never a problem.
+ */
+static int search_aliases(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e,
+ MDB_txn *txn,
+ ID *scopes,
+ ID *stack )
+{
+ ID *aliases, *curscop, *visited, *newsubs, *oldsubs, *tmp;
+ ID cursora, ida, cursoro, ido;
+ Entry *matched, *a;
+ struct berval bv_alias = BER_BVC( "alias" );
+ AttributeAssertion aa_alias = ATTRIBUTEASSERTION_INIT;
+ Filter af;
+ int first = 1;
+
+ aliases = stack; /* IDL of all aliases in the database */
+ curscop = aliases + MDB_IDL_DB_SIZE; /* Aliases in the current scope */
+ visited = curscop + MDB_IDL_DB_SIZE; /* IDs we've seen in this search */
+ newsubs = visited + MDB_IDL_DB_SIZE; /* New subtrees we've added */
+ oldsubs = newsubs + MDB_IDL_DB_SIZE; /* Subtrees added previously */
+ tmp = oldsubs + MDB_IDL_DB_SIZE; /* Scratch space for deref_base() */
+
+ af.f_choice = LDAP_FILTER_EQUALITY;
+ af.f_ava = &aa_alias;
+ af.f_av_desc = slap_schema.si_ad_objectClass;
+ af.f_av_value = bv_alias;
+ af.f_next = NULL;
+
+ /* Find all aliases in database */
+ MDB_IDL_ZERO( aliases );
+ rs->sr_err = mdb_filter_candidates( op, txn, &af, aliases,
+ curscop, visited );
+ if (rs->sr_err != LDAP_SUCCESS) {
+ return rs->sr_err;
+ }
+ oldsubs[0] = 1;
+ oldsubs[1] = e->e_id;
+
+ MDB_IDL_ZERO( visited );
+ MDB_IDL_ZERO( newsubs );
+
+ cursoro = 0;
+ ido = mdb_idl_first( oldsubs, &cursoro );
+
+ for (;;) {
+ /* Set curscop to only the aliases in the current scope. Start with
+ * all the aliases, then get the intersection with the scope.
+ */
+ rs->sr_err = mdb_idscope( op, txn, e->e_id, aliases, curscop );
+
+ if (first) {
+ first = 0;
+ } else {
+ mdb_entry_return( e );
+ }
+
+ /* Dereference all of the aliases in the current scope. */
+ cursora = 0;
+ for (ida = mdb_idl_first(curscop, &cursora); ida != NOID;
+ ida = mdb_idl_next(curscop, &cursora))
+ {
+ rs->sr_err = mdb_id2entry(op, txn, ida, &a);
+ if (rs->sr_err != LDAP_SUCCESS) {
+ continue;
+ }
+
+ /* This should only happen if the curscop IDL has maxed out and
+ * turned into a range that spans IDs indiscriminately
+ */
+ if (!is_entry_alias(a)) {
+ mdb_entry_return (a);
+ continue;
+ }
+
+ /* Actually dereference the alias */
+ MDB_IDL_ZERO(tmp);
+ a = deref_base( op, rs, a, &matched, txn,
+ tmp, visited );
+ if (a) {
+ /* If the target was not already in our current scopes,
+ * make note of it in the newsubs list.
+ */
+ if (mdb_idl_insert(scopes, a->e_id) == 0) {
+ mdb_idl_insert(newsubs, a->e_id);
+ }
+ mdb_entry_return( a );
+
+ } else if (matched) {
+ /* Alias could not be dereferenced, or it deref'd to
+ * an ID we've already seen. Ignore it.
+ */
+ mdb_entry_return( matched );
+ rs->sr_text = NULL;
+ }
+ }
+ /* If this is a OneLevel search, we're done; oldsubs only had one
+ * ID in it. For a Subtree search, oldsubs may be a list of scope IDs.
+ */
+ if ( op->ors_scope == LDAP_SCOPE_ONELEVEL ) break;
+nextido:
+ ido = mdb_idl_next( oldsubs, &cursoro );
+
+ /* If we're done processing the old scopes, did we add any new
+ * scopes in this iteration? If so, go back and do those now.
+ */
+ if (ido == NOID) {
+ if (MDB_IDL_IS_ZERO(newsubs)) break;
+ MDB_IDL_CPY(oldsubs, newsubs);
+ MDB_IDL_ZERO(newsubs);
+ cursoro = 0;
+ ido = mdb_idl_first( oldsubs, &cursoro );
+ }
+
+ /* Find the entry corresponding to the next scope. If it can't
+ * be found, ignore it and move on. This should never happen;
+ * we should never see the ID of an entry that doesn't exist.
+ */
+ rs->sr_err = mdb_id2entry(op, txn, ido, &e);
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ goto nextido;
+ }
+ }
+ return rs->sr_err;
+}
+
+/* Get the next ID from the DB. Used if the candidate list is
+ * a range and simple iteration hits missing entryIDs
+ */
+static int
+mdb_get_nextid(struct mdb_info *mdb, MDB_txn *txn, ID *cursor)
+{
+ MDB_cursor *curs;
+ MDB_val key;
+ ID id;
+ int rc;
+
+ id = *cursor + 1;
+ rc = mdb_cursor_open( txn, mdb->mi_id2entry, &curs );
+ if ( rc )
+ return rc;
+ key.mv_data = &id;
+ key.mv_size = sizeof(ID);
+ rc = mdb_cursor_get( curs, &key, NULL, MDB_SET_RANGE );
+ mdb_cursor_close( curs );
+ if ( rc )
+ return rc;
+ memcpy( cursor, key.mv_data, sizeof(ID));
+ return 0;
+}
+
+int
+mdb_search( Operation *op, SlapReply *rs )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ ID id, cursor;
+ ID lastid = NOID;
+ ID candidates[MDB_IDL_UM_SIZE];
+ ID scopes[MDB_IDL_DB_SIZE];
+ Entry *e = NULL, base;
+ Entry *matched = NULL;
+ AttributeName *attrs;
+ struct berval realbase = BER_BVNULL;
+ slap_mask_t mask;
+ time_t stoptime;
+ int manageDSAit;
+ int tentries = 0;
+
+ mdb_op_info opinfo = {0}, *moi = &opinfo;
+ MDB_txn *ltid = NULL;
+ MDB_cursor *idcursor = NULL;
+
+ Debug( LDAP_DEBUG_TRACE, "=> " LDAP_XSTRING(mdb_search) "\n", 0, 0, 0);
+ attrs = op->oq_search.rs_attrs;
+
+ manageDSAit = get_manageDSAit( op );
+
+ rs->sr_err = mdb_opinfo_get( op, mdb, 1, &moi );
+ switch(rs->sr_err) {
+ case 0:
+ break;
+ default:
+ send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
+ return rs->sr_err;
+ }
+
+ ltid = moi->moi_txn;
+
+ if ( op->ors_deref & LDAP_DEREF_FINDING ) {
+ MDB_IDL_ZERO(candidates);
+ }
+dn2entry_retry:
+ /* get entry with reader lock */
+ rs->sr_err = mdb_dn2entry( op, ltid, &op->o_req_ndn, &e, 1 );
+
+ switch(rs->sr_err) {
+ case MDB_NOTFOUND:
+ matched = e;
+ e = NULL;
+ break;
+ case 0:
+ break;
+ case LDAP_BUSY:
+ send_ldap_error( op, rs, LDAP_BUSY, "ldap server busy" );
+ goto done;
+ default:
+ send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
+ goto done;
+ }
+
+ if ( op->ors_deref & LDAP_DEREF_FINDING ) {
+ if ( matched && is_entry_alias( matched )) {
+ struct berval stub;
+
+ stub.bv_val = op->o_req_ndn.bv_val;
+ stub.bv_len = op->o_req_ndn.bv_len - matched->e_nname.bv_len - 1;
+ e = deref_base( op, rs, matched, &matched, ltid,
+ candidates, NULL );
+ if ( e ) {
+ build_new_dn( &op->o_req_ndn, &e->e_nname, &stub,
+ op->o_tmpmemctx );
+ mdb_entry_return (e);
+ matched = NULL;
+ goto dn2entry_retry;
+ }
+ } else if ( e && is_entry_alias( e )) {
+ e = deref_base( op, rs, e, &matched, ltid,
+ candidates, NULL );
+ }
+ }
+
+ if ( e == NULL ) {
+ struct berval matched_dn = BER_BVNULL;
+
+ if ( matched != NULL ) {
+ BerVarray erefs = NULL;
+
+ /* return referral only if "disclose"
+ * is granted on the object */
+ if ( ! access_allowed( op, matched,
+ slap_schema.si_ad_entry,
+ NULL, ACL_DISCLOSE, NULL ) )
+ {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+
+ } else {
+ ber_dupbv( &matched_dn, &matched->e_name );
+
+ erefs = is_entry_referral( matched )
+ ? get_entry_referrals( op, matched )
+ : NULL;
+ if ( rs->sr_err == MDB_NOTFOUND )
+ rs->sr_err = LDAP_REFERRAL;
+ rs->sr_matched = matched_dn.bv_val;
+ }
+
+ mdb_entry_return (matched);
+ matched = NULL;
+
+ if ( erefs ) {
+ rs->sr_ref = referral_rewrite( erefs, &matched_dn,
+ &op->o_req_dn, op->oq_search.rs_scope );
+ ber_bvarray_free( erefs );
+ }
+
+ } else {
+ rs->sr_ref = referral_rewrite( default_referral,
+ NULL, &op->o_req_dn, op->oq_search.rs_scope );
+ rs->sr_err = rs->sr_ref != NULL ? LDAP_REFERRAL : LDAP_NO_SUCH_OBJECT;
+ }
+
+ send_ldap_result( op, rs );
+
+ if ( rs->sr_ref ) {
+ ber_bvarray_free( rs->sr_ref );
+ rs->sr_ref = NULL;
+ }
+ if ( !BER_BVISNULL( &matched_dn ) ) {
+ ber_memfree( matched_dn.bv_val );
+ rs->sr_matched = NULL;
+ }
+ goto done;
+ }
+
+ /* NOTE: __NEW__ "search" access is required
+ * on searchBase object */
+ if ( ! access_allowed_mask( op, e, slap_schema.si_ad_entry,
+ NULL, ACL_SEARCH, NULL, &mask ) )
+ {
+ if ( !ACL_GRANT( mask, ACL_DISCLOSE ) ) {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ } else {
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ }
+
+ mdb_entry_return(e);
+ send_ldap_result( op, rs );
+ goto done;
+ }
+
+ if ( !manageDSAit && is_entry_referral( e ) ) {
+ /* entry is a referral */
+ struct berval matched_dn = BER_BVNULL;
+ BerVarray erefs = NULL;
+
+ ber_dupbv( &matched_dn, &e->e_name );
+ erefs = get_entry_referrals( op, e );
+
+ rs->sr_err = LDAP_REFERRAL;
+
+ mdb_entry_return( e );
+ e = NULL;
+
+ if ( erefs ) {
+ rs->sr_ref = referral_rewrite( erefs, &matched_dn,
+ &op->o_req_dn, op->oq_search.rs_scope );
+ ber_bvarray_free( erefs );
+
+ if ( !rs->sr_ref ) {
+ rs->sr_text = "bad_referral object";
+ }
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_search) ": entry is referral\n",
+ 0, 0, 0 );
+
+ rs->sr_matched = matched_dn.bv_val;
+ send_ldap_result( op, rs );
+
+ ber_bvarray_free( rs->sr_ref );
+ rs->sr_ref = NULL;
+ ber_memfree( matched_dn.bv_val );
+ rs->sr_matched = NULL;
+ goto done;
+ }
+
+ if ( get_assert( op ) &&
+ ( test_filter( op, e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
+ {
+ rs->sr_err = LDAP_ASSERTION_FAILED;
+ mdb_entry_return(e);
+ send_ldap_result( op, rs );
+ goto done;
+ }
+
+ /* compute it anyway; root does not use it */
+ stoptime = op->o_time + op->ors_tlimit;
+
+ /* need normalized dn below */
+ ber_dupbv( &realbase, &e->e_nname );
+
+ /* Copy info to base, must free entry before accessing the database
+ * in search_candidates, to avoid deadlocks.
+ */
+ base.e_private = e->e_private;
+ base.e_nname = realbase;
+ base.e_id = e->e_id;
+
+ mdb_entry_return(e);
+ e = NULL;
+
+ /* select candidates */
+ if ( op->oq_search.rs_scope == LDAP_SCOPE_BASE ) {
+ rs->sr_err = base_candidate( op->o_bd, &base, candidates );
+
+ } else {
+ MDB_IDL_ZERO( candidates );
+ MDB_IDL_ZERO( scopes );
+ mdb_idl_insert( scopes, base.e_id );
+ rs->sr_err = search_candidates( op, rs, &base,
+ ltid, candidates, scopes );
+ }
+
+ /* start cursor at beginning of candidates.
+ */
+ cursor = 0;
+
+ if ( candidates[0] == 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_search) ": no candidates\n",
+ 0, 0, 0 );
+
+ goto nochange;
+ }
+
+ /* if not root and candidates exceed to-be-checked entries, abort */
+ if ( op->ors_limit /* isroot == FALSE */ &&
+ op->ors_limit->lms_s_unchecked != -1 &&
+ MDB_IDL_N(candidates) > (unsigned) op->ors_limit->lms_s_unchecked )
+ {
+ rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
+ send_ldap_result( op, rs );
+ rs->sr_err = LDAP_SUCCESS;
+ goto done;
+ }
+
+ if ( op->ors_limit == NULL /* isroot == TRUE */ ||
+ !op->ors_limit->lms_s_pr_hide )
+ {
+ tentries = MDB_IDL_N(candidates);
+ }
+
+ if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) {
+ PagedResultsState *ps = op->o_pagedresults_state;
+ /* deferred cookie parsing */
+ rs->sr_err = parse_paged_cookie( op, rs );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ goto done;
+ }
+
+ cursor = (ID) ps->ps_cookie;
+ if ( cursor && ps->ps_size == 0 ) {
+ rs->sr_err = LDAP_SUCCESS;
+ rs->sr_text = "search abandoned by pagedResult size=0";
+ send_ldap_result( op, rs );
+ goto done;
+ }
+ id = mdb_idl_first( candidates, &cursor );
+ if ( id == NOID ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_search)
+ ": no paged results candidates\n",
+ 0, 0, 0 );
+ send_paged_response( op, rs, &lastid, 0 );
+
+ rs->sr_err = LDAP_OTHER;
+ goto done;
+ }
+ if ( id == (ID)ps->ps_cookie )
+ id = mdb_idl_next( candidates, &cursor );
+ goto loop_begin;
+ }
+
+ for ( id = mdb_idl_first( candidates, &cursor );
+ id != NOID ; id = mdb_idl_next( candidates, &cursor ) )
+ {
+ int scopeok;
+
+loop_begin:
+
+ /* check for abandon */
+ if ( op->o_abandon ) {
+ rs->sr_err = SLAPD_ABANDON;
+ send_ldap_result( op, rs );
+ goto done;
+ }
+
+ /* mostly needed by internal searches,
+ * e.g. related to syncrepl, for whom
+ * abandon does not get set... */
+ if ( slapd_shutdown ) {
+ rs->sr_err = LDAP_UNAVAILABLE;
+ send_ldap_disconnect( op, rs );
+ goto done;
+ }
+
+ /* check time limit */
+ if ( op->ors_tlimit != SLAP_NO_LIMIT
+ && slap_get_time() > stoptime )
+ {
+ rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
+ rs->sr_ref = rs->sr_v2ref;
+ send_ldap_result( op, rs );
+ rs->sr_err = LDAP_SUCCESS;
+ goto done;
+ }
+
+ /* get the entry */
+ rs->sr_err = mdb_id2entry( op, ltid, id, &e );
+
+ if (rs->sr_err == LDAP_BUSY) {
+ rs->sr_text = "ldap server busy";
+ send_ldap_result( op, rs );
+ goto done;
+
+ } else if ( rs->sr_err == LDAP_OTHER ) {
+ rs->sr_text = "internal error";
+ send_ldap_result( op, rs );
+ goto done;
+ }
+
+ if ( e == NULL ) {
+ if( !MDB_IDL_IS_RANGE(candidates) ) {
+ /* only complain for non-range IDLs */
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_search)
+ ": candidate %ld not found\n",
+ (long) id, 0, 0 );
+ } else {
+ /* get the next ID from the DB */
+ rs->sr_err = mdb_get_nextid( mdb, ltid, &cursor );
+ if ( rs->sr_err == MDB_NOTFOUND ) {
+ break;
+ }
+ if ( rs->sr_err ) {
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error in get_nextid";
+ send_ldap_result( op, rs );
+ goto done;
+ }
+ cursor--;
+ }
+
+ goto loop_continue;
+ }
+
+ if ( is_entry_subentry( e ) ) {
+ if( op->oq_search.rs_scope != LDAP_SCOPE_BASE ) {
+ if(!get_subentries_visibility( op )) {
+ /* only subentries are visible */
+ goto loop_continue;
+ }
+
+ } else if ( get_subentries( op ) &&
+ !get_subentries_visibility( op ))
+ {
+ /* only subentries are visible */
+ goto loop_continue;
+ }
+
+ } else if ( get_subentries_visibility( op )) {
+ /* only subentries are visible */
+ goto loop_continue;
+ }
+
+ /* Does this candidate actually satisfy the search scope?
+ */
+ scopeok = 0;
+ switch( op->ors_scope ) {
+ case LDAP_SCOPE_BASE:
+ /* This is always true, yes? */
+ if ( id == base.e_id ) scopeok = 1;
+ break;
+
+#ifdef LDAP_SCOPE_CHILDREN
+ case LDAP_SCOPE_CHILDREN:
+ if ( id == base.e_id ) break;
+ /* Fall-thru */
+#endif
+ case LDAP_SCOPE_SUBTREE:
+ if ( id == base.e_id ) {
+ scopeok = 1;
+ break;
+ }
+ /* Fall-thru */
+ case LDAP_SCOPE_ONELEVEL:
+ if ( mdb_idscopes( op, ltid, &idcursor, id, scopes ) == MDB_SUCCESS ) scopeok = 1;
+ break;
+ }
+
+ /* aliases were already dereferenced in candidate list */
+ if ( op->ors_deref & LDAP_DEREF_SEARCHING ) {
+ /* but if the search base is an alias, and we didn't
+ * deref it when finding, return it.
+ */
+ if ( is_entry_alias(e) &&
+ ((op->ors_deref & LDAP_DEREF_FINDING) ||
+ !bvmatch(&e->e_nname, &op->o_req_ndn)))
+ {
+ goto loop_continue;
+ }
+ }
+
+ /* Not in scope, ignore it */
+ if ( !scopeok )
+ {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_search)
+ ": %ld scope not okay\n",
+ (long) id, 0, 0 );
+ goto loop_continue;
+ }
+
+ if ( !manageDSAit && is_entry_glue( e )) {
+ goto loop_continue;
+ }
+
+ mdb_id2name( op, ltid, &idcursor, e->e_id,
+ &e->e_name, &e->e_nname );
+
+ /*
+ * if it's a referral, add it to the list of referrals. only do
+ * this for non-base searches, and don't check the filter
+ * explicitly here since it's only a candidate anyway.
+ */
+ if ( !manageDSAit && op->oq_search.rs_scope != LDAP_SCOPE_BASE
+ && is_entry_referral( e ) )
+ {
+ BerVarray erefs = get_entry_referrals( op, e );
+ rs->sr_ref = referral_rewrite( erefs, &e->e_name, NULL,
+ op->oq_search.rs_scope == LDAP_SCOPE_ONELEVEL
+ ? LDAP_SCOPE_BASE : LDAP_SCOPE_SUBTREE );
+
+ rs->sr_entry = e;
+ rs->sr_flags = 0;
+
+ send_search_reference( op, rs );
+
+ mdb_entry_return( e );
+ rs->sr_entry = NULL;
+ e = NULL;
+
+ ber_bvarray_free( rs->sr_ref );
+ ber_bvarray_free( erefs );
+ rs->sr_ref = NULL;
+
+ goto loop_continue;
+ }
+
+ /* if it matches the filter and scope, send it */
+ rs->sr_err = test_filter( op, e, op->oq_search.rs_filter );
+
+ if ( rs->sr_err == LDAP_COMPARE_TRUE ) {
+ /* check size limit */
+ if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
+ if ( rs->sr_nentries >= ((PagedResultsState *)op->o_pagedresults_state)->ps_size ) {
+ mdb_entry_return( e );
+ e = NULL;
+ send_paged_response( op, rs, &lastid, tentries );
+ goto done;
+ }
+ lastid = id;
+ }
+
+ if (e) {
+ /* safe default */
+ rs->sr_attrs = op->oq_search.rs_attrs;
+ rs->sr_operational_attrs = NULL;
+ rs->sr_ctrls = NULL;
+ rs->sr_entry = e;
+ RS_ASSERT( e->e_private != NULL );
+ rs->sr_flags = 0;
+ rs->sr_err = LDAP_SUCCESS;
+ rs->sr_err = send_search_entry( op, rs );
+ rs->sr_attrs = NULL;
+ rs->sr_entry = NULL;
+ mdb_entry_return( e );
+ e = NULL;
+
+ switch ( rs->sr_err ) {
+ case LDAP_SUCCESS: /* entry sent ok */
+ break;
+ default: /* entry not sent */
+ break;
+ case LDAP_UNAVAILABLE:
+ case LDAP_SIZELIMIT_EXCEEDED:
+ if ( rs->sr_err == LDAP_SIZELIMIT_EXCEEDED ) {
+ rs->sr_ref = rs->sr_v2ref;
+ send_ldap_result( op, rs );
+ rs->sr_err = LDAP_SUCCESS;
+
+ } else {
+ rs->sr_err = LDAP_OTHER;
+ }
+ goto done;
+ }
+ }
+
+ } else {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_search)
+ ": %ld does not match filter\n",
+ (long) id, 0, 0 );
+ }
+
+loop_continue:
+ if( e != NULL ) {
+ /* free reader lock */
+ mdb_entry_return( e );
+ RS_ASSERT( rs->sr_entry == NULL );
+ e = NULL;
+ rs->sr_entry = NULL;
+ }
+ }
+
+nochange:
+ rs->sr_ctrls = NULL;
+ rs->sr_ref = rs->sr_v2ref;
+ rs->sr_err = (rs->sr_v2ref == NULL) ? LDAP_SUCCESS : LDAP_REFERRAL;
+ rs->sr_rspoid = NULL;
+ if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
+ send_paged_response( op, rs, NULL, 0 );
+ } else {
+ send_ldap_result( op, rs );
+ }
+
+ rs->sr_err = LDAP_SUCCESS;
+
+done:
+ if ( moi == &opinfo ) {
+ mdb_txn_reset( moi->moi_txn );
+ LDAP_SLIST_REMOVE( &op->o_extra, &moi->moi_oe, OpExtra, oe_next );
+ }
+ if( idcursor )
+ mdb_cursor_close( idcursor );
+ if( rs->sr_v2ref ) {
+ ber_bvarray_free( rs->sr_v2ref );
+ rs->sr_v2ref = NULL;
+ }
+ if( realbase.bv_val ) ch_free( realbase.bv_val );
+
+ return rs->sr_err;
+}
+
+
+static int base_candidate(
+ BackendDB *be,
+ Entry *e,
+ ID *ids )
+{
+ Debug(LDAP_DEBUG_ARGS, "base_candidates: base: \"%s\" (0x%08lx)\n",
+ e->e_nname.bv_val, (long) e->e_id, 0);
+
+ ids[0] = 1;
+ ids[1] = e->e_id;
+ return 0;
+}
+
+/* Look for "objectClass Present" in this filter.
+ * Also count depth of filter tree while we're at it.
+ */
+static int oc_filter(
+ Filter *f,
+ int cur,
+ int *max )
+{
+ int rc = 0;
+
+ assert( f != NULL );
+
+ if( cur > *max ) *max = cur;
+
+ switch( f->f_choice ) {
+ case LDAP_FILTER_PRESENT:
+ if (f->f_desc == slap_schema.si_ad_objectClass) {
+ rc = 1;
+ }
+ break;
+
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ cur++;
+ for ( f=f->f_and; f; f=f->f_next ) {
+ (void) oc_filter(f, cur, max);
+ }
+ break;
+
+ default:
+ break;
+ }
+ return rc;
+}
+
+static void search_stack_free( void *key, void *data )
+{
+ ber_memfree_x(data, NULL);
+}
+
+static void *search_stack( Operation *op )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ void *ret = NULL;
+
+ if ( op->o_threadctx ) {
+ ldap_pvt_thread_pool_getkey( op->o_threadctx, (void *)search_stack,
+ &ret, NULL );
+ } else {
+ ret = mdb->mi_search_stack;
+ }
+
+ if ( !ret ) {
+ ret = ch_malloc( mdb->mi_search_stack_depth * MDB_IDL_UM_SIZE
+ * sizeof( ID ) );
+ if ( op->o_threadctx ) {
+ ldap_pvt_thread_pool_setkey( op->o_threadctx, (void *)search_stack,
+ ret, search_stack_free, NULL, NULL );
+ } else {
+ mdb->mi_search_stack = ret;
+ }
+ }
+ return ret;
+}
+
+static int search_candidates(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e,
+ MDB_txn *txn,
+ ID *ids,
+ ID *scopes )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ int rc, depth = 1;
+ Filter *f, rf, xf, nf, sf;
+ ID *stack;
+ AttributeAssertion aa_ref = ATTRIBUTEASSERTION_INIT;
+ AttributeAssertion aa_subentry = ATTRIBUTEASSERTION_INIT;
+
+ /*
+ * This routine takes as input a filter (user-filter)
+ * and rewrites it as follows:
+ * (&(scope=DN)[(objectClass=subentry)]
+ * (|[(objectClass=referral)](user-filter))
+ */
+
+ Debug(LDAP_DEBUG_TRACE,
+ "search_candidates: base=\"%s\" (0x%08lx) scope=%d\n",
+ e->e_nname.bv_val, (long) e->e_id, op->oq_search.rs_scope );
+
+ f = op->oq_search.rs_filter;
+
+ /* If the user's filter uses objectClass=*,
+ * these clauses are redundant.
+ */
+ if (!oc_filter(op->oq_search.rs_filter, 1, &depth)
+ && !get_subentries_visibility(op)) {
+ if( !get_manageDSAit(op) && !get_domainScope(op) ) {
+ /* match referral objects */
+ struct berval bv_ref = BER_BVC( "referral" );
+ rf.f_choice = LDAP_FILTER_EQUALITY;
+ rf.f_ava = &aa_ref;
+ rf.f_av_desc = slap_schema.si_ad_objectClass;
+ rf.f_av_value = bv_ref;
+ rf.f_next = f;
+ xf.f_or = &rf;
+ xf.f_choice = LDAP_FILTER_OR;
+ xf.f_next = NULL;
+ f = &xf;
+ depth++;
+ }
+ }
+
+ if( get_subentries_visibility( op ) ) {
+ struct berval bv_subentry = BER_BVC( "subentry" );
+ sf.f_choice = LDAP_FILTER_EQUALITY;
+ sf.f_ava = &aa_subentry;
+ sf.f_av_desc = slap_schema.si_ad_objectClass;
+ sf.f_av_value = bv_subentry;
+ sf.f_next = f;
+ nf.f_choice = LDAP_FILTER_AND;
+ nf.f_and = &sf;
+ nf.f_next = NULL;
+ f = &nf;
+ depth++;
+ }
+
+ /* Allocate IDL stack, plus 1 more for former tmp */
+ if ( depth+1 > mdb->mi_search_stack_depth ) {
+ stack = ch_malloc( (depth + 1) * MDB_IDL_UM_SIZE * sizeof( ID ) );
+ } else {
+ stack = search_stack( op );
+ }
+
+ if( op->ors_deref & LDAP_DEREF_SEARCHING ) {
+ rc = search_aliases( op, rs, e, txn, scopes, stack );
+ } else {
+ rc = LDAP_SUCCESS;
+ }
+
+ if ( rc == LDAP_SUCCESS ) {
+ rc = mdb_filter_candidates( op, txn, f, ids,
+ stack, stack+MDB_IDL_UM_SIZE );
+ }
+
+ if ( depth+1 > mdb->mi_search_stack_depth ) {
+ ch_free( stack );
+ }
+
+ if( rc ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "mdb_search_candidates: failed (rc=%d)\n",
+ rc, NULL, NULL );
+
+ } else {
+ Debug(LDAP_DEBUG_TRACE,
+ "mdb_search_candidates: id=%ld first=%ld last=%ld\n",
+ (long) ids[0],
+ (long) MDB_IDL_FIRST(ids),
+ (long) MDB_IDL_LAST(ids) );
+ }
+
+ return rc;
+}
+
+static int
+parse_paged_cookie( Operation *op, SlapReply *rs )
+{
+ int rc = LDAP_SUCCESS;
+ PagedResultsState *ps = op->o_pagedresults_state;
+
+ /* this function must be invoked only if the pagedResults
+ * control has been detected, parsed and partially checked
+ * by the frontend */
+ assert( get_pagedresults( op ) > SLAP_CONTROL_IGNORED );
+
+ /* cookie decoding/checks deferred to backend... */
+ if ( ps->ps_cookieval.bv_len ) {
+ PagedResultsCookie reqcookie;
+ if( ps->ps_cookieval.bv_len != sizeof( reqcookie ) ) {
+ /* bad cookie */
+ rs->sr_text = "paged results cookie is invalid";
+ rc = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+
+ AC_MEMCPY( &reqcookie, ps->ps_cookieval.bv_val, sizeof( reqcookie ));
+
+ if ( reqcookie > ps->ps_cookie ) {
+ /* bad cookie */
+ rs->sr_text = "paged results cookie is invalid";
+ rc = LDAP_PROTOCOL_ERROR;
+ goto done;
+
+ } else if ( reqcookie < ps->ps_cookie ) {
+ rs->sr_text = "paged results cookie is invalid or old";
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ } else {
+ /* we're going to use ps_cookie */
+ op->o_conn->c_pagedresults_state.ps_cookie = 0;
+ }
+
+done:;
+
+ return rc;
+}
+
+static void
+send_paged_response(
+ Operation *op,
+ SlapReply *rs,
+ ID *lastid,
+ int tentries )
+{
+ LDAPControl *ctrls[2];
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ PagedResultsCookie respcookie;
+ struct berval cookie;
+
+ Debug(LDAP_DEBUG_ARGS,
+ "send_paged_response: lastid=0x%08lx nentries=%d\n",
+ lastid ? *lastid : 0, rs->sr_nentries, NULL );
+
+ ctrls[1] = NULL;
+
+ ber_init2( ber, NULL, LBER_USE_DER );
+
+ if ( lastid ) {
+ respcookie = ( PagedResultsCookie )(*lastid);
+ cookie.bv_len = sizeof( respcookie );
+ cookie.bv_val = (char *)&respcookie;
+
+ } else {
+ respcookie = ( PagedResultsCookie )0;
+ BER_BVSTR( &cookie, "" );
+ }
+
+ op->o_conn->c_pagedresults_state.ps_cookie = respcookie;
+ op->o_conn->c_pagedresults_state.ps_count =
+ ((PagedResultsState *)op->o_pagedresults_state)->ps_count +
+ rs->sr_nentries;
+
+ /* return size of 0 -- no estimate */
+ ber_printf( ber, "{iO}", 0, &cookie );
+
+ ctrls[0] = op->o_tmpalloc( sizeof(LDAPControl), op->o_tmpmemctx );
+ if ( ber_flatten2( ber, &ctrls[0]->ldctl_value, 0 ) == -1 ) {
+ goto done;
+ }
+
+ ctrls[0]->ldctl_oid = LDAP_CONTROL_PAGEDRESULTS;
+ ctrls[0]->ldctl_iscritical = 0;
+
+ slap_add_ctrls( op, rs, ctrls );
+ rs->sr_err = LDAP_SUCCESS;
+ send_ldap_result( op, rs );
+
+done:
+ (void) ber_free_buf( ber );
+}
--- /dev/null
+/* tools.c - tools for slap tools */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2011 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include <ac/errno.h>
+
+#define AVL_INTERNAL
+#include "back-mdb.h"
+#include "idl.h"
+
+static MDB_txn *txn = NULL, *txi = NULL;
+static MDB_cursor *cursor = NULL, *idcursor = NULL;
+static MDB_val key, data;
+static EntryHeader eh;
+static ID previd = NOID;
+
+typedef struct dn_id {
+ ID id;
+ struct berval dn;
+} dn_id;
+
+#define HOLE_SIZE 4096
+static dn_id hbuf[HOLE_SIZE], *holes = hbuf;
+static unsigned nhmax = HOLE_SIZE;
+static unsigned nholes;
+
+static struct berval *tool_base;
+static int tool_scope;
+static Filter *tool_filter;
+static Entry *tool_next_entry;
+
+#if 0
+static ID mdb_tool_ix_id;
+static Operation *mdb_tool_ix_op;
+static int *mdb_tool_index_threads, mdb_tool_index_tcount;
+static void *mdb_tool_index_rec;
+static struct mdb_info *mdb_tool_info;
+static ldap_pvt_thread_mutex_t mdb_tool_index_mutex;
+static ldap_pvt_thread_cond_t mdb_tool_index_cond_main;
+static ldap_pvt_thread_cond_t mdb_tool_index_cond_work;
+static void * mdb_tool_index_task( void *ctx, void *ptr );
+#endif
+
+static int mdb_writes, mdb_writes_per_commit;
+
+static int
+mdb_tool_entry_get_int( BackendDB *be, ID id, Entry **ep );
+
+int mdb_tool_entry_open(
+ BackendDB *be, int mode )
+{
+ /* In Quick mode, commit once per 1000 entries */
+ mdb_writes = 0;
+ if ( slapMode & SLAP_TOOL_QUICK )
+ mdb_writes_per_commit = 1000;
+ else
+ mdb_writes_per_commit = 1;
+
+#if 0
+ /* Set up for threaded slapindex */
+ if (( slapMode & (SLAP_TOOL_QUICK|SLAP_TOOL_READONLY)) == SLAP_TOOL_QUICK ) {
+ if ( !mdb_tool_info ) {
+ ldap_pvt_thread_mutex_init( &mdb_tool_index_mutex );
+ ldap_pvt_thread_cond_init( &mdb_tool_index_cond_main );
+ ldap_pvt_thread_cond_init( &mdb_tool_index_cond_work );
+ if ( mdb->bi_nattrs ) {
+ int i;
+ mdb_tool_index_threads = ch_malloc( slap_tool_thread_max * sizeof( int ));
+ mdb_tool_index_rec = ch_malloc( mdb->bi_nattrs * sizeof( IndexRec ));
+ mdb_tool_index_tcount = slap_tool_thread_max - 1;
+ for (i=1; i<slap_tool_thread_max; i++) {
+ int *ptr = ch_malloc( sizeof( int ));
+ *ptr = i;
+ ldap_pvt_thread_pool_submit( &connection_pool,
+ mdb_tool_index_task, ptr );
+ }
+ }
+ mdb_tool_info = mdb;
+ }
+ }
+#endif
+
+ return 0;
+}
+
+int mdb_tool_entry_close(
+ BackendDB *be )
+{
+#if 0
+ if ( mdb_tool_info ) {
+ slapd_shutdown = 1;
+ ldap_pvt_thread_mutex_lock( &mdb_tool_index_mutex );
+
+ /* There might still be some threads starting */
+ while ( mdb_tool_index_tcount ) {
+ ldap_pvt_thread_cond_wait( &mdb_tool_index_cond_main,
+ &mdb_tool_index_mutex );
+ }
+
+ mdb_tool_index_tcount = slap_tool_thread_max - 1;
+ ldap_pvt_thread_cond_broadcast( &mdb_tool_index_cond_work );
+
+ /* Make sure all threads are stopped */
+ while ( mdb_tool_index_tcount ) {
+ ldap_pvt_thread_cond_wait( &mdb_tool_index_cond_main,
+ &mdb_tool_index_mutex );
+ }
+ ldap_pvt_thread_mutex_unlock( &mdb_tool_index_mutex );
+
+ mdb_tool_info = NULL;
+ slapd_shutdown = 0;
+ ch_free( mdb_tool_index_threads );
+ ch_free( mdb_tool_index_rec );
+ mdb_tool_index_tcount = slap_tool_thread_max - 1;
+ }
+#endif
+
+ if( idcursor ) {
+ mdb_cursor_close( idcursor );
+ idcursor = NULL;
+ }
+ if( cursor ) {
+ mdb_cursor_close( cursor );
+ cursor = NULL;
+ }
+ if( txn ) {
+ if ( mdb_txn_commit( txn ))
+ return -1;
+ txn = NULL;
+ }
+
+ if( nholes ) {
+ unsigned i;
+ fprintf( stderr, "Error, entries missing!\n");
+ for (i=0; i<nholes; i++) {
+ fprintf(stderr, " entry %ld: %s\n",
+ holes[i].id, holes[i].dn.bv_val);
+ }
+ nholes = 0;
+ return -1;
+ }
+
+ return 0;
+}
+
+ID
+mdb_tool_entry_first_x(
+ BackendDB *be,
+ struct berval *base,
+ int scope,
+ Filter *f )
+{
+ tool_base = base;
+ tool_scope = scope;
+ tool_filter = f;
+
+ return mdb_tool_entry_next( be );
+}
+
+ID mdb_tool_entry_next(
+ BackendDB *be )
+{
+ int rc;
+ ID id;
+ struct mdb_info *mdb;
+
+ assert( be != NULL );
+ assert( slapMode & SLAP_TOOL_MODE );
+
+ mdb = (struct mdb_info *) be->be_private;
+ assert( mdb != NULL );
+
+ if ( !txn ) {
+ rc = mdb_txn_begin( mdb->mi_dbenv, MDB_RDONLY, &txn );
+ if ( rc )
+ return NOID;
+ rc = mdb_cursor_open( txn, mdb->mi_id2entry, &cursor );
+ if ( rc ) {
+ mdb_txn_abort( txn );
+ return NOID;
+ }
+ }
+
+next:;
+ rc = mdb_cursor_get( cursor, &key, &data, MDB_NEXT );
+
+ if( rc ) {
+ return NOID;
+ }
+
+ previd = *(ID *)key.mv_data;
+ id = previd;
+
+ if ( tool_filter || tool_base ) {
+ static Operation op = {0};
+ static Opheader ohdr = {0};
+
+ op.o_hdr = &ohdr;
+ op.o_bd = be;
+ op.o_tmpmemctx = NULL;
+ op.o_tmpmfuncs = &ch_mfuncs;
+
+ if ( tool_next_entry ) {
+ mdb_entry_release( &op, tool_next_entry, 0 );
+ tool_next_entry = NULL;
+ }
+
+ rc = mdb_tool_entry_get_int( be, id, &tool_next_entry );
+ if ( rc == LDAP_NO_SUCH_OBJECT ) {
+ goto next;
+ }
+
+ assert( tool_next_entry != NULL );
+
+ if ( tool_filter && test_filter( NULL, tool_next_entry, tool_filter ) != LDAP_COMPARE_TRUE )
+ {
+ mdb_entry_release( &op, tool_next_entry, 0 );
+ tool_next_entry = NULL;
+ goto next;
+ }
+ }
+
+ return id;
+}
+
+ID mdb_tool_dn2id_get(
+ Backend *be,
+ struct berval *dn
+)
+{
+ struct mdb_info *mdb;
+ Operation op = {0};
+ Opheader ohdr = {0};
+ ID id;
+ int rc;
+
+ if ( BER_BVISEMPTY(dn) )
+ return 0;
+
+ mdb = (struct mdb_info *) be->be_private;
+
+ if ( !txn ) {
+ rc = mdb_txn_begin( mdb->mi_dbenv, (slapMode & SLAP_TOOL_READONLY) != 0 ?
+ MDB_RDONLY : 0, &txn );
+ if ( rc )
+ return NOID;
+ }
+
+ op.o_hdr = &ohdr;
+ op.o_bd = be;
+ op.o_tmpmemctx = NULL;
+ op.o_tmpmfuncs = &ch_mfuncs;
+
+ rc = mdb_dn2id( &op, txn, dn, &id, NULL, NULL );
+ if ( rc == MDB_NOTFOUND )
+ return NOID;
+
+ return id;
+}
+
+static int
+mdb_tool_entry_get_int( BackendDB *be, ID id, Entry **ep )
+{
+ Entry *e = NULL;
+ struct berval dn = BER_BVNULL, ndn = BER_BVNULL;
+ int rc;
+
+ assert( be != NULL );
+ assert( slapMode & SLAP_TOOL_MODE );
+
+ if ( ( tool_filter || tool_base ) && id == previd && tool_next_entry != NULL ) {
+ *ep = tool_next_entry;
+ tool_next_entry = NULL;
+ return LDAP_SUCCESS;
+ }
+
+ if ( id != previd ) {
+ key.mv_size = sizeof(ID);
+ key.mv_data = &id;
+ rc = mdb_cursor_get( cursor, &key, &data, MDB_SET );
+ if ( rc ) {
+ rc = LDAP_OTHER;
+ goto done;
+ }
+ }
+
+ if ( slapMode & SLAP_TOOL_READONLY ) {
+ Operation op = {0};
+ Opheader ohdr = {0};
+
+ op.o_hdr = &ohdr;
+ op.o_bd = be;
+ op.o_tmpmemctx = NULL;
+ op.o_tmpmfuncs = &ch_mfuncs;
+
+ rc = mdb_id2name( &op, txn, &idcursor, id, &dn, &ndn );
+ if ( rc ) {
+ rc = LDAP_OTHER;
+ mdb_entry_return( e );
+ e = NULL;
+ goto done;
+ }
+ if ( tool_base != NULL ) {
+ if ( !dnIsSuffixScope( &ndn, tool_base, tool_scope ) ) {
+ ch_free( dn.bv_val );
+ ch_free( ndn.bv_val );
+ rc = LDAP_NO_SUCH_OBJECT;
+ }
+ }
+ }
+ /* Get the header */
+ eh.bv.bv_val = data.mv_data;
+ eh.bv.bv_len = data.mv_size;
+
+ rc = entry_header( &eh );
+ if ( rc ) {
+ rc = LDAP_OTHER;
+ goto done;
+ }
+ eh.bv.bv_len = eh.nvals * sizeof( struct berval );
+ eh.bv.bv_val = ch_malloc( eh.bv.bv_len );
+ rc = entry_decode( &eh, &e );
+ e->e_id = id;
+ if ( !BER_BVISNULL( &dn )) {
+ e->e_name = dn;
+ e->e_nname = ndn;
+ } else {
+ e->e_name.bv_val = NULL;
+ e->e_nname.bv_val = NULL;
+ }
+ e->e_bv = eh.bv;
+
+done:
+ if ( e != NULL ) {
+ *ep = e;
+ }
+
+ return rc;
+}
+
+Entry*
+mdb_tool_entry_get( BackendDB *be, ID id )
+{
+ Entry *e = NULL;
+
+ (void)mdb_tool_entry_get_int( be, id, &e );
+ return e;
+}
+
+static int mdb_tool_next_id(
+ Operation *op,
+ MDB_txn *tid,
+ Entry *e,
+ struct berval *text,
+ int hole )
+{
+ struct berval dn = e->e_name;
+ struct berval ndn = e->e_nname;
+ struct berval pdn, npdn, nmatched;
+ ID id, pid = 0;
+ int rc;
+
+ if (ndn.bv_len == 0) {
+ e->e_id = 0;
+ return 0;
+ }
+
+ rc = mdb_dn2id( op, tid, &ndn, &id, NULL, &nmatched );
+ if ( rc == MDB_NOTFOUND ) {
+ if ( !be_issuffix( op->o_bd, &ndn ) ) {
+ ID eid = e->e_id;
+ dnParent( &ndn, &npdn );
+ if ( nmatched.bv_len != npdn.bv_len ) {
+ dnParent( &dn, &pdn );
+ e->e_name = pdn;
+ e->e_nname = npdn;
+ rc = mdb_tool_next_id( op, tid, e, text, 1 );
+ e->e_name = dn;
+ e->e_nname = ndn;
+ if ( rc ) {
+ return rc;
+ }
+ /* If parent didn't exist, it was created just now
+ * and its ID is now in e->e_id. Make sure the current
+ * entry gets added under the new parent ID.
+ */
+ if ( eid != e->e_id ) {
+ pid = e->e_id;
+ }
+ } else {
+ pid = id;
+ }
+ }
+ rc = mdb_next_id( op->o_bd, tid, &e->e_id );
+ if ( rc ) {
+ snprintf( text->bv_val, text->bv_len,
+ "next_id failed: %s (%d)",
+ mdb_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> mdb_tool_next_id: %s\n", text->bv_val, 0, 0 );
+ return rc;
+ }
+ rc = mdb_dn2id_add( op, tid, pid, e );
+ if ( rc ) {
+ snprintf( text->bv_val, text->bv_len,
+ "dn2id_add failed: %s (%d)",
+ mdb_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> mdb_tool_next_id: %s\n", text->bv_val, 0, 0 );
+ } else if ( hole ) {
+ if ( nholes == nhmax - 1 ) {
+ if ( holes == hbuf ) {
+ holes = ch_malloc( nhmax * sizeof(dn_id) * 2 );
+ AC_MEMCPY( holes, hbuf, sizeof(hbuf) );
+ } else {
+ holes = ch_realloc( holes, nhmax * sizeof(dn_id) * 2 );
+ }
+ nhmax *= 2;
+ }
+ ber_dupbv( &holes[nholes].dn, &ndn );
+ holes[nholes++].id = e->e_id;
+ }
+ } else if ( !hole ) {
+ unsigned i, j;
+
+ e->e_id = id;
+
+ for ( i=0; i<nholes; i++) {
+ if ( holes[i].id == e->e_id ) {
+ free(holes[i].dn.bv_val);
+ for (j=i;j<nholes;j++) holes[j] = holes[j+1];
+ holes[j].id = 0;
+ nholes--;
+ break;
+ } else if ( holes[i].id > e->e_id ) {
+ break;
+ }
+ }
+ }
+ return rc;
+}
+
+static int
+mdb_tool_index_add(
+ Operation *op,
+ MDB_txn *txn,
+ Entry *e )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+
+ if ( !mdb->mi_nattrs )
+ return 0;
+
+#if 0
+ if ( slapMode & SLAP_TOOL_QUICK ) {
+ IndexRec *ir;
+ int i, rc;
+ Attribute *a;
+
+ ir = mdb_tool_index_rec;
+ memset(ir, 0, mdb->bi_nattrs * sizeof( IndexRec ));
+
+ for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
+ rc = mdb_index_recset( mdb, a, a->a_desc->ad_type,
+ &a->a_desc->ad_tags, ir );
+ if ( rc )
+ return rc;
+ }
+ mdb_tool_ix_id = e->e_id;
+ mdb_tool_ix_op = op;
+ ldap_pvt_thread_mutex_lock( &mdb_tool_index_mutex );
+ /* Wait for all threads to be ready */
+ while ( mdb_tool_index_tcount ) {
+ ldap_pvt_thread_cond_wait( &mdb_tool_index_cond_main,
+ &mdb_tool_index_mutex );
+ }
+ for ( i=1; i<slap_tool_thread_max; i++ )
+ mdb_tool_index_threads[i] = LDAP_BUSY;
+ mdb_tool_index_tcount = slap_tool_thread_max - 1;
+ ldap_pvt_thread_cond_broadcast( &mdb_tool_index_cond_work );
+ ldap_pvt_thread_mutex_unlock( &mdb_tool_index_mutex );
+ rc = mdb_index_recrun( op, mdb, ir, e->e_id, 0 );
+ if ( rc )
+ return rc;
+ ldap_pvt_thread_mutex_lock( &mdb_tool_index_mutex );
+ for ( i=1; i<slap_tool_thread_max; i++ ) {
+ if ( mdb_tool_index_threads[i] == LDAP_BUSY ) {
+ ldap_pvt_thread_cond_wait( &mdb_tool_index_cond_main,
+ &mdb_tool_index_mutex );
+ i--;
+ continue;
+ }
+ if ( mdb_tool_index_threads[i] ) {
+ rc = mdb_tool_index_threads[i];
+ break;
+ }
+ }
+ ldap_pvt_thread_mutex_unlock( &mdb_tool_index_mutex );
+ return rc;
+ } else
+#endif
+ {
+ return mdb_index_entry_add( op, txn, e );
+ }
+}
+
+ID mdb_tool_entry_put(
+ BackendDB *be,
+ Entry *e,
+ struct berval *text )
+{
+ int rc;
+ struct mdb_info *mdb;
+ Operation op = {0};
+ Opheader ohdr = {0};
+
+ assert( be != NULL );
+ assert( slapMode & SLAP_TOOL_MODE );
+
+ assert( text != NULL );
+ assert( text->bv_val != NULL );
+ assert( text->bv_val[0] == '\0' ); /* overconservative? */
+
+ Debug( LDAP_DEBUG_TRACE, "=> " LDAP_XSTRING(mdb_tool_entry_put)
+ "( %ld, \"%s\" )\n", (long) e->e_id, e->e_dn, 0 );
+
+ mdb = (struct mdb_info *) be->be_private;
+
+ if ( !txn ) {
+ rc = mdb_txn_begin( mdb->mi_dbenv, 0, &txn );
+ if( rc != 0 ) {
+ snprintf( text->bv_val, text->bv_len,
+ "txn_begin failed: %s (%d)",
+ mdb_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(mdb_tool_entry_put) ": %s\n",
+ text->bv_val, 0, 0 );
+ return NOID;
+ }
+ }
+
+ op.o_hdr = &ohdr;
+ op.o_bd = be;
+ op.o_tmpmemctx = NULL;
+ op.o_tmpmfuncs = &ch_mfuncs;
+
+ /* add dn2id indices */
+ rc = mdb_tool_next_id( &op, txn, e, text, 0 );
+ if( rc != 0 ) {
+ goto done;
+ }
+
+ rc = mdb_tool_index_add( &op, txn, e );
+ if( rc != 0 ) {
+ snprintf( text->bv_val, text->bv_len,
+ "index_entry_add failed: %s (%d)",
+ rc == LDAP_OTHER ? "Internal error" :
+ mdb_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(mdb_tool_entry_put) ": %s\n",
+ text->bv_val, 0, 0 );
+ goto done;
+ }
+
+
+ /* id2entry index */
+ rc = mdb_id2entry_add( &op, txn, e );
+ if( rc != 0 ) {
+ snprintf( text->bv_val, text->bv_len,
+ "id2entry_add failed: %s (%d)",
+ mdb_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(mdb_tool_entry_put) ": %s\n",
+ text->bv_val, 0, 0 );
+ goto done;
+ }
+
+done:
+ if( rc == 0 ) {
+ mdb_writes++;
+ if ( mdb_writes >= mdb_writes_per_commit ) {
+ rc = mdb_txn_commit( txn );
+ mdb_writes = 0;
+ txn = NULL;
+ if( rc != 0 ) {
+ snprintf( text->bv_val, text->bv_len,
+ "txn_commit failed: %s (%d)",
+ mdb_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(mdb_tool_entry_put) ": %s\n",
+ text->bv_val, 0, 0 );
+ e->e_id = NOID;
+ }
+ }
+
+ } else {
+ mdb_txn_abort( txn );
+ txn = NULL;
+ snprintf( text->bv_val, text->bv_len,
+ "txn_aborted! %s (%d)",
+ rc == LDAP_OTHER ? "Internal error" :
+ mdb_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(mdb_tool_entry_put) ": %s\n",
+ text->bv_val, 0, 0 );
+ e->e_id = NOID;
+ }
+
+ return e->e_id;
+}
+
+int mdb_tool_entry_reindex(
+ BackendDB *be,
+ ID id,
+ AttributeDescription **adv )
+{
+ struct mdb_info *mi = (struct mdb_info *) be->be_private;
+ int rc;
+ Entry *e;
+ Operation op = {0};
+ Opheader ohdr = {0};
+
+ Debug( LDAP_DEBUG_ARGS,
+ "=> " LDAP_XSTRING(mdb_tool_entry_reindex) "( %ld )\n",
+ (long) id, 0, 0 );
+ assert( tool_base == NULL );
+ assert( tool_filter == NULL );
+
+ /* No indexes configured, nothing to do. Could return an
+ * error here to shortcut things.
+ */
+ if (!mi->mi_attrs) {
+ return 0;
+ }
+
+ /* Check for explicit list of attrs to index */
+ if ( adv ) {
+ int i, j, n;
+
+ if ( mi->mi_attrs[0]->ai_desc != adv[0] ) {
+ /* count */
+ for ( n = 0; adv[n]; n++ ) ;
+
+ /* insertion sort */
+ for ( i = 0; i < n; i++ ) {
+ AttributeDescription *ad = adv[i];
+ for ( j = i-1; j>=0; j--) {
+ if ( SLAP_PTRCMP( adv[j], ad ) <= 0 ) break;
+ adv[j+1] = adv[j];
+ }
+ adv[j+1] = ad;
+ }
+ }
+
+ for ( i = 0; adv[i]; i++ ) {
+ if ( mi->mi_attrs[i]->ai_desc != adv[i] ) {
+ for ( j = i+1; j < mi->mi_nattrs; j++ ) {
+ if ( mi->mi_attrs[j]->ai_desc == adv[i] ) {
+ AttrInfo *ai = mi->mi_attrs[i];
+ mi->mi_attrs[i] = mi->mi_attrs[j];
+ mi->mi_attrs[j] = ai;
+ break;
+ }
+ }
+ if ( j == mi->mi_nattrs ) {
+ Debug( LDAP_DEBUG_ANY,
+ LDAP_XSTRING(mdb_tool_entry_reindex)
+ ": no index configured for %s\n",
+ adv[i]->ad_cname.bv_val, 0, 0 );
+ return -1;
+ }
+ }
+ }
+ mi->mi_nattrs = i;
+ }
+
+ e = mdb_tool_entry_get( be, id );
+
+ if( e == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ LDAP_XSTRING(mdb_tool_entry_reindex)
+ ": could not locate id=%ld\n",
+ (long) id, 0, 0 );
+ return -1;
+ }
+
+ if ( !txi ) {
+ rc = mdb_txn_begin( mi->mi_dbenv, 0, &txi );
+ if( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(mdb_tool_entry_reindex) ": "
+ "txn_begin failed: %s (%d)\n",
+ mdb_strerror(rc), rc, 0 );
+ goto done;
+ }
+ }
+
+ /*
+ * just (re)add them for now
+ * assume that some other routine (not yet implemented)
+ * will zap index databases
+ *
+ */
+
+ Debug( LDAP_DEBUG_TRACE,
+ "=> " LDAP_XSTRING(mdb_tool_entry_reindex) "( %ld, \"%s\" )\n",
+ (long) id, e->e_dn, 0 );
+
+ op.o_hdr = &ohdr;
+ op.o_bd = be;
+ op.o_tmpmemctx = NULL;
+ op.o_tmpmfuncs = &ch_mfuncs;
+
+ rc = mdb_tool_index_add( &op, txi, e );
+
+done:
+ if( rc == 0 ) {
+ mdb_writes++;
+ if ( mdb_writes >= mdb_writes_per_commit ) {
+ rc = mdb_txn_commit( txi );
+ if( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(mdb_tool_entry_reindex)
+ ": txn_commit failed: %s (%d)\n",
+ mdb_strerror(rc), rc, 0 );
+ e->e_id = NOID;
+ }
+ txi = NULL;
+ }
+
+ } else {
+ mdb_txn_abort( txi );
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(mdb_tool_entry_reindex)
+ ": txn_aborted! %s (%d)\n",
+ mdb_strerror(rc), rc, 0 );
+ e->e_id = NOID;
+ txi = NULL;
+ }
+ mdb_entry_release( &op, e, 0 );
+
+ return rc;
+}
+
+ID mdb_tool_entry_modify(
+ BackendDB *be,
+ Entry *e,
+ struct berval *text )
+{
+ int rc;
+ struct mdb_info *mdb;
+ MDB_txn *tid;
+ Operation op = {0};
+ Opheader ohdr = {0};
+
+ assert( be != NULL );
+ assert( slapMode & SLAP_TOOL_MODE );
+
+ assert( text != NULL );
+ assert( text->bv_val != NULL );
+ assert( text->bv_val[0] == '\0' ); /* overconservative? */
+
+ assert ( e->e_id != NOID );
+
+ Debug( LDAP_DEBUG_TRACE,
+ "=> " LDAP_XSTRING(mdb_tool_entry_modify) "( %ld, \"%s\" )\n",
+ (long) e->e_id, e->e_dn, 0 );
+
+ mdb = (struct mdb_info *) be->be_private;
+
+ if( cursor ) {
+ mdb_cursor_close( cursor );
+ cursor = NULL;
+ }
+ rc = mdb_txn_begin( mdb->mi_dbenv, 0, &tid );
+ if( rc != 0 ) {
+ snprintf( text->bv_val, text->bv_len,
+ "txn_begin failed: %s (%d)",
+ mdb_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(mdb_tool_entry_modify) ": %s\n",
+ text->bv_val, 0, 0 );
+ return NOID;
+ }
+
+ op.o_hdr = &ohdr;
+ op.o_bd = be;
+ op.o_tmpmemctx = NULL;
+ op.o_tmpmfuncs = &ch_mfuncs;
+
+ /* id2entry index */
+ rc = mdb_id2entry_update( &op, tid, e );
+ if( rc != 0 ) {
+ snprintf( text->bv_val, text->bv_len,
+ "id2entry_add failed: %s (%d)",
+ mdb_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(mdb_tool_entry_modify) ": %s\n",
+ text->bv_val, 0, 0 );
+ goto done;
+ }
+
+done:
+ if( rc == 0 ) {
+ rc = mdb_txn_commit( tid );
+ if( rc != 0 ) {
+ snprintf( text->bv_val, text->bv_len,
+ "txn_commit failed: %s (%d)",
+ mdb_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(mdb_tool_entry_modify) ": "
+ "%s\n", text->bv_val, 0, 0 );
+ e->e_id = NOID;
+ }
+
+ } else {
+ mdb_txn_abort( tid );
+ snprintf( text->bv_val, text->bv_len,
+ "txn_aborted! %s (%d)",
+ mdb_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(mdb_tool_entry_modify) ": %s\n",
+ text->bv_val, 0, 0 );
+ e->e_id = NOID;
+ }
+
+ return e->e_id;
+}
+
+#if 0
+static void *
+mdb_tool_index_task( void *ctx, void *ptr )
+{
+ int base = *(int *)ptr;
+
+ free( ptr );
+ while ( 1 ) {
+ ldap_pvt_thread_mutex_lock( &mdb_tool_index_mutex );
+ mdb_tool_index_tcount--;
+ if ( !mdb_tool_index_tcount )
+ ldap_pvt_thread_cond_signal( &mdb_tool_index_cond_main );
+ ldap_pvt_thread_cond_wait( &mdb_tool_index_cond_work,
+ &mdb_tool_index_mutex );
+ if ( slapd_shutdown ) {
+ mdb_tool_index_tcount--;
+ if ( !mdb_tool_index_tcount )
+ ldap_pvt_thread_cond_signal( &mdb_tool_index_cond_main );
+ ldap_pvt_thread_mutex_unlock( &mdb_tool_index_mutex );
+ break;
+ }
+ ldap_pvt_thread_mutex_unlock( &mdb_tool_index_mutex );
+
+ mdb_tool_index_threads[base] = mdb_index_recrun( mdb_tool_ix_op,
+ mdb_tool_info, mdb_tool_index_rec, mdb_tool_ix_id, base );
+ }
+
+ return NULL;
+}
+#endif