From 0ba4206ed93215f2c5c2d12d16342d6a846c278a Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Thu, 1 Sep 2011 16:52:53 -0700 Subject: [PATCH] Import back-mdb --- doc/man/man5/slapd-mdb.5 | 145 ++++ servers/slapd/back-mdb/Makefile.in | 61 ++ servers/slapd/back-mdb/add.c | 438 ++++++++++ servers/slapd/back-mdb/attr.c | 522 ++++++++++++ servers/slapd/back-mdb/back-mdb.h | 162 ++++ servers/slapd/back-mdb/bind.c | 156 ++++ servers/slapd/back-mdb/compare.c | 140 ++++ servers/slapd/back-mdb/config.c | 588 +++++++++++++ servers/slapd/back-mdb/delete.c | 470 +++++++++++ servers/slapd/back-mdb/dn2entry.c | 64 ++ servers/slapd/back-mdb/dn2id.c | 1083 ++++++++++++++++++++++++ servers/slapd/back-mdb/extended.c | 54 ++ servers/slapd/back-mdb/filterindex.c | 1144 +++++++++++++++++++++++++ servers/slapd/back-mdb/id2entry.c | 425 ++++++++++ servers/slapd/back-mdb/idl.c | 1166 ++++++++++++++++++++++++++ servers/slapd/back-mdb/idl.h | 74 ++ servers/slapd/back-mdb/index.c | 555 ++++++++++++ servers/slapd/back-mdb/init.c | 445 ++++++++++ servers/slapd/back-mdb/key.c | 94 +++ servers/slapd/back-mdb/modify.c | 693 +++++++++++++++ servers/slapd/back-mdb/modrdn.c | 655 +++++++++++++++ servers/slapd/back-mdb/monitor.c | 656 +++++++++++++++ servers/slapd/back-mdb/nextid.c | 59 ++ servers/slapd/back-mdb/operational.c | 119 +++ servers/slapd/back-mdb/proto-mdb.h | 376 +++++++++ servers/slapd/back-mdb/referral.c | 149 ++++ servers/slapd/back-mdb/search.c | 1112 ++++++++++++++++++++++++ servers/slapd/back-mdb/tools.c | 875 +++++++++++++++++++ 28 files changed, 12480 insertions(+) create mode 100644 doc/man/man5/slapd-mdb.5 create mode 100644 servers/slapd/back-mdb/Makefile.in create mode 100644 servers/slapd/back-mdb/add.c create mode 100644 servers/slapd/back-mdb/attr.c create mode 100644 servers/slapd/back-mdb/back-mdb.h create mode 100644 servers/slapd/back-mdb/bind.c create mode 100644 servers/slapd/back-mdb/compare.c create mode 100644 servers/slapd/back-mdb/config.c create mode 100644 servers/slapd/back-mdb/delete.c create mode 100644 servers/slapd/back-mdb/dn2entry.c create mode 100644 servers/slapd/back-mdb/dn2id.c create mode 100644 servers/slapd/back-mdb/extended.c create mode 100644 servers/slapd/back-mdb/filterindex.c create mode 100644 servers/slapd/back-mdb/id2entry.c create mode 100644 servers/slapd/back-mdb/idl.c create mode 100644 servers/slapd/back-mdb/idl.h create mode 100644 servers/slapd/back-mdb/index.c create mode 100644 servers/slapd/back-mdb/init.c create mode 100644 servers/slapd/back-mdb/key.c create mode 100644 servers/slapd/back-mdb/modify.c create mode 100644 servers/slapd/back-mdb/modrdn.c create mode 100644 servers/slapd/back-mdb/monitor.c create mode 100644 servers/slapd/back-mdb/nextid.c create mode 100644 servers/slapd/back-mdb/operational.c create mode 100644 servers/slapd/back-mdb/proto-mdb.h create mode 100644 servers/slapd/back-mdb/referral.c create mode 100644 servers/slapd/back-mdb/search.c create mode 100644 servers/slapd/back-mdb/tools.c diff --git a/doc/man/man5/slapd-mdb.5 b/doc/man/man5/slapd-mdb.5 new file mode 100644 index 0000000000..9c723d38de --- /dev/null +++ b/doc/man/man5/slapd-mdb.5 @@ -0,0 +1,145 @@ +.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 \ \ +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\fP data has been written or +\fI\fP minutes have passed since the last checkpoint. +Both arguments default to zero, in which case they are ignored. When +the \fI\fP argument is non-zero, an internal task will run every +\fI\fP minutes to perform the checkpoint. +Note: currently the \fI\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 \ +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\fR|\fBdefault\fR} [\fBpres\fR,\fBeq\fR,\fBapprox\fR,\fBsub\fR,\fI\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\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 \ +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 \ +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 \ +Specify the file protection mode that newly created database +files should have. +The default is 0600. +.TP +.BI searchstack \ +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. diff --git a/servers/slapd/back-mdb/Makefile.in b/servers/slapd/back-mdb/Makefile.in new file mode 100644 index 0000000000..93cb911f4b --- /dev/null +++ b/servers/slapd/back-mdb/Makefile.in @@ -0,0 +1,61 @@ +# Makefile.in for back-mdb +# $OpenLDAP$ +## This work is part of OpenLDAP Software . +## +## 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 +## . + +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 diff --git a/servers/slapd/back-mdb/add.c b/servers/slapd/back-mdb/add.c new file mode 100644 index 0000000000..7822b9fcf0 --- /dev/null +++ b/servers/slapd/back-mdb/add.c @@ -0,0 +1,438 @@ +/* add.c - ldap mdb back-end add routine */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ + +#include "portable.h" + +#include +#include + +#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; +} diff --git a/servers/slapd/back-mdb/attr.c b/servers/slapd/back-mdb/attr.c new file mode 100644 index 0000000000..8207d50a2f --- /dev/null +++ b/servers/slapd/back-mdb/attr.c @@ -0,0 +1,522 @@ +/* attr.c - backend routines for dealing with attributes */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ + +#include "portable.h" + +#include + +#include +#include + +#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; imi_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; imi_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; imi_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; imi_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 (; imi_nattrs; i++) + mdb->mi_attrs[i] = mdb->mi_attrs[i+1]; + } +} + +void mdb_attr_flush( struct mdb_info *mdb ) +{ + int i; + + for ( i=0; imi_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; jmi_nattrs; j++) + mdb->mi_attrs[j] = mdb->mi_attrs[j+1]; + i--; + } + } +} diff --git a/servers/slapd/back-mdb/back-mdb.h b/servers/slapd/back-mdb/back-mdb.h new file mode 100644 index 0000000000..a9c8de85f7 --- /dev/null +++ b/servers/slapd/back-mdb/back-mdb.h @@ -0,0 +1,162 @@ +/* back-mdb.h - mdb back-end header file */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ + +#ifndef _BACK_MDB_H_ +#define _BACK_MDB_H_ + +#include +#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. + * + * 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 + * . + */ + +#include "portable.h" + +#include +#include +#include + +#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; +} diff --git a/servers/slapd/back-mdb/compare.c b/servers/slapd/back-mdb/compare.c new file mode 100644 index 0000000000..9bb05dc53c --- /dev/null +++ b/servers/slapd/back-mdb/compare.c @@ -0,0 +1,140 @@ +/* compare.c - mdb backend compare routine */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ + +#include "portable.h" + +#include +#include + +#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; +} diff --git a/servers/slapd/back-mdb/config.c b/servers/slapd/back-mdb/config.c new file mode 100644 index 0000000000..c6cb3cb043 --- /dev/null +++ b/servers/slapd/back-mdb/config.c @@ -0,0 +1,588 @@ +/* config.c - mdb backend configuration file routine */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ + +#include "portable.h" + +#include +#include +#include +#include + +#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> <[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; +} diff --git a/servers/slapd/back-mdb/delete.c b/servers/slapd/back-mdb/delete.c new file mode 100644 index 0000000000..01c31f183f --- /dev/null +++ b/servers/slapd/back-mdb/delete.c @@ -0,0 +1,470 @@ +/* delete.c - mdb backend delete routine */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ + +#include "portable.h" + +#include +#include + +#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; +} diff --git a/servers/slapd/back-mdb/dn2entry.c b/servers/slapd/back-mdb/dn2entry.c new file mode 100644 index 0000000000..3aa8167534 --- /dev/null +++ b/servers/slapd/back-mdb/dn2entry.c @@ -0,0 +1,64 @@ +/* dn2entry.c - routines to deal with the dn2id / id2entry glue */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ + +#include "portable.h" + +#include +#include + +#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; +} diff --git a/servers/slapd/back-mdb/dn2id.c b/servers/slapd/back-mdb/dn2id.c new file mode 100644 index 0000000000..c0ae1781e2 --- /dev/null +++ b/servers/slapd/back-mdb/dn2id.c @@ -0,0 +1,1083 @@ +/* dn2id.c - routines to deal with the dn2id index */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ + +#include "portable.h" + +#include +#include + +#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 diff --git a/servers/slapd/back-mdb/extended.c b/servers/slapd/back-mdb/extended.c new file mode 100644 index 0000000000..57a057139c --- /dev/null +++ b/servers/slapd/back-mdb/extended.c @@ -0,0 +1,54 @@ +/* extended.c - mdb backend extended routines */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ + +#include "portable.h" + +#include +#include + +#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; +} + diff --git a/servers/slapd/back-mdb/filterindex.c b/servers/slapd/back-mdb/filterindex.c new file mode 100644 index 0000000000..3e3a87b7ef --- /dev/null +++ b/servers/slapd/back-mdb/filterindex.c @@ -0,0 +1,1144 @@ +/* filterindex.c - generate the list of candidate entries from a filter */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ + +#include "portable.h" + +#include +#include + +#include "back-mdb.h" +#include "idl.h" +#ifdef LDAP_COMP_MATCH +#include +#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 ); +} diff --git a/servers/slapd/back-mdb/id2entry.c b/servers/slapd/back-mdb/id2entry.c new file mode 100644 index 0000000000..511fda9e85 --- /dev/null +++ b/servers/slapd/back-mdb/id2entry.c @@ -0,0 +1,425 @@ +/* id2entry.c - routines to deal with the id2entry database */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ + +#include "portable.h" + +#include +#include +#include + +#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; +} diff --git a/servers/slapd/back-mdb/idl.c b/servers/slapd/back-mdb/idl.c new file mode 100644 index 0000000000..cc181235a2 --- /dev/null +++ b/servers/slapd/back-mdb/idl.c @@ -0,0 +1,1166 @@ +/* idl.c - ldap id list handling routines */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ + +#include "portable.h" + +#include +#include + +#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 */ diff --git a/servers/slapd/back-mdb/idl.h b/servers/slapd/back-mdb/idl.h new file mode 100644 index 0000000000..21de4d8aa2 --- /dev/null +++ b/servers/slapd/back-mdb/idl.h @@ -0,0 +1,74 @@ +/* idl.h - ldap mdb back-end ID list header file */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ + +#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<. + * + * 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 + * . + */ + +#include "portable.h" + +#include + +#include +#include + +#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; imi_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; +} diff --git a/servers/slapd/back-mdb/init.c b/servers/slapd/back-mdb/init.c new file mode 100644 index 0000000000..4e174e22f6 --- /dev/null +++ b/servers/slapd/back-mdb/init.c @@ -0,0 +1,445 @@ +/* init.c - initialize mdb backend */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ + +#include "portable.h" + +#include +#include +#include +#include +#include +#include +#include "back-mdb.h" +#include +#include +#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; imi_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 */ + diff --git a/servers/slapd/back-mdb/key.c b/servers/slapd/back-mdb/key.c new file mode 100644 index 0000000000..e33392008c --- /dev/null +++ b/servers/slapd/back-mdb/key.c @@ -0,0 +1,94 @@ +/* index.c - routines for dealing with attribute indexes */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ + +#include "portable.h" + +#include + +#include +#include + +#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; +} diff --git a/servers/slapd/back-mdb/modify.c b/servers/slapd/back-mdb/modify.c new file mode 100644 index 0000000000..c577ea9173 --- /dev/null +++ b/servers/slapd/back-mdb/modify.c @@ -0,0 +1,693 @@ +/* modify.c - mdb backend modify routine */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ + +#include "portable.h" + +#include +#include +#include + +#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; +} diff --git a/servers/slapd/back-mdb/modrdn.c b/servers/slapd/back-mdb/modrdn.c new file mode 100644 index 0000000000..b628c259b5 --- /dev/null +++ b/servers/slapd/back-mdb/modrdn.c @@ -0,0 +1,655 @@ +/* modrdn.c - mdb backend modrdn routine */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ + +#include "portable.h" + +#include +#include + +#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; +} diff --git a/servers/slapd/back-mdb/monitor.c b/servers/slapd/back-mdb/monitor.c new file mode 100644 index 0000000000..728bd01447 --- /dev/null +++ b/servers/slapd/back-mdb/monitor.c @@ -0,0 +1,656 @@ +/* monitor.c - monitor mdb backend */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ + +#include "portable.h" + +#include +#include +#include +#include +#include +#include +#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 */ diff --git a/servers/slapd/back-mdb/nextid.c b/servers/slapd/back-mdb/nextid.c new file mode 100644 index 0000000000..1d7b3893cb --- /dev/null +++ b/servers/slapd/back-mdb/nextid.c @@ -0,0 +1,59 @@ +/* init.c - initialize mdb backend */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ + +#include "portable.h" + +#include +#include + +#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; +} diff --git a/servers/slapd/back-mdb/operational.c b/servers/slapd/back-mdb/operational.c new file mode 100644 index 0000000000..28f6e7b4aa --- /dev/null +++ b/servers/slapd/back-mdb/operational.c @@ -0,0 +1,119 @@ +/* operational.c - mdb backend operational attributes function */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ + +#include "portable.h" + +#include + +#include +#include + +#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; +} + diff --git a/servers/slapd/back-mdb/proto-mdb.h b/servers/slapd/back-mdb/proto-mdb.h new file mode 100644 index 0000000000..2c05d6b370 --- /dev/null +++ b/servers/slapd/back-mdb/proto-mdb.h @@ -0,0 +1,376 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ + +#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 */ diff --git a/servers/slapd/back-mdb/referral.c b/servers/slapd/back-mdb/referral.c new file mode 100644 index 0000000000..be74c48492 --- /dev/null +++ b/servers/slapd/back-mdb/referral.c @@ -0,0 +1,149 @@ +/* referral.c - MDB backend referral handler */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ + +#include "portable.h" +#include +#include + +#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; +} diff --git a/servers/slapd/back-mdb/search.c b/servers/slapd/back-mdb/search.c new file mode 100644 index 0000000000..2009f006a2 --- /dev/null +++ b/servers/slapd/back-mdb/search.c @@ -0,0 +1,1112 @@ +/* search.c - search operation */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ + +#include "portable.h" + +#include +#include + +#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 ); +} diff --git a/servers/slapd/back-mdb/tools.c b/servers/slapd/back-mdb/tools.c new file mode 100644 index 0000000000..1e95a48d53 --- /dev/null +++ b/servers/slapd/back-mdb/tools.c @@ -0,0 +1,875 @@ +/* tools.c - tools for slap tools */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ + +#include "portable.h" + +#include +#include +#include + +#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; ibe_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; ie_id ) { + free(holes[i].dn.bv_val); + for (j=i;j 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; ie_id, 0 ); + if ( rc ) + return rc; + ldap_pvt_thread_mutex_lock( &mdb_tool_index_mutex ); + for ( i=1; ibv_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 -- 2.39.5