summaryrefslogtreecommitdiffstats
path: root/servers/slapd/back-wt
diff options
context:
space:
mode:
Diffstat (limited to 'servers/slapd/back-wt')
-rw-r--r--servers/slapd/back-wt/Makefile.in54
-rw-r--r--servers/slapd/back-wt/add.c373
-rw-r--r--servers/slapd/back-wt/attr.c425
-rw-r--r--servers/slapd/back-wt/back-wt.h120
-rw-r--r--servers/slapd/back-wt/bind.c150
-rw-r--r--servers/slapd/back-wt/cache.c231
-rw-r--r--servers/slapd/back-wt/compare.c154
-rw-r--r--servers/slapd/back-wt/config.c209
-rw-r--r--servers/slapd/back-wt/ctx.c117
-rw-r--r--servers/slapd/back-wt/delete.c419
-rw-r--r--servers/slapd/back-wt/dn2entry.c176
-rw-r--r--servers/slapd/back-wt/dn2id.c453
-rw-r--r--servers/slapd/back-wt/extended.c58
-rw-r--r--servers/slapd/back-wt/filterindex.c718
-rw-r--r--servers/slapd/back-wt/id2entry.c352
-rw-r--r--servers/slapd/back-wt/idl.c789
-rw-r--r--servers/slapd/back-wt/idl.h80
-rw-r--r--servers/slapd/back-wt/index.c423
-rw-r--r--servers/slapd/back-wt/init.c385
-rw-r--r--servers/slapd/back-wt/key.c162
-rw-r--r--servers/slapd/back-wt/modify.c714
-rw-r--r--servers/slapd/back-wt/modrdn.c552
-rw-r--r--servers/slapd/back-wt/nextid.c88
-rw-r--r--servers/slapd/back-wt/operational.c110
-rw-r--r--servers/slapd/back-wt/proto-wt.h268
-rw-r--r--servers/slapd/back-wt/search.c759
-rw-r--r--servers/slapd/back-wt/tools.c721
27 files changed, 9060 insertions, 0 deletions
diff --git a/servers/slapd/back-wt/Makefile.in b/servers/slapd/back-wt/Makefile.in
new file mode 100644
index 0000000..054025e
--- /dev/null
+++ b/servers/slapd/back-wt/Makefile.in
@@ -0,0 +1,54 @@
+# Makefile.in for back-wt
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+SRCS = init.c tools.c config.c \
+ add.c bind.c compare.c delete.c modify.c modrdn.c search.c \
+ extended.c operational.c \
+ attr.c index.c key.c filterindex.c \
+ dn2entry.c dn2id.c id2entry.c idl.c \
+ nextid.c ctx.c cache.c
+
+OBJS = init.lo tools.lo config.lo \
+ add.lo bind.lo compare.lo delete.lo modify.lo modrdn.lo search.lo \
+ extended.lo operational.lo \
+ attr.lo index.lo key.lo filterindex.lo \
+ dn2entry.lo dn2id.lo id2entry.lo idl.lo \
+ nextid.lo ctx.lo cache.lo
+
+LDAP_INCDIR= ../../../include
+LDAP_LIBDIR= ../../../libraries
+
+BUILD_OPT = "--enable-wt"
+BUILD_MOD = @BUILD_WT@
+
+mod_DEFS = -DSLAPD_IMPORT
+MOD_DEFS = @WT_CFLAGS@
+MOD_LIBS = @WT_LIBS@
+
+
+shared_LDAP_LIBS = $(LDAP_LIBLDAP_LA) $(LDAP_LIBLBER_LA)
+NT_LINK_LIBS = -L.. -lslapd $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS)
+UNIX_LINK_LIBS = $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS)
+
+LIBBASE = back_wt
+
+XINCPATH = -I.. -I$(srcdir)/..
+XDEFS = $(MODULES_CPPFLAGS)
+
+all-local-lib: ../.backend
+
+../.backend: lib$(LIBBASE).a
+ @touch $@
+
diff --git a/servers/slapd/back-wt/add.c b/servers/slapd/back-wt/add.c
new file mode 100644
index 0000000..04c08a1
--- /dev/null
+++ b/servers/slapd/back-wt/add.c
@@ -0,0 +1,373 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include "back-wt.h"
+#include "slap-config.h"
+
+int
+wt_add( Operation *op, SlapReply *rs )
+{
+ struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+ struct berval pdn;
+ char textbuf[SLAP_TEXT_BUFLEN];
+ size_t textlen = sizeof textbuf;
+ AttributeDescription *children = slap_schema.si_ad_children;
+ AttributeDescription *entry = slap_schema.si_ad_entry;
+ ID eid = NOID;
+ LDAPControl **postread_ctrl = NULL;
+ LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
+ int num_ctrls = 0;
+ wt_ctx *wc;
+ Entry *e = NULL;
+ Entry *p = NULL;
+ ID pid = NOID;
+ int rc;
+
+ Debug( LDAP_DEBUG_ARGS, "==> wt_add: %s\n", op->ora_e->e_name.bv_val );
+
+ 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,
+ "wt_add: entry failed schema check: %s (%d)\n",
+ rs->sr_text, rs->sr_err );
+ 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,
+ "wt_add: entry failed op attrs add: %s (%d)\n",
+ rs->sr_text, rs->sr_err );
+ 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;
+ }
+
+ /* Not used
+ * subentry = is_entry_subentry( op->ora_e );
+ */
+
+ /*
+ * 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 );
+ }
+
+ wc = wt_ctx_get(op, wi);
+ if( !wc ){
+ Debug( LDAP_DEBUG_ANY, "wt_add: wt_ctx_get failed\n" );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ send_ldap_result( op, rs );
+ return rs->sr_err;
+ }
+
+ rc = wt_dn2entry(op->o_bd, wc, &op->o_req_ndn, &e);
+ switch( rc ) {
+ case 0:
+ rs->sr_err = LDAP_ALREADY_EXISTS;
+ goto return_results;
+ break;
+ case WT_NOTFOUND:
+ break;
+ default:
+ /* TODO: retry handling */
+ Debug( LDAP_DEBUG_ANY,
+ "wt_add: error at wt_dn2entry() rc=%d\n", rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ /* get parent entry */
+ rc = wt_dn2pentry(op->o_bd, wc, &op->o_req_ndn, &p);
+ switch( rc ){
+ case 0:
+ case WT_NOTFOUND:
+ break;
+ default:
+ Debug( LDAP_DEBUG_ANY,
+ "wt_add: error at wt_dn2pentry() rc=%d\n", rc );
+ 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 ) {
+ rs->sr_ref = is_entry_referral( p )
+ ? get_entry_referrals( op, p )
+ : NULL;
+ wt_entry_return( p );
+ } else {
+ rs->sr_ref = NULL;
+ }
+ p = NULL;
+ Debug( LDAP_DEBUG_TRACE, "wt_add: parent does not exist\n" );
+ 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 )
+ wt_entry_return( op, p );
+ */
+ p = NULL;
+
+ Debug( LDAP_DEBUG_TRACE, "wt_add: no write access to parent\n" );
+ 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 ) ) {
+ wt_entry_return( p );
+ p = NULL;
+ /* parent is a subentry, don't allow add */
+ Debug( LDAP_DEBUG_TRACE, "wt_add: parent is subentry\n" );
+ rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
+ rs->sr_text = "parent is a subentry";
+ goto return_results;;
+ }
+
+ if ( is_entry_alias( p ) ) {
+ wt_entry_return( p );
+ p = NULL;
+ /* parent is an alias, don't allow add */
+ Debug( LDAP_DEBUG_TRACE, "wt_add: parent is alias\n" );
+ 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 );
+ wt_entry_return( p );
+ p = NULL;
+ Debug( LDAP_DEBUG_TRACE, "wt_add: parent is referral\n" );
+
+ rs->sr_err = LDAP_REFERRAL;
+ rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
+ goto return_results;
+ }
+ }
+
+#if 0
+ if ( subentry ) {
+ /* FIXME: */
+ /* parent must be an administrative point of the required kind */
+ }
+#endif
+
+ /* 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 ... */
+ }
+ }
+
+ wt_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, "wt_add: no write access to entry\n" );
+ 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, op->ora_e, op->ora_modlist)) {
+ Debug( LDAP_DEBUG_TRACE, "wt_add: no write access to attribute\n" );
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ rs->sr_text = "no write access to attribute";
+ goto return_results;
+ }
+
+ rc = wc->session->begin_transaction(wc->session, "isolation=read-uncommitted");
+ if( rc ) {
+ Debug( LDAP_DEBUG_TRACE, "wt_add: begin_transaction failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "begin_transaction failed";
+ goto return_results;
+ }
+ Debug( LDAP_DEBUG_TRACE, "wt_add: session id: %p\n", wc->session );
+
+ wt_next_id( op->o_bd, &eid );
+ op->ora_e->e_id = eid;
+
+ rc = wt_dn2id_add( op, wc, pid, op->ora_e );
+ if( rc ){
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_add: dn2id_add failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ switch( rc ) {
+ case WT_DUPLICATE_KEY:
+ rs->sr_err = LDAP_ALREADY_EXISTS;
+ break;
+ default:
+ rs->sr_err = LDAP_OTHER;
+ }
+ wc->session->rollback_transaction(wc->session, NULL);
+ goto return_results;
+ }
+
+ rc = wt_id2entry_add( op, wc, op->ora_e );
+ if ( rc ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_add: id2entry_add failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ if ( rc == LDAP_ADMINLIMIT_EXCEEDED ) {
+ rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
+ rs->sr_text = "entry is too big";
+ } else {
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "entry store failed";
+ }
+ wc->session->rollback_transaction(wc->session, NULL);
+ goto return_results;
+ }
+
+ /* add indices */
+ rc = wt_index_entry_add( op, wc, op->ora_e );
+ if ( rc ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "<== wt_add: index add failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "index add failed";
+ wc->session->rollback_transaction(wc->session, NULL);
+ goto return_results;
+ }
+
+ rc = wc->session->commit_transaction(wc->session, NULL);
+ if( rc ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<== wt_add: commit_transaction failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "commit_transaction failed";
+ goto return_results;
+ }
+
+ rs->sr_err = LDAP_SUCCESS;
+
+ /* 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, "<=- wt_add: post-read failed!\n" );
+ if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
+ /* FIXME: is it correct to abort
+ * operation if control fails? */
+ goto return_results;
+ }
+ }
+ }
+
+ Debug(LDAP_DEBUG_TRACE,
+ "wt_add: added%s id=%08lx dn=\"%s\"\n",
+ op->o_noop ? " (no-op)" : "",
+ op->ora_e->e_id, op->ora_e->e_dn );
+
+return_results:
+ send_ldap_result( op, rs );
+
+ 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;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/attr.c b/servers/slapd/back-wt/attr.c
new file mode 100644
index 0000000..70f278c
--- /dev/null
+++ b/servers/slapd/back-wt/attr.c
@@ -0,0 +1,425 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "back-wt.h"
+#include "slap-config.h"
+#include "lutil.h"
+
+/* Find the ad, return -1 if not found,
+ * set point for insertion if ins is non-NULL
+ */
+int
+wt_attr_slot( struct wt_info *wi, AttributeDescription *ad, int *ins )
+{
+ unsigned base = 0, cursor = 0;
+ unsigned n = wi->wi_nattrs;
+ int val = 0;
+
+ while ( 0 < n ) {
+ unsigned pivot = n >> 1;
+ cursor = base + pivot;
+
+ val = SLAP_PTRCMP( ad, wi->wi_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 wt_info *wi, AttrInfo *a )
+{
+ int x = INT_MAX;
+ int i = wt_attr_slot( wi, a->ai_desc, &x );
+
+ /* Is it a dup? */
+ if ( i >= 0 )
+ return -1;
+
+ wi->wi_attrs = ch_realloc( wi->wi_attrs, ( wi->wi_nattrs+1 ) *
+ sizeof( AttrInfo * ));
+ if ( x < wi->wi_nattrs )
+ AC_MEMCPY( &wi->wi_attrs[x+1], &wi->wi_attrs[x],
+ ( wi->wi_nattrs - x ) * sizeof( AttrInfo *));
+ wi->wi_attrs[x] = a;
+ wi->wi_nattrs++;
+ return 0;
+}
+
+AttrInfo *
+wt_attr_mask(
+ struct wt_info *wi,
+ AttributeDescription *desc )
+{
+ int i = wt_attr_slot( wi, desc, NULL );
+ return i < 0 ? NULL : wi->wi_attrs[i];
+}
+
+int
+wt_attr_index_config(
+ struct wt_info *wi,
+ 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 = wi->wi_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 ) {
+ wi->wi_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 );
+ }
+fail:
+#ifdef LDAP_COMP_MATCH
+ ch_free( cr );
+#endif
+ 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 fail;
+ }
+
+ 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 fail;
+ }
+
+ 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 fail;
+ }
+
+ 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 fail;
+ }
+
+ Debug( LDAP_DEBUG_CONFIG, "index %s 0x%04lx\n",
+ ad->ad_cname.bv_val, mask );
+
+ a = (AttrInfo *) ch_malloc( sizeof(AttrInfo) );
+
+#ifdef LDAP_COMP_MATCH
+ a->ai_cr = NULL;
+#endif
+ a->ai_desc = ad;
+
+ if ( wi->wi_flags & WT_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 = wt_attr_mask( wi, ad );
+ if ( a_cr ) {
+ /*
+ * AttrInfo is already in AVL
+ * just add the extracted component reference
+ * in the AttrInfo
+ */
+ ch_free( a );
+ 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 fail;
+ }
+ 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;
+ ch_free( a );
+ goto fail;
+ }
+ }
+ }
+#endif
+ rc = ainfo_insert( wi, a );
+ if( rc ) {
+ if ( wi->wi_flags & WT_IS_OPEN ) {
+ AttrInfo *b = wt_attr_mask( wi, 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 & WT_INDEX_DELETING ) {
+ /* If we were editing this attr, reset it */
+ b->ai_indexmask &= ~WT_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
+wt_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,
+ (const char*)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
+wt_attr_index_unparse( struct wt_info *wi, BerVarray *bva )
+{
+ int i;
+
+ if ( wi->wi_defaultmask ) {
+ aidef.ai_indexmask = wi->wi_defaultmask;
+ wt_attr_index_unparser( &aidef, bva );
+ }
+ for ( i=0; i<wi->wi_nattrs; i++ )
+ wt_attr_index_unparser( wi->wi_attrs[i], bva );
+}
+
+void
+wt_attr_info_free( AttrInfo *ai )
+{
+#ifdef LDAP_COMP_MATCH
+ free( ai->ai_cr );
+#endif
+ free( ai );
+}
+
+void
+wt_attr_index_destroy( struct wt_info *wi )
+{
+ int i;
+
+ for ( i=0; i<wi->wi_nattrs; i++ )
+ wt_attr_info_free( wi->wi_attrs[i] );
+
+ free( wi->wi_attrs );
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/back-wt.h b/servers/slapd/back-wt/back-wt.h
new file mode 100644
index 0000000..386dd85
--- /dev/null
+++ b/servers/slapd/back-wt/back-wt.h
@@ -0,0 +1,120 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#ifndef _BACK_WT_H_
+#define _BACK_WT_H_
+
+#include <portable.h>
+
+#include <ac/errno.h>
+#include <sys/stat.h>
+
+#include "slap.h"
+#include "wiredtiger.h"
+
+/* The default search IDL stack cache depth */
+#define DEFAULT_SEARCH_STACK_DEPTH 16
+
+#define WT_CONFIG_MAX 2048
+
+struct wt_info {
+ WT_CONNECTION *wi_conn;
+ WT_CONNECTION *wi_cache;
+ char *wi_home;
+ char *wi_config;
+ ID wi_lastid;
+
+ slap_mask_t wi_defaultmask;
+ int wi_nattrs;
+ struct wt_attrinfo **wi_attrs;
+ void *wi_search_stack;
+ int wi_search_stack_depth;
+
+ struct re_s *wi_index_task;
+
+ int wi_flags;
+#define WT_IS_OPEN 0x01
+#define WT_OPEN_INDEX 0x02
+#define WT_DEL_INDEX 0x08
+#define WT_RE_OPEN 0x10
+#define WT_NEED_UPGRADE 0x20
+#define WT_USE_IDLCACHE 0x40
+};
+
+#define WT_TABLE_ID2ENTRY "table:id2entry"
+#define WT_TABLE_DN2ID "table:dn2id"
+
+#define WT_INDEX_DN "index:id2entry:dn"
+#define WT_INDEX_NDN "index:dn2id:ndn"
+#define WT_INDEX_PID "index:dn2id:pid"
+/* Currently, revdn is primary key, the revdn index is obsolete. */
+#define WT_INDEX_REVDN "index:dn2id:revdn"
+
+/* table for cache */
+#define WT_TABLE_IDLCACHE "table:idlcache"
+
+#define ITEMzero(item) (memset((item), 0, sizeof(WT_ITEM)))
+#define ITEM2bv(item,bv) ((bv)->bv_val = (item)->data, \
+ (bv)->bv_len = (item)->size)
+#define bv2ITEM(bv,item) ((item)->data = (bv)->bv_val, \
+ (item)->size = (bv)->bv_len )
+
+#define WT_INDEX_CACHE_SIZE 1024
+
+typedef struct {
+ WT_SESSION *session;
+ int is_begin_transaction;
+ WT_CURSOR *dn2id;
+ WT_CURSOR *dn2id_w;
+ WT_CURSOR *dn2id_ndn;
+ WT_CURSOR *dn2entry;
+ WT_CURSOR *id2entry;
+ WT_CURSOR *id2entry_add;
+ WT_CURSOR *id2entry_update;
+ WT_SESSION *idlcache_session;
+ WT_CURSOR *index_pid;
+} wt_ctx;
+
+/* for the cache of attribute information (which are indexed, etc.) */
+typedef struct wt_attrinfo {
+ AttributeDescription *ai_desc; /* attribute description cn;lang-en */
+ slap_mask_t ai_indexmask; /* how the attr is indexed */
+ slap_mask_t ai_newmask; /* new settings to replace old mask */
+ #ifdef LDAP_COMP_MATCH
+ ComponentReference* ai_cr; /*component indexing*/
+ #endif
+} AttrInfo;
+
+/* These flags must not clash with SLAP_INDEX flags or ops in slap.h! */
+#define WT_INDEX_DELETING 0x8000U /* index is being modified */
+#define WT_INDEX_UPDATE_OP 0x03 /* performing an index update */
+
+#include "proto-wt.h"
+
+#endif /* _BACK_WT_H_ */
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/bind.c b/servers/slapd/back-wt/bind.c
new file mode 100644
index 0000000..43abe87
--- /dev/null
+++ b/servers/slapd/back-wt/bind.c
@@ -0,0 +1,150 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include "back-wt.h"
+#include "slap-config.h"
+
+int
+wt_bind( Operation *op, SlapReply *rs )
+{
+ struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+ wt_ctx *wc;
+ int rc;
+ Entry *e = NULL;
+ Attribute *a;
+ AttributeDescription *password = slap_schema.si_ad_userPassword;
+
+ Debug( LDAP_DEBUG_ARGS, "==> wt_bind: dn: %s\n",
+ op->o_req_dn.bv_val );
+
+ /* 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;
+ }
+
+ wc = wt_ctx_get(op, wi);
+ if( !wc ){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_bind: wt_ctx_get failed\n" );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ send_ldap_result( op, rs );
+ return rs->sr_err;
+ }
+
+ /* get entry */
+ rc = wt_dn2entry(op->o_bd, wc, &op->o_req_ndn, &e);
+ switch( rc ) {
+ case 0:
+ break;
+ case WT_NOTFOUND:
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ send_ldap_result( op, rs );
+ return rs->sr_err;
+ default:
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ send_ldap_result( op, rs );
+ return rs->sr_err;
+ }
+
+ 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" );
+ 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" );
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ goto done;
+ }
+
+ if ( is_entry_referral( e ) ) {
+ Debug( LDAP_DEBUG_TRACE, "entry is referral\n" );
+ 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:
+ rs->sr_err = LDAP_STRONG_AUTH_NOT_SUPPORTED;
+ rs->sr_text = "authentication method not supported";
+ }
+
+done:
+ /* free entry */
+ if (e) {
+ wt_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;
+ }
+ }
+ return rs->sr_err;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/cache.c b/servers/slapd/back-wt/cache.c
new file mode 100644
index 0000000..ee393a0
--- /dev/null
+++ b/servers/slapd/back-wt/cache.c
@@ -0,0 +1,231 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include "back-wt.h"
+#include "slap-config.h"
+#include "idl.h"
+
+int wt_idlcache_get(wt_ctx *wc, struct berval *ndn, int scope, ID *ids)
+{
+ int rc = 0;
+ WT_ITEM item;
+ WT_SESSION *session = wc->idlcache_session;
+ WT_CURSOR *cursor = NULL;
+
+ Debug( LDAP_DEBUG_TRACE,
+ "=> wt_idlcache_get(\"%s\", %d)\n",
+ ndn->bv_val, scope );
+
+ rc = session->open_cursor(session, WT_TABLE_IDLCACHE, NULL,
+ NULL, &cursor);
+ if(rc){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_idlcache_get: open_cursor failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ return rc;
+ }
+ cursor->set_key(cursor, ndn->bv_val, (int8_t)scope);
+ rc = cursor->search(cursor);
+ switch( rc ){
+ case 0:
+ break;
+ case WT_NOTFOUND:
+ Debug(LDAP_DEBUG_TRACE, "<= wt_idlcache_get: miss\n" );
+ goto done;
+ default:
+ Debug( LDAP_DEBUG_ANY, "<= wt_idlcache_get: search failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ rc = 0;
+ goto done;
+ }
+ rc = cursor->get_value(cursor, &item);
+ if (rc) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_idlcache_get: get_value failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+ if (item.size == 0) {
+ Debug(LDAP_DEBUG_TRACE, "<= wt_idlcache_get: updating\n");
+ rc = WT_NOTFOUND;
+ goto done;
+ }
+ memcpy(ids, item.data, item.size);
+
+ Debug(LDAP_DEBUG_TRACE,
+ "<= wt_idlcache_get: hit id=%ld first=%ld last=%ld\n",
+ (long)ids[0],
+ (long)WT_IDL_FIRST(ids),
+ (long)WT_IDL_LAST(ids));
+done:
+ if(cursor) {
+ cursor->close(cursor);
+ }
+ return rc;
+}
+
+int wt_idlcache_set(wt_ctx *wc, struct berval *ndn, int scope, ID *ids)
+{
+ int rc = 0;
+ WT_ITEM item;
+ WT_SESSION *session = wc->idlcache_session;
+ WT_CURSOR *cursor = NULL;
+
+ Debug( LDAP_DEBUG_TRACE,
+ "=> wt_idlcache_set(\"%s\", %d)\n",
+ ndn->bv_val, scope );
+
+ item.size = WT_IDL_SIZEOF(ids);
+ item.data = ids;
+
+ rc = session->open_cursor(session, WT_TABLE_IDLCACHE, NULL,
+ "overwrite=false", &cursor);
+ if(rc){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_idlcache_set: open_cursor failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ return rc;
+ }
+ cursor->set_key(cursor, ndn->bv_val, (int8_t)scope);
+ cursor->set_value(cursor, &item);
+ rc = cursor->update(cursor);
+ switch( rc ){
+ case 0:
+ break;
+ case WT_NOTFOUND:
+ // updating cache by another thread
+ goto done;
+ default:
+ Debug( LDAP_DEBUG_ANY,
+ "wt_idlcache_set: update failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+
+ Debug(LDAP_DEBUG_TRACE,
+ "<= wt_idlcache_set: set idl size=%ld\n",
+ (long)ids[0]);
+done:
+ if(cursor) {
+ cursor->close(cursor);
+ }
+ return rc;
+}
+
+int wt_idlcache_begin(wt_ctx *wc, struct berval *ndn, int scope)
+{
+ int rc = 0;
+ WT_ITEM item;
+ WT_SESSION *session = wc->idlcache_session;
+ WT_CURSOR *cursor = NULL;
+
+ Debug( LDAP_DEBUG_TRACE,
+ "=> wt_idlcache_begin(\"%s\", %d)\n",
+ ndn->bv_val, scope );
+
+ item.size = 0;
+ item.data = "";
+
+ rc = session->open_cursor(session, WT_TABLE_IDLCACHE, NULL,
+ "overwrite=true", &cursor);
+ if(rc){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_idlcache_begin: open_cursor failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ return rc;
+ }
+ cursor->set_key(cursor, ndn->bv_val, (int8_t)scope);
+ cursor->set_value(cursor, &item);
+ rc = cursor->update(cursor);
+ if(rc){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_idlcache_begin: update failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+
+ Debug(LDAP_DEBUG_TRACE,
+ "<= wt_idlcache_begin: set updating\n" );
+
+done:
+ if(cursor) {
+ cursor->close(cursor);
+ }
+ return rc;
+}
+
+int wt_idlcache_clear(Operation *op, wt_ctx *wc, struct berval *ndn)
+{
+ BackendDB *be = op->o_bd;
+ int rc = 0;
+ struct berval pdn = *ndn;
+ WT_SESSION *session = wc->idlcache_session;
+ WT_CURSOR *cursor = NULL;
+ int level = 0;
+
+ Debug( LDAP_DEBUG_TRACE,
+ "=> wt_idlcache_clear(\"%s\")\n",
+ ndn->bv_val );
+
+ if (be_issuffix( be, ndn )) {
+ return 0;
+ }
+
+ rc = session->open_cursor(session, WT_TABLE_IDLCACHE, NULL,
+ NULL, &cursor);
+ if(rc){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_idlcache_clear: open_cursor failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ return rc;
+ }
+
+ do {
+ dnParent( &pdn, &pdn );
+ if (level == 0) {
+ /* clear only parent level cache */
+ cursor->set_key(cursor, pdn.bv_val, (int8_t)LDAP_SCOPE_ONE);
+ cursor->remove(cursor);
+ }
+ cursor->set_key(cursor, pdn.bv_val, (int8_t)LDAP_SCOPE_SUB);
+ cursor->remove(cursor);
+ cursor->set_key(cursor, pdn.bv_val, (int8_t)LDAP_SCOPE_CHILDREN);
+ cursor->remove(cursor);
+ level++;
+ }while(!be_issuffix( be, &pdn ));
+
+ if(cursor) {
+ cursor->close(cursor);
+ }
+ return 0;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/compare.c b/servers/slapd/back-wt/compare.c
new file mode 100644
index 0000000..038b273
--- /dev/null
+++ b/servers/slapd/back-wt/compare.c
@@ -0,0 +1,154 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-wt.h"
+#include "slap-config.h"
+
+int
+wt_compare( Operation *op, SlapReply *rs )
+{
+ struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+ Entry *e = NULL;
+ int manageDSAit = get_manageDSAit( op );
+ int rc;
+ wt_ctx *wc = NULL;
+
+ Debug( LDAP_DEBUG_ARGS, "==> wt_compare: %s\n",
+ op->o_req_dn.bv_val );
+
+ wc = wt_ctx_get(op, wi);
+ if( !wc ){
+ Debug( LDAP_DEBUG_ANY, "wt_compare: wt_ctx_get failed\n" );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ send_ldap_result( op, rs );
+ return rs->sr_err;
+ }
+
+ rc = wt_dn2entry(op->o_bd, wc, &op->o_req_ndn, &e);
+ switch( rc ) {
+ case 0:
+ case WT_NOTFOUND:
+ break;
+ default:
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ if ( rc == WT_NOTFOUND ||
+ (!manageDSAit && e && is_entry_glue( e ) )) {
+
+ if ( !e ) {
+ rc = wt_dn2aentry(op->o_bd, wc, &op->o_req_ndn, &e);
+ switch( rc ) {
+ case 0:
+ break;
+ case WT_NOTFOUND:
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ goto return_results;
+ default:
+ Debug( LDAP_DEBUG_ANY, "wt_compare: wt_dn2aentry failed (%d)\n",
+ rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+ }
+
+ /* 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;
+ }
+ 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" );
+
+ 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 ( e != NULL ) {
+ wt_entry_return( e );
+ }
+ return rs->sr_err;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/config.c b/servers/slapd/back-wt/config.c
new file mode 100644
index 0000000..804e25b
--- /dev/null
+++ b/servers/slapd/back-wt/config.c
@@ -0,0 +1,209 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include "back-wt.h"
+#include "slap-config.h"
+
+#include "lutil.h"
+#include "ldap_rq.h"
+
+static ConfigDriver wt_cf_gen;
+
+enum {
+ WT_DIRECTORY = 1,
+ WT_CONFIG,
+ WT_INDEX,
+ WT_MODE,
+ WT_IDLCACHE,
+};
+
+static ConfigTable wtcfg[] = {
+ { "directory", "dir", 2, 2, 0, ARG_STRING|ARG_MAGIC|WT_DIRECTORY,
+ wt_cf_gen, "( OLcfgDbAt:0.1 NAME 'olcDbDirectory' "
+ "DESC 'Directory for database content' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "index", "attr> <[pres,eq,approx,sub]", 2, 3, 0, ARG_MAGIC|WT_INDEX,
+ wt_cf_gen, "( OLcfgDbAt:0.2 NAME 'olcDbIndex' "
+ "DESC 'Attribute index parameters' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "mode", "mode", 2, 2, 0, ARG_MAGIC|WT_MODE,
+ wt_cf_gen, "( OLcfgDbAt:0.3 NAME 'olcDbMode' "
+ "DESC 'Unix permissions of database files' "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "wtconfig", "config", 2, 2, 0, ARG_STRING|ARG_MAGIC|WT_CONFIG,
+ wt_cf_gen, "( OLcfgDbAt:13.1 NAME 'olcWtConfig' "
+ "DESC 'Configuration for WiredTiger' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "idlcache", NULL, 1, 2, 0, ARG_ON_OFF|ARG_MAGIC|WT_IDLCACHE,
+ wt_cf_gen, "( OLcfgDbAt:13.2 NAME 'olcIDLcache' "
+ "DESC 'enable IDL cache' "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED,
+ NULL, NULL, NULL, NULL }
+};
+
+static ConfigOCs wtocs[] = {
+ { "( OLcfgDbOc:13.1 "
+ "NAME 'olcWtConfig' "
+ "DESC 'Wt backend configuration' "
+ "SUP olcDatabaseConfig "
+ "MUST olcDbDirectory "
+ "MAY ( olcWtConfig $ olcDbIndex $ olcDbMode $ olcIDLcache) )",
+ Cft_Database, wtcfg },
+ { NULL, 0, NULL }
+};
+
+/* reindex entries on the fly */
+static void *
+wt_online_index( void *ctx, void *arg )
+{
+ // Not implement yet
+ return NULL;
+}
+
+/* Cleanup loose ends after Modify completes */
+static int
+wt_cf_cleanup( ConfigArgs *c )
+{
+ // Not implement yet
+ return 0;
+}
+
+static int
+wt_cf_gen( ConfigArgs *c )
+{
+ struct wt_info *wi = (struct wt_info *) c->be->be_private;
+ int rc;
+
+ if( c->op == SLAP_CONFIG_EMIT ) {
+ rc = 0;
+ switch( c->type ) {
+ case WT_DIRECTORY:
+ if ( wi->wi_home ) {
+ c->value_string = ch_strdup( wi->wi_home );
+ } else {
+ rc = 1;
+ }
+ break;
+ case WT_INDEX:
+ wt_attr_index_unparse( wi, &c->rvalue_vals );
+ if ( !c->rvalue_vals ) rc = 1;
+ break;
+ case WT_IDLCACHE:
+ if ( wi->wi_flags & WT_USE_IDLCACHE) {
+ c->value_int = 1;
+ }
+ break;
+ }
+ return rc;
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ rc = 0;
+ return rc;
+ }
+
+ switch( c->type ) {
+ case WT_DIRECTORY:
+ ch_free( wi->wi_home );
+ wi->wi_home = c->value_string;
+ break;
+ case WT_CONFIG:
+ if(strlen(wi->wi_config) + 1 + strlen(c->value_string) > WT_CONFIG_MAX){
+ fprintf( stderr, "%s: "
+ "\"wtconfig\" are too long. Increase WT_CONFIG_MAX or you may realloc the buffer.\n",
+ c->log );
+ return 1;
+ }
+ /* size of wi->wi_config is WT_CONFIG_MAX + 1, it's guaranteed with NUL-terminate. */
+ strcat(wi->wi_config, ",");
+ strcat(wi->wi_config, c->value_string);
+ break;
+
+ case WT_INDEX:
+ rc = wt_attr_index_config( wi, c->fname, c->lineno,
+ c->argc - 1, &c->argv[1], &c->reply);
+
+ if( rc != LDAP_SUCCESS ) return 1;
+ wi->wi_flags |= WT_OPEN_INDEX;
+
+ if ( wi->wi_flags & WT_IS_OPEN ) {
+ config_push_cleanup( c, wt_cf_cleanup );
+
+ if ( !wi->wi_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 );
+ wi->wi_index_task = ldap_pvt_runqueue_insert(&slapd_rq, 36000,
+ wt_online_index, c->be,
+ LDAP_XSTRING(wt_online_index),
+ c->be->be_suffix[0].bv_val );
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+ }
+ }
+ break;
+
+ case WT_MODE:
+ fprintf( stderr, "%s: "
+ "back-wt does not support \"mode\" option. use umask instead.\n",
+ c->log );
+ return 1;
+
+ case WT_IDLCACHE:
+ if ( c->value_int ) {
+ wi->wi_flags |= WT_USE_IDLCACHE;
+ } else {
+ wi->wi_flags &= ~WT_USE_IDLCACHE;
+ }
+ break;
+ }
+ return LDAP_SUCCESS;
+}
+
+int wt_back_init_cf( BackendInfo *bi )
+{
+ int rc;
+ bi->bi_cf_ocs = wtocs;
+
+ rc = config_register_schema( wtcfg, wtocs );
+ if ( rc ) return rc;
+ return 0;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/ctx.c b/servers/slapd/back-wt/ctx.c
new file mode 100644
index 0000000..de6578e
--- /dev/null
+++ b/servers/slapd/back-wt/ctx.c
@@ -0,0 +1,117 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "back-wt.h"
+#include "slap-config.h"
+
+wt_ctx *
+wt_ctx_init(struct wt_info *wi)
+{
+ int rc;
+ wt_ctx *wc;
+
+ wc = ch_malloc( sizeof( wt_ctx ) );
+ if( !wc ) {
+ Debug( LDAP_DEBUG_ANY, "wt_ctx_init: cannot allocate memory\n" );
+ return NULL;
+ }
+
+ memset(wc, 0, sizeof(wt_ctx));
+
+ rc = wi->wi_conn->open_session(wi->wi_conn, NULL, NULL, &wc->session);
+ if( rc ) {
+ Debug( LDAP_DEBUG_ANY, "wt_ctx_init: open_session error %s(%d)\n",
+ wiredtiger_strerror(rc), rc );
+ return NULL;
+ }
+
+ /* readonly mode */
+ if (!wi->wi_cache) {
+ return wc;
+ }
+
+ rc = wi->wi_cache->open_session(wi->wi_cache, NULL, NULL, &wc->idlcache_session);
+ if( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_ctx_init: cannot open idlcache session %s(%d)\n",
+ wiredtiger_strerror(rc), rc );
+ return NULL;
+ }
+
+ return wc;
+}
+
+void
+wt_ctx_free( void *key, void *data )
+{
+ wt_ctx *wc = data;
+
+ if(wc->session){
+ /*
+ * The session will close automatically when db closing.
+ * We can close session here, but it's require to check db
+ * status, otherwise it will cause SEGV.
+ */
+ /*
+ if(IS_DB_OPEN) {
+ wc->session->close(wc->session, NULL);
+ }
+ */
+ wc->session = NULL;
+ }
+
+ ch_free(wc);
+}
+
+wt_ctx *
+wt_ctx_get(Operation *op, struct wt_info *wi){
+ int rc;
+ void *data;
+ wt_ctx *wc = NULL;
+
+ rc = ldap_pvt_thread_pool_getkey(op->o_threadctx,
+ wi, &data, NULL );
+ if( rc ){
+ wc = wt_ctx_init(wi);
+ if( !wc ) {
+ Debug( LDAP_DEBUG_ANY, "wt_ctx: wt_ctx_init failed\n" );
+ return NULL;
+ }
+ rc = ldap_pvt_thread_pool_setkey( op->o_threadctx,
+ wi, wc, wt_ctx_free,
+ NULL, NULL );
+ if( rc ) {
+ Debug( LDAP_DEBUG_ANY, "wt_ctx: setkey error(%d)\n",
+ rc );
+ return NULL;
+ }
+ return wc;
+ }
+ return (wt_ctx *)data;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/delete.c b/servers/slapd/back-wt/delete.c
new file mode 100644
index 0000000..9673662
--- /dev/null
+++ b/servers/slapd/back-wt/delete.c
@@ -0,0 +1,419 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-wt.h"
+#include "slap-config.h"
+
+int
+wt_delete( Operation *op, SlapReply *rs )
+{
+ struct wt_info *wi = (struct wt_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;
+
+ LDAPControl **preread_ctrl = NULL;
+ LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
+ int num_ctrls = 0;
+
+ wt_ctx *wc;
+ int rc;
+
+ int parent_is_glue = 0;
+ int parent_is_leaf = 0;
+
+ Debug( LDAP_DEBUG_ARGS, "==> wt_delete: %s\n",
+ op->o_req_dn.bv_val );
+
+ if( op->o_txnSpec && txn_preop( op, rs ))
+ return rs->sr_err;
+
+ ctrls[num_ctrls] = 0;
+ rs->sr_text = NULL;
+
+ wc = wt_ctx_get(op, wi);
+ if( !wc ){
+ Debug( LDAP_DEBUG_TRACE, "wt_delete: wt_ctx_get failed\n" );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+/* 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 );
+ }
+
+ if ( !be_issuffix( op->o_bd, &op->o_req_ndn ) ) {
+ dnParent( &op->o_req_ndn, &pdn );
+ }
+
+ /* get parent */
+ rc = wt_dn2entry(op->o_bd, wc, &pdn, &p);
+ switch( rc ) {
+ case 0:
+ case WT_NOTFOUND:
+ break;
+ default:
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ Debug( LDAP_DEBUG_ANY,
+ "wt_delete: error at wt_dn2entry() rc=%d\n", rc );
+ goto return_results;
+ }
+
+ if ( rc == WT_NOTFOUND && pdn.bv_len != 0 ) {
+ Debug( LDAP_DEBUG_ARGS,
+ "<== wt_delete: parent not found %s\n", op->o_req_dn.bv_val );
+ rc = wt_dn2aentry(op->o_bd, wc, &op->o_req_ndn, &e);
+ Debug( LDAP_DEBUG_ARGS, "<== wt_delete: rc=%d\n", rc );
+
+ switch( rc ) {
+ case 0:
+ break;
+ case WT_NOTFOUND:
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ goto return_results;
+ default:
+ Debug( LDAP_DEBUG_ANY, "wt_delete: wt_dn2aentry failed (%d)\n", rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ if ( e && !BER_BVISEMPTY( &e->e_name )) {
+ rs->sr_matched = ch_strdup( e->e_name.bv_val );
+ 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;
+ }
+ } else {
+ rs->sr_ref = referral_rewrite( default_referral, NULL,
+ &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+ }
+
+ rs->sr_err = LDAP_REFERRAL;
+ rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
+ goto return_results;
+ }
+
+ /* get entry */
+ rc = wt_dn2entry(op->o_bd, wc, &op->o_req_ndn, &e);
+ switch( rc ) {
+ case 0:
+ break;
+ case WT_NOTFOUND:
+ break;
+ default:
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ Debug( LDAP_DEBUG_ANY,
+ "wt_delete: error at wt_dn2entry() rc=%d\n", rc );
+ goto return_results;
+ }
+
+ /* FIXME : dn2entry() should return non-glue entry */
+ if (rc == WT_NOTFOUND ||
+ ( !manageDSAit && e && is_entry_glue( e ) )) {
+ if ( !e ) {
+ Debug( LDAP_DEBUG_ARGS,
+ "<== wt_delete: no such object %s\n",
+ op->o_req_dn.bv_val);
+ rc = wt_dn2aentry(op->o_bd, wc, &op->o_req_ndn, &e);
+ switch( rc ) {
+ case 0:
+ break;
+ case WT_NOTFOUND:
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ goto return_results;
+ default:
+ Debug( LDAP_DEBUG_ANY, "wt_delete: wt_dn2aentry failed (%d)\n", rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+ }
+
+ 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;
+ 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,
+ "<== wt_delete: no write access to parent\n" );
+ 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,
+ "<== wt_delete: no access to parent\n" );
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ rs->sr_text = "no write access to parent";
+ goto return_results;
+ }
+
+ } else {
+ Debug( LDAP_DEBUG_TRACE,
+ "<== wt_delete: no parent and not root\n" );
+ 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,
+ "<== wt_delete: no write access to entry\n" );
+ 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, "wt_delete: entry is referral\n" );
+
+ 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,
+ "<== wt_delete: pre-read failed!\n" );
+ if ( op->o_preread & SLAP_CONTROL_CRITICAL ) {
+ /* FIXME: is it correct to abort
+ * operation if control fails? */
+ goto return_results;
+ }
+ }
+ }
+
+ /* Can't do it if we have kids */
+ rc = wt_dn2id_has_children( op, wc, e->e_id );
+ if( rc != WT_NOTFOUND ) {
+ switch( rc ) {
+ case 0:
+ Debug(LDAP_DEBUG_ARGS,
+ "<== wt_delete: non-leaf %s\n", op->o_req_dn.bv_val );
+ rs->sr_err = LDAP_NOT_ALLOWED_ON_NONLEAF;
+ rs->sr_text = "subordinate objects must be deleted first";
+ break;
+ default:
+ Debug(LDAP_DEBUG_ARGS,
+ "<== wt_delete: has_children failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ }
+ goto return_results;
+ }
+
+ /* begin transaction */
+ rc = wc->session->begin_transaction(wc->session, NULL);
+ if( rc ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_delete: begin_transaction failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "begin_transaction failed";
+ goto return_results;
+ }
+
+ /* delete from dn2id */
+ rc = wt_dn2id_delete( op, wc, &op->o_req_ndn);
+ if ( rc ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "<== wt_delete: dn2id failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "dn2id delete failed";
+ wc->session->rollback_transaction(wc->session, NULL);
+ goto return_results;
+ }
+
+ /* delete indices for old attributes */
+ rc = wt_index_entry_del( op, wc, e );
+ if ( rc ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "<== wt_delete: index delete failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "index delete failed";
+ wc->session->rollback_transaction(wc->session, NULL);
+ 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 = wt_index_values( op, wc, 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;
+ wc->session->rollback_transaction(wc->session, NULL);
+ goto return_results;
+ }
+ }
+
+ /* delete from id2entry */
+ rc = wt_id2entry_delete( op, wc, e );
+ if ( rc ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<== wt_delete: id2entry failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "entry delete failed";
+ wc->session->rollback_transaction(wc->session, NULL);
+ goto return_results;
+ }
+
+ if ( pdn.bv_len != 0 ) {
+ // TODO: glue entry
+ }
+
+ rc = wc->session->commit_transaction(wc->session, NULL);
+ if( rc ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<== wt_delete: commit_transaction failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "commit_transaction failed";
+ goto return_results;
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_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 ) {
+ wt_entry_return( p );
+ }
+
+ /* free entry */
+ if( e != NULL ) {
+ wt_entry_return( e );
+ }
+
+ 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 );
+ }
+
+ /* TODO: checkpoint */
+
+ return rs->sr_err;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/dn2entry.c b/servers/slapd/back-wt/dn2entry.c
new file mode 100644
index 0000000..84cb13d
--- /dev/null
+++ b/servers/slapd/back-wt/dn2entry.c
@@ -0,0 +1,176 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include "back-wt.h"
+#include "slap-config.h"
+
+/*
+ * dn2entry - look up dn in the db and return the corresponding entry.
+ * No longer return closest ancestor, see wt_dn2pentry().
+ */
+int wt_dn2entry( BackendDB *be,
+ wt_ctx *wc,
+ struct berval *ndn,
+ Entry **ep ){
+ uint64_t id;
+ WT_ITEM item;
+ EntryHeader eh;
+ int rc;
+ int eoff;
+ Entry *e = NULL;
+ WT_SESSION *session = wc->session;
+ WT_CURSOR *cursor = wc->dn2entry;
+
+ if( ndn->bv_len == 0 ){
+ /* empty dn */
+ e = entry_alloc();
+ ber_dupbv(&e->e_nname, ndn);
+ *ep = e;
+ return LDAP_SUCCESS;
+ }
+
+ if(!cursor){
+ rc = session->open_cursor(session,
+ WT_INDEX_DN"(id, entry)",
+ NULL, NULL, &cursor);
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_dn2entry: open_cursor failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+ wc->dn2entry = cursor;
+ }
+
+ cursor->set_key(cursor, ndn->bv_val);
+ rc = cursor->search(cursor);
+ switch( rc ){
+ case 0:
+ break;
+ case WT_NOTFOUND:
+ goto done;
+ default:
+ Debug( LDAP_DEBUG_ANY,
+ "wt_dn2entry: search failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+ cursor->get_value(cursor, &id, &item);
+ rc = wt_entry_header( &item, &eh );
+
+ eoff = eh.data - (char *)item.data;
+ eh.bv.bv_len = eh.nvals * sizeof( struct berval ) + item.size;
+ eh.bv.bv_val = ch_malloc( eh.bv.bv_len );
+ memset(eh.bv.bv_val, 0xff, eh.bv.bv_len);
+ eh.data = eh.bv.bv_val + eh.nvals * sizeof( struct berval );
+ memcpy(eh.data, item.data, item.size);
+ eh.data += eoff;
+ rc = entry_decode( &eh, &e );
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_dn2entry: entry decode error: %d\n", rc );
+ goto done;
+ }
+
+ e->e_id = id;
+ *ep = e;
+
+done:
+
+#ifdef WT_CURSOR_CACHE
+ if(cursor){
+ cursor->reset(cursor);
+ }
+#else
+ if(cursor){
+ cursor->close(cursor);
+ wc->dn2entry = NULL;
+ }
+#endif
+ return rc;
+}
+
+/* dn2pentry - return parent entry */
+int wt_dn2pentry( BackendDB *be,
+ wt_ctx *wc,
+ struct berval *ndn,
+ Entry **ep ){
+ Entry *e = NULL;
+ struct berval pdn;
+ int rc;
+
+ if (be_issuffix( be, ndn )) {
+ *ep = NULL;
+ return WT_NOTFOUND;
+ }
+
+ dnParent( ndn, &pdn );
+ rc = wt_dn2entry(be, wc, &pdn, &e);
+ *ep = e;
+ return rc;
+}
+
+/* dn2aentry - return ancestor entry */
+int wt_dn2aentry( BackendDB *be,
+ wt_ctx *wc,
+ struct berval *ndn,
+ Entry **ep ) {
+ Entry *e = NULL;
+ struct berval pdn;
+ int rc;
+
+ if (be_issuffix( be, ndn )) {
+ *ep = NULL;
+ return 0;
+ }
+
+ dnParent( ndn, &pdn );
+ rc = wt_dn2entry(be, wc, &pdn, &e);
+ switch( rc ) {
+ case 0:
+ *ep = e;
+ break;
+ case WT_NOTFOUND:
+ rc = wt_dn2aentry(be, wc, &pdn, &e);
+ if (rc != 0 && rc != WT_NOTFOUND) {
+ return rc;
+ }
+ *ep = e;
+ break;
+ default:
+ Debug( LDAP_DEBUG_ANY,
+ "wt_dn2aentry: failed %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ }
+ return rc;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/dn2id.c b/servers/slapd/back-wt/dn2id.c
new file mode 100644
index 0000000..d8765ce
--- /dev/null
+++ b/servers/slapd/back-wt/dn2id.c
@@ -0,0 +1,453 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include "back-wt.h"
+#include "slap-config.h"
+#include "idl.h"
+
+static char *
+mkrevdn(struct berval src){
+ char *dst, *p;
+ char *rdn;
+ size_t rdn_len;
+
+ p = dst = ch_malloc(src.bv_len + 2);
+ while(src.bv_len){
+ rdn = ber_bvrchr( &src, ',' );
+ if (rdn) {
+ rdn_len = src.bv_len;
+ src.bv_len = rdn - src.bv_val;
+ rdn_len -= src.bv_len + 1;
+ rdn++;
+ }else{
+ /* first rdn */
+ rdn_len = src.bv_len;
+ rdn = src.bv_val;
+ src.bv_len = 0;
+ }
+ memcpy( p, rdn, rdn_len );
+ p += rdn_len;
+ *p++ = ',';
+ }
+ *p = '\0';
+ return dst;
+}
+
+int
+wt_dn2id_add(
+ Operation *op,
+ wt_ctx *wc,
+ ID pid,
+ Entry *e)
+{
+ struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+ int rc;
+ WT_SESSION *session = wc->session;
+ WT_CURSOR *cursor = wc->dn2id_w;
+ char *revdn = NULL;
+
+ Debug( LDAP_DEBUG_TRACE, "=> wt_dn2id_add 0x%lx: \"%s\"\n",
+ e->e_id, e->e_ndn );
+ assert( e->e_id != NOID );
+
+ /* make reverse dn */
+ revdn = mkrevdn(e->e_nname);
+
+ if(!cursor){
+ rc = session->open_cursor(session, WT_TABLE_DN2ID, NULL,
+ "overwrite=false", &cursor);
+ if(rc){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_dn2id_add: open_cursor failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+ wc->dn2id_w = cursor;
+ }
+ cursor->set_key(cursor, revdn);
+ cursor->set_value(cursor, e->e_ndn, e->e_id, pid);
+ rc = cursor->insert(cursor);
+ if(rc){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_dn2id_add: insert failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+
+ if (wi->wi_flags & WT_USE_IDLCACHE) {
+ wt_idlcache_clear(op, wc, &e->e_nname);
+ }
+
+done:
+ if(revdn){
+ ch_free(revdn);
+ }
+
+#ifdef WT_CURSOR_CACHE
+ if(cursor){
+ cursor->reset(cursor);
+ }
+#else
+ if(cursor){
+ cursor->close(cursor);
+ wc->dn2id_w = NULL;
+ }
+#endif
+
+ Debug( LDAP_DEBUG_TRACE, "<= wt_dn2id_add 0x%lx: %d\n", e->e_id, rc );
+ return rc;
+}
+
+int
+wt_dn2id_delete(
+ Operation *op,
+ wt_ctx *wc,
+ struct berval *ndn)
+{
+ struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+ int rc = 0;
+ WT_SESSION *session = wc->session;
+ WT_CURSOR *cursor = wc->dn2id_w;
+ char *revdn = NULL;
+
+ Debug( LDAP_DEBUG_TRACE, "=> wt_dn2id_delete %s\n", ndn->bv_val );
+
+ /* make reverse dn */
+ revdn = mkrevdn(*ndn);
+
+ if(!cursor){
+ rc = session->open_cursor(session, WT_TABLE_DN2ID, NULL,
+ "overwrite=false", &cursor);
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_dn2id_delete: open_cursor failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+ wc->dn2id_w = cursor;
+ }
+
+ cursor->set_key(cursor, revdn);
+ rc = cursor->remove(cursor);
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_dn2id_delete: remove failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+
+ if (wi->wi_flags & WT_USE_IDLCACHE) {
+ wt_idlcache_clear(op, wc, ndn);
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ "<= wt_dn2id_delete %s: %d\n", ndn->bv_val, rc );
+done:
+ if(revdn){
+ ch_free(revdn);
+ }
+
+#ifdef WT_CURSOR_CACHE
+ if(cursor){
+ cursor->reset(cursor);
+ }
+#else
+ if(cursor){
+ cursor->close(cursor);
+ wc->dn2id_w = NULL;
+ }
+#endif
+ return rc;
+}
+
+int
+wt_dn2id(
+ Operation *op,
+ wt_ctx *wc,
+ struct berval *ndn,
+ ID *id)
+{
+ WT_SESSION *session = wc->session;
+ WT_CURSOR *cursor = wc->dn2id_ndn;
+ int rc = LDAP_SUCCESS;
+
+ Debug( LDAP_DEBUG_TRACE, "=> wt_dn2id(\"%s\")\n", ndn->bv_val );
+
+ if ( ndn->bv_len == 0 ) {
+ *id = 0;
+ goto done;
+ }
+
+ if(!cursor){
+ rc = session->open_cursor(session, WT_INDEX_NDN
+ "(id)",
+ NULL, NULL, &cursor);
+ if( rc ){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_dn2id: cursor open failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+ wc->dn2id_ndn = cursor;
+ }
+
+ cursor->set_key(cursor, ndn->bv_val);
+ rc = cursor->search(cursor);
+ switch( rc ){
+ case 0:
+ break;
+ case WT_NOTFOUND:
+ goto done;
+ default:
+ Debug( LDAP_DEBUG_ANY,
+ "wt_dn2id: search failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+ rc = cursor->get_value(cursor, id);
+ if( rc ){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_dn2id: get_value failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+
+done:
+
+#ifdef WT_CURSOR_CACHE
+ if(cursor){
+ cursor->reset(cursor);
+ }
+#else
+ if(cursor){
+ cursor->close(cursor);
+ wc->dn2id_ndn = NULL;
+ }
+#endif
+
+ if( rc ) {
+ Debug( LDAP_DEBUG_TRACE, "<= wt_dn2id: get failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ } else {
+ Debug( LDAP_DEBUG_TRACE, "<= wt_dn2id: got id=0x%lx\n",
+ *id );
+ }
+
+ return rc;
+}
+
+int
+wt_dn2id_has_children(
+ Operation *op,
+ wt_ctx *wc,
+ ID id )
+{
+ WT_SESSION *session = wc->session;
+ WT_CURSOR *cursor = wc->index_pid;
+ int rc;
+ uint64_t key = id;
+
+ if(!cursor){
+ rc = session->open_cursor(session, WT_INDEX_PID,
+ NULL, NULL, &cursor);
+ if( rc ){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_dn2id_has_children: cursor open failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+ wc->index_pid = cursor;
+ }
+
+ cursor->set_key(cursor, key);
+ rc = cursor->search(cursor);
+
+done:
+
+#ifdef WT_CURSOR_CACHE
+ if(cursor){
+ cursor->reset(cursor);
+ }
+#else
+ if(cursor){
+ cursor->close(cursor);
+ wc->index_pid = NULL;
+ }
+#endif
+
+ return rc;
+}
+
+int
+wt_dn2idl_db(
+ Operation *op,
+ wt_ctx *wc,
+ struct berval *ndn,
+ Entry *e,
+ ID *ids,
+ ID *stack)
+{
+ WT_SESSION *session = wc->session;
+ WT_CURSOR *cursor = wc->dn2id;
+ int rc;
+ char *revdn = NULL;
+ size_t revdn_len;
+ char *key;
+ ID id, pid;
+
+ Debug( LDAP_DEBUG_TRACE,
+ "=> wt_dn2idl(\"%s\")\n",
+ ndn->bv_val );
+
+ revdn = mkrevdn(*ndn);
+ revdn_len = strlen(revdn);
+
+ if ( !cursor ) {
+ rc = session->open_cursor(session, WT_TABLE_DN2ID"(id, pid)",
+ NULL, NULL, &cursor);
+ if( rc ){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_dn2idl: cursor open failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+ wc->dn2id = cursor;
+ }
+ cursor->set_key(cursor, revdn);
+ rc = cursor->search(cursor);
+ if( rc ){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_dn2idl: search failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+
+ if( op->ors_scope == LDAP_SCOPE_CHILDREN ) {
+ cursor->next(cursor);
+ }
+
+ do {
+ rc = cursor->get_key(cursor, &key);
+ if( rc ){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_dn2idl: get_key failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+ rc = cursor->get_value(cursor, &id, &pid);
+ if( rc ){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_dn2id: get_value failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+
+ if( strncmp(revdn, key, revdn_len) ){
+ break;
+ }
+
+ if( op->ors_scope == LDAP_SCOPE_ONELEVEL && e->e_id != pid ){
+ goto next;
+ }
+ wt_idl_append_one(ids, id);
+ next:
+ rc = cursor->next(cursor);
+ }while(rc == 0);
+
+ if (rc == WT_NOTFOUND ) {
+ rc = LDAP_SUCCESS;
+ }
+
+ wt_idl_sort(ids, stack);
+ Debug( LDAP_DEBUG_TRACE,
+ "<= wt_dn2idl_db: size=%ld first=%ld last=%ld\n",
+ (long) ids[0],
+ (long) WT_IDL_FIRST(ids),
+ (long) WT_IDL_LAST(ids) );
+
+done:
+ if(revdn){
+ ch_free(revdn);
+ }
+#ifdef WT_CURSOR_CACHE
+ if(cursor){
+ cursor->reset(cursor);
+ }
+#else
+ if(cursor){
+ cursor->close(cursor);
+ wc->dn2id = NULL;
+ }
+#endif
+ return rc;
+}
+
+int
+wt_dn2idl(
+ Operation *op,
+ wt_ctx *wc,
+ struct berval *ndn,
+ Entry *e,
+ ID *ids,
+ ID *stack)
+{
+ struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+ int rc;
+
+ Debug( LDAP_DEBUG_TRACE,
+ "=> wt_dn2idl(\"%s\")\n", ndn->bv_val );
+
+ if(op->ors_scope != LDAP_SCOPE_ONELEVEL &&
+ be_issuffix( op->o_bd, &e->e_nname )){
+ WT_IDL_ALL(wi, ids);
+ return 0;
+ }
+
+ if (wi->wi_flags & WT_USE_IDLCACHE) {
+ rc = wt_idlcache_get(wc, ndn, op->ors_scope, ids);
+ if (rc == 0) {
+ /* cache hit */
+ return rc;
+ }
+ /* cache miss */
+ }
+
+ if ( wi->wi_flags & WT_USE_IDLCACHE ) {
+ wt_idlcache_begin(wc, ndn, op->ors_scope);
+ }
+ rc = wt_dn2idl_db(op, wc, ndn, e, ids, stack);
+ if ( rc == 0 && wi->wi_flags & WT_USE_IDLCACHE ) {
+ wt_idlcache_set(wc, ndn, op->ors_scope, ids);
+ }
+
+ return rc;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/extended.c b/servers/slapd/back-wt/extended.c
new file mode 100644
index 0000000..595672f
--- /dev/null
+++ b/servers/slapd/back-wt/extended.c
@@ -0,0 +1,58 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-wt.h"
+#include "lber_pvt.h"
+
+static struct exop {
+ struct berval *oid;
+ BI_op_extended *extended;
+} exop_table[] = {
+ { NULL, NULL }
+};
+
+int
+wt_extended( Operation *op, SlapReply *rs )
+{
+ 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;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/filterindex.c b/servers/slapd/back-wt/filterindex.c
new file mode 100644
index 0000000..f321128
--- /dev/null
+++ b/servers/slapd/back-wt/filterindex.c
@@ -0,0 +1,718 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include "back-wt.h"
+#include "idl.h"
+
+static int
+presence_candidates(
+ Operation *op,
+ wt_ctx *wc,
+ AttributeDescription *desc,
+ ID *ids )
+{
+ struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+ slap_mask_t mask;
+ struct berval prefix = {0, NULL};
+ int rc;
+ WT_CURSOR *cursor = NULL;
+
+ Debug( LDAP_DEBUG_TRACE, "=> wt_presence_candidates (%s)\n",
+ desc->ad_cname.bv_val );
+
+ WT_IDL_ALL( wi, ids );
+
+ if( desc == slap_schema.si_ad_objectClass ) {
+ return 0;
+ }
+
+ rc = wt_index_param( op->o_bd, desc, LDAP_FILTER_PRESENT,
+ &mask, &prefix );
+
+ if( rc == LDAP_INAPPROPRIATE_MATCHING ) {
+ /* not indexed */
+ Debug( LDAP_DEBUG_FILTER,
+ "<= wt_presence_candidates: (%s) not indexed\n",
+ desc->ad_cname.bv_val );
+ return 0;
+ }
+
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= wt_presence_candidates: (%s) index_param "
+ "returned=%d\n",
+ desc->ad_cname.bv_val, rc );
+ return 0;
+ }
+
+ if( prefix.bv_val == NULL ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= wt_presence_candidates: (%s) no prefix\n",
+ desc->ad_cname.bv_val );
+ return -1;
+ }
+
+ /* open index cursor */
+ cursor = wt_index_open(wc, &desc->ad_type->sat_cname, 0);
+ if( !cursor ) {
+ Debug( LDAP_DEBUG_ANY,
+ "<= wt_presence_candidates: open index cursor failed: %s\n",
+ desc->ad_type->sat_cname.bv_val );
+ return 0;
+ }
+
+ rc = wt_key_read( op->o_bd, cursor, &prefix, ids, NULL, 0 );
+
+ cursor->close(cursor);
+ Debug(LDAP_DEBUG_TRACE,
+ "<= wt_presence_candidates: id=%ld first=%ld last=%ld\n",
+ (long) ids[0],
+ (long) WT_IDL_FIRST(ids),
+ (long) WT_IDL_LAST(ids) );
+
+ return 0;
+}
+
+static int
+equality_candidates(
+ Operation *op,
+ wt_ctx *wc,
+ AttributeAssertion *ava,
+ ID *ids,
+ ID *tmp)
+{
+ struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+ slap_mask_t mask;
+ struct berval prefix = {0, NULL};
+ struct berval *keys = NULL;
+ int i;
+ int rc;
+ MatchingRule *mr;
+ WT_CURSOR *cursor = NULL;
+
+ Debug( LDAP_DEBUG_TRACE, "=> wt_equality_candidates (%s=%s)\n",
+ ava->aa_desc->ad_cname.bv_val, ava->aa_value.bv_val );
+
+ if ( ava->aa_desc == slap_schema.si_ad_entryDN ) {
+ ID id = NOID;
+ rc = wt_dn2id(op, wc, &ava->aa_value, &id);
+ if( rc == 0 ){
+ wt_idl_append_one(ids, id);
+ }else if ( rc == WT_NOTFOUND ) {
+ WT_IDL_ZERO( ids );
+ rc = 0;
+ }
+ return rc;
+ }
+
+ WT_IDL_ALL( wi, ids );
+
+ rc = wt_index_param( op->o_bd, ava->aa_desc, LDAP_FILTER_EQUALITY,
+ &mask, &prefix );
+
+ if ( rc == LDAP_INAPPROPRIATE_MATCHING ) {
+ Debug( LDAP_DEBUG_FILTER,
+ "<= wt_equality_candidates: (%s) not indexed\n",
+ ava->aa_desc->ad_cname.bv_val );
+ return 0;
+ }
+
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "<= wt_equality_candidates: (%s) index_param failed (%d)\n",
+ ava->aa_desc->ad_cname.bv_val, rc );
+ 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,
+ "<= wt_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,
+ "<= wt_equality_candidates: (%s) no keys\n",
+ ava->aa_desc->ad_cname.bv_val );
+ return 0;
+ }
+
+ /* open index cursor */
+ cursor = wt_index_open(wc, &ava->aa_desc->ad_type->sat_cname, 0);
+ if( !cursor ) {
+ Debug( LDAP_DEBUG_ANY,
+ "<= wt_equality_candidates: open index cursor failed: %s\n",
+ ava->aa_desc->ad_type->sat_cname.bv_val );
+ return 0;
+ }
+
+ for ( i= 0; keys[i].bv_val != NULL; i++ ) {
+ rc = wt_key_read( op->o_bd, cursor, &keys[i], tmp, NULL, 0 );
+ if( rc == WT_NOTFOUND ) {
+ WT_IDL_ZERO( ids );
+ rc = 0;
+ break;
+ } else if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= wt_equality_candidates: (%s) key read failed (%d)\n",
+ ava->aa_desc->ad_cname.bv_val, rc );
+ break;
+ }
+ if ( i == 0 ) {
+ WT_IDL_CPY( ids, tmp );
+ } else {
+ wt_idl_intersection( ids, tmp );
+ }
+
+ if( WT_IDL_IS_ZERO( ids ) )
+ break;
+ }
+
+ ber_bvarray_free_x( keys, op->o_tmpmemctx );
+
+ cursor->close(cursor);
+
+ Debug( LDAP_DEBUG_TRACE,
+ "<= wt_equality_candidates: id=%ld, first=%ld, last=%ld\n",
+ (long) ids[0],
+ (long) WT_IDL_FIRST(ids),
+ (long) WT_IDL_LAST(ids) );
+
+ return rc;
+}
+
+static int
+approx_candidates(
+ Operation *op,
+ wt_ctx *wc,
+ AttributeAssertion *ava,
+ ID *ids,
+ ID *tmp )
+{
+ struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+ int i;
+ int rc;
+ slap_mask_t mask;
+ struct berval prefix = {0, NULL};
+ struct berval *keys = NULL;
+ MatchingRule *mr;
+ WT_CURSOR *cursor = NULL;
+
+ Debug( LDAP_DEBUG_TRACE, "=> wt_approx_candidates (%s)\n",
+ ava->aa_desc->ad_cname.bv_val );
+
+ WT_IDL_ALL( wi, ids );
+
+ rc = wt_index_param( op->o_bd, ava->aa_desc, LDAP_FILTER_APPROX,
+ &mask, &prefix );
+
+ if ( rc == LDAP_INAPPROPRIATE_MATCHING ) {
+ Debug( LDAP_DEBUG_FILTER,
+ "<= wt_approx_candidates: (%s) not indexed\n",
+ ava->aa_desc->ad_cname.bv_val );
+ return 0;
+ }
+
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "<= wt_approx_candidates: (%s) index_param failed (%d)\n",
+ ava->aa_desc->ad_cname.bv_val, rc );
+ 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,
+ "<= wt_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,
+ "<= wt_approx_candidates: (%s) no keys (%s)\n",
+ prefix.bv_val, ava->aa_desc->ad_cname.bv_val );
+ return 0;
+ }
+
+ /* open index cursor */
+ cursor = wt_index_open(wc, &ava->aa_desc->ad_type->sat_cname, 0);
+ if( !cursor ) {
+ Debug( LDAP_DEBUG_ANY,
+ "<= wt_approx_candidates: open index cursor failed: %s\n",
+ ava->aa_desc->ad_type->sat_cname.bv_val );
+ return 0;
+ }
+
+ for ( i= 0; keys[i].bv_val != NULL; i++ ) {
+ rc = wt_key_read( op->o_bd, cursor, &keys[i], tmp, NULL, 0 );
+ if( rc == WT_NOTFOUND ) {
+ WT_IDL_ZERO( ids );
+ rc = 0;
+ break;
+ } else if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= wt_approx_candidates: (%s) key read failed (%d)\n",
+ ava->aa_desc->ad_cname.bv_val, rc );
+ break;
+ }
+
+ if( WT_IDL_IS_ZERO( tmp ) ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= wt_approx_candidates: (%s) NULL\n",
+ ava->aa_desc->ad_cname.bv_val );
+ WT_IDL_ZERO( ids );
+ break;
+ }
+
+ if ( i == 0 ) {
+ WT_IDL_CPY( ids, tmp );
+ } else {
+ wt_idl_intersection( ids, tmp );
+ }
+
+ if( WT_IDL_IS_ZERO( ids ) )
+ break;
+ }
+
+ ber_bvarray_free_x( keys, op->o_tmpmemctx );
+
+ cursor->close(cursor);
+
+ Debug( LDAP_DEBUG_TRACE,
+ "<= wt_approx_candidates %ld, first=%ld, last=%ld\n",
+ (long) ids[0],
+ (long) WT_IDL_FIRST(ids),
+ (long) WT_IDL_LAST(ids) );
+
+ return rc;
+}
+
+static int
+substring_candidates(
+ Operation *op,
+ wt_ctx *wc,
+ SubstringsAssertion *sub,
+ ID *ids,
+ ID *tmp )
+{
+ struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+ int i;
+ int rc;
+ slap_mask_t mask;
+ struct berval prefix = {0, NULL};
+ struct berval *keys = NULL;
+ MatchingRule *mr;
+ WT_CURSOR *cursor = NULL;
+
+ Debug( LDAP_DEBUG_TRACE, "=> wt_substring_candidates (%s)\n",
+ sub->sa_desc->ad_cname.bv_val );
+
+ WT_IDL_ALL( wi, ids );
+
+ rc = wt_index_param( op->o_bd, sub->sa_desc, LDAP_FILTER_SUBSTRINGS,
+ &mask, &prefix );
+
+ if ( rc == LDAP_INAPPROPRIATE_MATCHING ) {
+ Debug( LDAP_DEBUG_FILTER,
+ "<= wt_substring_candidates: (%s) not indexed\n",
+ sub->sa_desc->ad_cname.bv_val );
+ return 0;
+ }
+
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "<= wt_substring_candidates: (%s) index_param failed (%d)\n",
+ sub->sa_desc->ad_cname.bv_val, rc );
+ 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,
+ "<= wt_substring_candidates: (%s) MR filter failed (%d)\n",
+ sub->sa_desc->ad_cname.bv_val, rc );
+ return 0;
+ }
+
+ if( keys == NULL ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= wt_substring_candidates: (0x%04lx) no keys (%s)\n",
+ mask, sub->sa_desc->ad_cname.bv_val );
+ return 0;
+ }
+
+ /* open index cursor */
+ cursor = wt_index_open(wc, &sub->sa_desc->ad_cname, 0);
+ if( !cursor ) {
+ Debug( LDAP_DEBUG_ANY,
+ "<= wt_substring_candidates: open index cursor failed: %s\n",
+ sub->sa_desc->ad_cname.bv_val );
+ return 0;
+ }
+
+ for ( i= 0; keys[i].bv_val != NULL; i++ ) {
+ rc = wt_key_read( op->o_bd, cursor, &keys[i], tmp, NULL, 0 );
+
+ if( rc == WT_NOTFOUND ) {
+ WT_IDL_ZERO( ids );
+ rc = 0;
+ break;
+ } else if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= wt_substring_candidates: (%s) key read failed (%d)\n",
+ sub->sa_desc->ad_cname.bv_val, rc );
+ break;
+ }
+
+ if( WT_IDL_IS_ZERO( tmp ) ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= wt_substring_candidates: (%s) NULL\n",
+ sub->sa_desc->ad_cname.bv_val );
+ WT_IDL_ZERO( ids );
+ break;
+ }
+
+ if ( i == 0 ) {
+ WT_IDL_CPY( ids, tmp );
+ } else {
+ wt_idl_intersection( ids, tmp );
+ }
+
+ if( WT_IDL_IS_ZERO( ids ) )
+ break;
+ }
+
+ ber_bvarray_free_x( keys, op->o_tmpmemctx );
+
+ cursor->close(cursor);
+
+ Debug( LDAP_DEBUG_TRACE,
+ "<= wt_substring_candidates: %ld, first=%ld, last=%ld\n",
+ (long) ids[0],
+ (long) WT_IDL_FIRST(ids),
+ (long) WT_IDL_LAST(ids));
+ return rc;
+}
+
+#ifdef LDAP_COMP_MATCH
+static int
+comp_candidates (
+ Operation *op,
+ wt_ctx *wc,
+ MatchingRuleAssertion *mra,
+ ComponentFilter *f,
+ ID *ids,
+ ID *tmp,
+ ID *stack)
+{
+ int rc = 0;
+
+ if ( !f ) return LDAP_PROTOCOL_ERROR;
+
+ Debug( LDAP_DEBUG_FILTER, "comp_candidates\n" );
+ /* TODO: */
+ Debug( LDAP_DEBUG_FILTER, "=> not implement yet\n" );
+ return( rc );
+}
+
+#endif
+
+static int
+ext_candidates(
+ Operation *op,
+ wt_ctx *wc,
+ MatchingRuleAssertion *mra,
+ ID *ids,
+ ID *tmp,
+ ID *stack )
+{
+ struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+
+#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, wc, mra, mra->ma_cf, ids, tmp, stack);
+ }
+#endif
+ if ( mra->ma_desc == slap_schema.si_ad_entryDN ) {
+ /* TODO: */
+ Debug( LDAP_DEBUG_FILTER, "=> not implement yet.\n" );
+ }
+ WT_IDL_ALL( wi, ids );
+ return 0;
+}
+
+static int
+list_candidates(
+ Operation *op,
+ wt_ctx *wc,
+ Filter *flist,
+ int ftype,
+ ID *ids,
+ ID *tmp,
+ ID *save )
+{
+ int rc = 0;
+ Filter *f;
+
+ Debug( LDAP_DEBUG_FILTER, "=> wt_list_candidates 0x%x\n", ftype );
+ 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;
+ }
+ WT_IDL_ZERO( save );
+ rc = wt_filter_candidates( op, wc, f, save, tmp,
+ save+WT_IDL_UM_SIZE );
+
+ if ( rc != 0 ) {
+ /* TODO: error handling */
+ /*
+ if ( rc == DB_LOCK_DEADLOCK )
+ return rc;
+ */
+ if ( ftype == LDAP_FILTER_AND ) {
+ rc = 0;
+ continue;
+ }
+ break;
+ }
+
+
+ if ( ftype == LDAP_FILTER_AND ) {
+ if ( f == flist ) {
+ WT_IDL_CPY( ids, save );
+ } else {
+ wt_idl_intersection( ids, save );
+ }
+ if( WT_IDL_IS_ZERO( ids ) )
+ break;
+ } else {
+ if ( f == flist ) {
+ WT_IDL_CPY( ids, save );
+ } else {
+ wt_idl_union( ids, save );
+ }
+ }
+ }
+
+ if( rc == LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_FILTER,
+ "<= wt_list_candidates: id=%ld first=%ld last=%ld\n",
+ (long) ids[0],
+ (long) WT_IDL_FIRST(ids),
+ (long) WT_IDL_LAST(ids) );
+
+ } else {
+ Debug( LDAP_DEBUG_FILTER,
+ "<= wt_list_candidates: undefined rc=%d\n",
+ rc );
+ }
+
+ return 0;
+}
+
+int
+wt_filter_candidates(
+ Operation *op,
+ wt_ctx *wc,
+ Filter *f,
+ ID *ids,
+ ID *tmp,
+ ID *stack )
+{
+ struct wt_info *wi = (struct wt_info *)op->o_bd->be_private;
+ int rc = LDAP_SUCCESS;
+ Debug( LDAP_DEBUG_FILTER, "=> wt_filter_candidates\n" );
+
+ if ( f->f_choice & SLAPD_FILTER_UNDEFINED ) {
+ WT_IDL_ZERO( ids );
+ goto done;
+ }
+
+ 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:
+ WT_IDL_ZERO( ids );
+ break;
+ case LDAP_COMPARE_TRUE: {
+
+ WT_IDL_ALL( wi, 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" );
+ rc = presence_candidates( op, wc, f->f_desc, ids );
+ break;
+
+ case LDAP_FILTER_EQUALITY:
+ Debug( LDAP_DEBUG_FILTER, "\tEQUALITY\n" );
+ rc = equality_candidates( op, wc, f->f_ava, ids, tmp );
+ break;
+
+ case LDAP_FILTER_APPROX:
+ Debug( LDAP_DEBUG_FILTER, "\tAPPROX\n" );
+ rc = approx_candidates( op, wc, f->f_ava, ids, tmp );
+ break;
+
+ case LDAP_FILTER_SUBSTRINGS:
+ Debug( LDAP_DEBUG_FILTER, "\tSUBSTRINGS\n" );
+ rc = substring_candidates( op, wc, f->f_sub, ids, tmp );
+ break;
+
+ case LDAP_FILTER_GE:
+ /* if no GE index, use pres */
+ /* TODO: not implement yet */
+ rc = presence_candidates( op, wc, f->f_ava->aa_desc, ids );
+ break;
+
+ case LDAP_FILTER_LE:
+ /* if no LE index, use pres */
+ /* TODO: not implement yet */
+ Debug( LDAP_DEBUG_FILTER, "\tLE\n" );
+ rc = presence_candidates( op, wc, f->f_ava->aa_desc, ids );
+ break;
+
+ case LDAP_FILTER_NOT:
+ /* no indexing to support NOT filters */
+ Debug( LDAP_DEBUG_FILTER, "\tNOT\n" );
+ WT_IDL_ALL( wi, ids );
+ break;
+
+ case LDAP_FILTER_AND:
+ Debug( LDAP_DEBUG_FILTER, "\tAND\n" );
+ rc = list_candidates( op, wc,
+ f->f_and, LDAP_FILTER_AND, ids, tmp, stack );
+ break;
+
+ case LDAP_FILTER_OR:
+ Debug( LDAP_DEBUG_FILTER, "\tOR\n" );
+ rc = list_candidates( op, wc,
+ f->f_or, LDAP_FILTER_OR, ids, tmp, stack );
+ break;
+
+ case LDAP_FILTER_EXT:
+ Debug( LDAP_DEBUG_FILTER, "\tEXT\n" );
+ rc = ext_candidates( op, wc, f->f_mra, ids, tmp, stack);
+ break;
+
+ default:
+ Debug( LDAP_DEBUG_FILTER, "\tUNKNOWN %lu\n",
+ (unsigned long) f->f_choice );
+ /* Must not return NULL, otherwise extended filters break */
+ WT_IDL_ALL( wi, ids );
+ }
+
+done:
+ Debug( LDAP_DEBUG_FILTER,
+ "<= wt_filter_candidates: id=%ld first=%ld last=%ld\n",
+ (long) ids[0],
+ (long) WT_IDL_FIRST( ids ),
+ (long) WT_IDL_LAST( ids ) );
+ return rc;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/id2entry.c b/servers/slapd/back-wt/id2entry.c
new file mode 100644
index 0000000..95b197c
--- /dev/null
+++ b/servers/slapd/back-wt/id2entry.c
@@ -0,0 +1,352 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "back-wt.h"
+#include "slap-config.h"
+
+static int wt_id2entry_put(
+ Operation *op,
+ wt_ctx *wc,
+ Entry *e,
+ WT_CURSOR *cursor)
+{
+ struct berval bv;
+ WT_ITEM item;
+ int rc;
+
+ rc = entry_encode( e, &bv );
+ if(rc != LDAP_SUCCESS){
+ return -1;
+ }
+ item.size = bv.bv_len;
+ item.data = bv.bv_val;
+
+ cursor->set_key(cursor, e->e_id);
+ cursor->set_value(cursor, e->e_ndn, &item);
+ rc = cursor->insert(cursor);
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_id2entry_put: insert failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+
+done:
+ ch_free( bv.bv_val );
+
+ return rc;
+}
+
+int wt_id2entry_add(
+ Operation *op,
+ wt_ctx *wc,
+ Entry *e )
+{
+ WT_SESSION *session = wc->session;
+ WT_CURSOR *cursor = wc->id2entry_add;
+ int rc;
+
+ if(!cursor){
+ rc = session->open_cursor(session, WT_TABLE_ID2ENTRY, NULL,
+ "overwrite=false", &cursor);
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_id2entry_put: open_cursor failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ return rc;
+ }
+ wc->id2entry_add = cursor;
+ }
+
+ rc = wt_id2entry_put(op, wc, e, cursor);
+
+#ifdef WT_CURSOR_CACHE
+ if(cursor){
+ cursor->reset(cursor);
+ }
+#else
+ if(cursor){
+ cursor->close(cursor);
+ wc->id2entry_add = NULL;
+ }
+#endif
+
+ return rc;
+}
+
+int wt_id2entry_update(
+ Operation *op,
+ wt_ctx *wc,
+ Entry *e )
+{
+ WT_SESSION *session = wc->session;
+ WT_CURSOR *cursor = wc->id2entry_update;
+ int rc;
+
+ if(!cursor){
+ rc = session->open_cursor(session, WT_TABLE_ID2ENTRY, NULL,
+ "overwrite=true", &cursor);
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_id2entry_put: open_cursor failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ return rc;
+ }
+ wc->id2entry_update = cursor;
+ }
+ rc = wt_id2entry_put(op, wc, e, cursor);
+
+#ifdef WT_CURSOR_CACHE
+ if(cursor){
+ cursor->reset(cursor);
+ }
+#else
+ if(cursor){
+ cursor->close(cursor);
+ wc->id2entry_update = NULL;
+ }
+#endif
+ return rc;
+}
+
+int wt_id2entry_delete(
+ Operation *op,
+ wt_ctx *wc,
+ Entry *e )
+{
+ int rc;
+ WT_SESSION *session = wc->session;
+ WT_CURSOR *cursor = NULL;
+
+ rc = session->open_cursor(session, WT_TABLE_ID2ENTRY, NULL,
+ NULL, &cursor);
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_id2entry_delete: open_cursor failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+ cursor->set_key(cursor, e->e_id);
+ rc = cursor->remove(cursor);
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_id2entry_delete: remove failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+
+done:
+ if(cursor){
+ cursor->close(cursor);
+ }
+ return rc;
+}
+
+int wt_id2entry( BackendDB *be,
+ wt_ctx *wc,
+ ID id,
+ Entry **ep ){
+ int rc;
+ WT_SESSION *session = wc->session;
+ WT_CURSOR *cursor = wc->id2entry;
+ WT_ITEM item;
+ EntryHeader eh;
+ int eoff;
+ Entry *e = NULL;
+
+ if(!cursor){
+ rc = session->open_cursor(session, WT_TABLE_ID2ENTRY"(entry)", NULL,
+ NULL, &cursor);
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_id2entry: open_cursor failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+ wc->id2entry = cursor;
+ }
+
+ cursor->set_key(cursor, id);
+ rc = cursor->search(cursor);
+ if ( rc ) {
+ goto done;
+ }
+
+ cursor->get_value(cursor, &item);
+ rc = wt_entry_header( &item, &eh );
+ eoff = eh.data - (char *)item.data;
+ eh.bv.bv_len = eh.nvals * sizeof( struct berval ) + item.size;
+ eh.bv.bv_val = ch_malloc( eh.bv.bv_len );
+ memset(eh.bv.bv_val, 0xff, eh.bv.bv_len);
+ eh.data = eh.bv.bv_val + eh.nvals * sizeof( struct berval );
+ memcpy(eh.data, item.data, item.size);
+ eh.data += eoff;
+ rc = entry_decode( &eh, &e );
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_id2entry: entry decode error: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+ e->e_id = id;
+ *ep = e;
+
+done:
+
+#ifdef WT_CURSOR_CACHE
+ if(cursor){
+ cursor->reset(cursor);
+ }
+#else
+ if(cursor){
+ cursor->close(cursor);
+ wc->id2entry = NULL;
+ }
+#endif
+ return rc;
+}
+
+int wt_entry_return(
+ Entry *e
+ )
+{
+ if ( !e ) {
+ return 0;
+ }
+
+ /* Our entries are allocated in two blocks; the data comes from
+ * the db itself and the Entry structure and associated pointers
+ * are allocated in entry_decode. The db data pointer is saved
+ * in e_bv.
+ */
+ if ( e->e_bv.bv_val ) {
+#if 0
+ /* See if the DNs were changed by modrdn */
+ if( e->e_nname.bv_val < e->e_bv.bv_val || e->e_nname.bv_val >
+ e->e_bv.bv_val + e->e_bv.bv_len ) {
+ ch_free(e->e_name.bv_val);
+ ch_free(e->e_nname.bv_val);
+ }
+#endif
+ e->e_name.bv_val = NULL;
+ e->e_nname.bv_val = NULL;
+ /* In tool mode the e_bv buffer is realloc'd, leave it alone */
+ if( !(slapMode & SLAP_TOOL_MODE) ) {
+ free( e->e_bv.bv_val );
+ }
+ BER_BVZERO( &e->e_bv );
+ }
+
+ entry_free( e );
+}
+
+int wt_entry_release(
+ Operation *op,
+ Entry *e,
+ int rw )
+{
+ return wt_entry_return( e );
+}
+
+/*
+ * return LDAP_SUCCESS IFF we can retrieve the specified entry.
+ */
+int wt_entry_get(
+ Operation *op,
+ struct berval *ndn,
+ ObjectClass *oc,
+ AttributeDescription *at,
+ int rw,
+ Entry **ent )
+{
+ struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+ wt_ctx *wc;
+ Entry *e = NULL;
+ int rc;
+ const char *at_name = at ? at->ad_cname.bv_val : "(null)";
+
+ Debug( LDAP_DEBUG_ARGS,
+ "wt_entry_get: ndn: \"%s\"\n", ndn->bv_val );
+ Debug( LDAP_DEBUG_ARGS,
+ "wt_entry_get: oc: \"%s\", at: \"%s\"\n",
+ oc ? oc->soc_cname.bv_val : "(null)", at_name );
+
+ wc = wt_ctx_get(op, wi);
+ if( !wc ){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_entry_get: wt_ctx_get failed\n" );
+ return LDAP_OTHER;
+ }
+ rc = wt_dn2entry(op->o_bd, wc, ndn, &e);
+ switch( rc ) {
+ case 0:
+ break;
+ case WT_NOTFOUND:
+ Debug( LDAP_DEBUG_ACL,
+ "wt_entry_get: cannot find entry: \"%s\"\n",
+ ndn->bv_val );
+ return LDAP_NO_SUCH_OBJECT;
+ default:
+ Debug( LDAP_DEBUG_ANY,
+ "wt_entry_get: wt_dn2entry failed %s rc=%d\n",
+ wiredtiger_strerror(rc), rc );
+ rc = LDAP_OTHER;
+ }
+
+ Debug( LDAP_DEBUG_ACL,
+ "wt_entry_get: found entry: \"%s\"\n", ndn->bv_val );
+
+ if ( oc && !is_entry_objectclass( e, oc, 0 )) {
+ Debug( LDAP_DEBUG_ACL,
+ "wt_entry_get: failed to find objectClass %s\n",
+ oc->soc_cname.bv_val );
+ 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,
+ "wt_entry_get: failed to find attribute %s\n",
+ at->ad_cname.bv_val );
+ rc = LDAP_NO_SUCH_ATTRIBUTE;
+ goto return_results;
+ }
+
+return_results:
+ if( rc != LDAP_SUCCESS ) {
+ wt_entry_return( e );
+ }else{
+ *ent = e;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "wt_entry_get: rc=%d\n", rc );
+
+ return rc;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/idl.c b/servers/slapd/back-wt/idl.c
new file mode 100644
index 0000000..0f3167d
--- /dev/null
+++ b/servers/slapd/back-wt/idl.c
@@ -0,0 +1,789 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-wt.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) )
+
+void wt_idl_check( ID *ids )
+{
+ if( WT_IDL_IS_RANGE( ids ) ) {
+ assert( WT_IDL_RANGE_FIRST(ids) <= WT_IDL_RANGE_LAST(ids) );
+ } else {
+ ID i;
+ for( i=1; i < ids[0]; i++ ) {
+ assert( ids[i+1] > ids[i] );
+ }
+ }
+}
+
+void wt_idl_dump( ID *ids )
+{
+ if( WT_IDL_IS_RANGE( ids ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "IDL: range ( %ld - %ld )\n",
+ (long) WT_IDL_RANGE_FIRST( ids ),
+ (long) WT_IDL_RANGE_LAST( ids ) );
+
+ } else {
+ ID i;
+ Debug( LDAP_DEBUG_ANY, "IDL: size %ld", (long) ids[0] );
+
+ for( i=1; i<=ids[0]; i++ ) {
+ if( i % 16 == 1 ) {
+ Debug( LDAP_DEBUG_ANY, "\n" );
+ }
+ Debug( LDAP_DEBUG_ANY, " %02lx", (long) ids[i] );
+ }
+
+ Debug( LDAP_DEBUG_ANY, "\n" );
+ }
+
+ wt_idl_check( ids );
+}
+
+unsigned wt_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 position greater than id
+ */
+ unsigned base = 0;
+ unsigned cursor = 1;
+ int val = 0;
+ unsigned n = ids[0];
+
+#if IDL_DEBUG > 0
+ idl_check( ids );
+#endif
+
+ while( 0 < n ) {
+ unsigned pivot = n >> 1;
+ cursor = base + pivot + 1;
+ val = IDL_CMP( id, ids[cursor] );
+
+ if( val < 0 ) {
+ n = pivot;
+
+ } else if ( val > 0 ) {
+ base = cursor;
+ n -= pivot + 1;
+
+ } else {
+ return cursor;
+ }
+ }
+
+ if( val > 0 ) {
+ ++cursor;
+ }
+ return cursor;
+
+#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 wt_idl_insert( ID *ids, ID id )
+{
+ unsigned x;
+
+#if IDL_DEBUG > 1
+ Debug( LDAP_DEBUG_ANY, "insert: %04lx at %d\n", (long) id, x );
+ idl_dump( ids );
+#elif IDL_DEBUG > 0
+ wt_idl_check( ids );
+#endif
+
+ if (WT_IDL_IS_RANGE( ids )) {
+ /* if already in range, treat as a dup */
+ if (id >= WT_IDL_RANGE_FIRST(ids) && id <= WT_IDL_RANGE_LAST(ids))
+ return -1;
+ if (id < WT_IDL_RANGE_FIRST(ids))
+ ids[1] = id;
+ else if (id > WT_IDL_RANGE_LAST(ids))
+ ids[2] = id;
+ return 0;
+ }
+
+ x = wt_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] >= WT_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
+ wt_idl_dump( ids );
+#elif IDL_DEBUG > 0
+ wt_idl_check( ids );
+#endif
+
+ return 0;
+}
+
+static int wt_idl_delete( ID *ids, ID id )
+{
+ unsigned x;
+
+#if IDL_DEBUG > 1
+ Debug( LDAP_DEBUG_ANY, "delete: %04lx at %d\n", (long) id, x );
+ idl_dump( ids );
+#elif IDL_DEBUG > 0
+ wt_idl_check( ids );
+#endif
+
+ if (WT_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 = wt_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
+ wt_idl_dump( ids );
+#elif IDL_DEBUG > 0
+ wt_idl_check( ids );
+#endif
+
+ return 0;
+}
+
+static char *
+wt_show_key(
+ char *buf,
+ void *val,
+ size_t len )
+{
+ if ( len == 4 /* LUTIL_HASH_BYTES */ ) {
+ unsigned char *c = val;
+ sprintf( buf, "[%02x%02x%02x%02x]", c[0], c[1], c[2], c[3] );
+ return buf;
+ } else {
+ return val;
+ }
+}
+
+/*
+ * idl_intersection - return a = a intersection b
+ */
+int
+wt_idl_intersection(
+ ID *a,
+ ID *b )
+{
+ ID ida, idb;
+ ID idmax, idmin;
+ ID cursora = 0, cursorb = 0, cursorc;
+ int swap = 0;
+
+ if ( WT_IDL_IS_ZERO( a ) || WT_IDL_IS_ZERO( b ) ) {
+ a[0] = 0;
+ return 0;
+ }
+
+ idmin = IDL_MAX( WT_IDL_FIRST(a), WT_IDL_FIRST(b) );
+ idmax = IDL_MIN( WT_IDL_LAST(a), WT_IDL_LAST(b) );
+ if ( idmin > idmax ) {
+ a[0] = 0;
+ return 0;
+ } else if ( idmin == idmax ) {
+ a[0] = 1;
+ a[1] = idmin;
+ return 0;
+ }
+
+ if ( WT_IDL_IS_RANGE( a ) ) {
+ if ( WT_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 ( WT_IDL_IS_RANGE( b )
+ && WT_IDL_RANGE_FIRST( b ) <= WT_IDL_FIRST( a )
+ && WT_IDL_RANGE_LAST( b ) >= WT_IDL_LLAST( 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 = wt_idl_first( a, &cursora );
+ idb = wt_idl_first( b, &cursorb );
+ cursorc = 0;
+
+ while( ida <= idmax || idb <= idmax ) {
+ if( ida == idb ) {
+ a[++cursorc] = ida;
+ ida = wt_idl_next( a, &cursora );
+ idb = wt_idl_next( b, &cursorb );
+ } else if ( ida < idb ) {
+ ida = wt_idl_next( a, &cursora );
+ } else {
+ idb = wt_idl_next( b, &cursorb );
+ }
+ }
+ a[0] = cursorc;
+done:
+ if (swap)
+ WT_IDL_CPY( b, a );
+
+ return 0;
+}
+
+
+/*
+ * idl_union - return a = a union b
+ */
+int
+wt_idl_union(
+ ID *a,
+ ID *b )
+{
+ ID ida, idb;
+ ID cursora = 0, cursorb = 0, cursorc;
+
+ if ( WT_IDL_IS_ZERO( b ) ) {
+ return 0;
+ }
+
+ if ( WT_IDL_IS_ZERO( a ) ) {
+ WT_IDL_CPY( a, b );
+ return 0;
+ }
+
+ if ( WT_IDL_IS_RANGE( a ) || WT_IDL_IS_RANGE(b) ) {
+over: ida = IDL_MIN( WT_IDL_FIRST(a), WT_IDL_FIRST(b) );
+ idb = IDL_MAX( WT_IDL_LAST(a), WT_IDL_LAST(b) );
+ a[0] = NOID;
+ a[1] = ida;
+ a[2] = idb;
+ return 0;
+ }
+
+ ida = wt_idl_first( a, &cursora );
+ idb = wt_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 > WT_IDL_UM_MAX ) {
+ goto over;
+ }
+ b[cursorc] = ida;
+ ida = wt_idl_next( a, &cursora );
+
+ } else {
+ if ( ida == idb )
+ ida = wt_idl_next( a, &cursora );
+ idb = wt_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
+/*
+ * wt_idl_notin - return a intersection ~b (or a minus b)
+ */
+int
+wt_idl_notin(
+ ID *a,
+ ID *b,
+ ID *ids )
+{
+ ID ida, idb;
+ ID cursora = 0, cursorb = 0;
+
+ if( WT_IDL_IS_ZERO( a ) ||
+ WT_IDL_IS_ZERO( b ) ||
+ WT_IDL_IS_RANGE( b ) )
+ {
+ WT_IDL_CPY( ids, a );
+ return 0;
+ }
+
+ if( WT_IDL_IS_RANGE( a ) ) {
+ WT_IDL_CPY( ids, a );
+ return 0;
+ }
+
+ ida = wt_idl_first( a, &cursora ),
+ idb = wt_idl_first( b, &cursorb );
+
+ ids[0] = 0;
+
+ while( ida != NOID ) {
+ if ( idb == NOID ) {
+ /* we could shortcut this */
+ ids[++ids[0]] = ida;
+ ida = wt_idl_next( a, &cursora );
+
+ } else if ( ida < idb ) {
+ ids[++ids[0]] = ida;
+ ida = wt_idl_next( a, &cursora );
+
+ } else if ( ida > idb ) {
+ idb = wt_idl_next( b, &cursorb );
+
+ } else {
+ ida = wt_idl_next( a, &cursora );
+ idb = wt_idl_next( b, &cursorb );
+ }
+ }
+
+ return 0;
+}
+#endif
+
+ID wt_idl_first( ID *ids, ID *cursor )
+{
+ ID pos;
+
+ if ( ids[0] == 0 ) {
+ *cursor = NOID;
+ return NOID;
+ }
+
+ if ( WT_IDL_IS_RANGE( ids ) ) {
+ if( *cursor < ids[1] ) {
+ *cursor = ids[1];
+ }
+ return *cursor;
+ }
+
+ if ( *cursor == 0 )
+ pos = 1;
+ else
+ pos = wt_idl_search( ids, *cursor );
+
+ if( pos > ids[0] ) {
+ return NOID;
+ }
+
+ *cursor = pos;
+ return ids[pos];
+}
+
+ID wt_idl_next( ID *ids, ID *cursor )
+{
+ if ( WT_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 wt_idl_append_one( ID *ids, ID id )
+{
+ if (WT_IDL_IS_RANGE( ids )) {
+ /* if already in range, treat as a dup */
+ if (id >= WT_IDL_RANGE_FIRST(ids) && id <= WT_IDL_RANGE_LAST(ids))
+ return -1;
+ if (id < WT_IDL_RANGE_FIRST(ids))
+ ids[1] = id;
+ else if (id > WT_IDL_RANGE_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] >= WT_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 wt_idl_append( ID *a, ID *b )
+{
+ ID ida, idb, tmp, swap = 0;
+
+ if ( WT_IDL_IS_ZERO( b ) ) {
+ return 0;
+ }
+
+ if ( WT_IDL_IS_ZERO( a ) ) {
+ WT_IDL_CPY( a, b );
+ return 0;
+ }
+
+ ida = WT_IDL_LAST( a );
+ idb = WT_IDL_LAST( b );
+ if ( WT_IDL_IS_RANGE( a ) || WT_IDL_IS_RANGE(b) ||
+ a[0] + b[0] >= WT_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
+wt_idl_sort( ID *ids, ID *tmp )
+{
+ int *istack = (int *)tmp; /* Private stack, not used by caller */
+ int i,j,k,l,ir,jstack;
+ ID a, itmp;
+
+ if ( WT_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-l) {
+ 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 ebackes@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
+wt_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 ( WT_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 occurrences 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-wt/idl.h b/servers/slapd/back-wt/idl.h
new file mode 100644
index 0000000..d323e54
--- /dev/null
+++ b/servers/slapd/back-wt/idl.h
@@ -0,0 +1,80 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#ifndef _WT_IDL_H_
+#define _WT_IDL_H_
+
+/* IDL sizes - likely should be even bigger
+ * limiting factors: sizeof(ID), thread stack size
+ */
+#define WT_IDL_LOGN 16 /* DB_SIZE is 2^16, UM_SIZE is 2^17 */
+#define WT_IDL_DB_SIZE (1<<WT_IDL_LOGN)
+#define WT_IDL_UM_SIZE (1<<(WT_IDL_LOGN+1))
+#define WT_IDL_UM_SIZEOF (WT_IDL_UM_SIZE * sizeof(ID))
+
+#define WT_IDL_DB_MAX (WT_IDL_DB_SIZE-1)
+
+#define WT_IDL_UM_MAX (WT_IDL_UM_SIZE-1)
+
+#define WT_IDL_IS_RANGE(ids) ((ids)[0] == NOID)
+#define WT_IDL_RANGE_SIZE (3)
+#define WT_IDL_RANGE_SIZEOF (WT_IDL_RANGE_SIZE * sizeof(ID))
+#define WT_IDL_SIZEOF(ids) ((WT_IDL_IS_RANGE(ids) \
+ ? WT_IDL_RANGE_SIZE : ((ids)[0]+1)) * sizeof(ID))
+
+#define WT_IDL_RANGE_FIRST(ids) ((ids)[1])
+#define WT_IDL_RANGE_LAST(ids) ((ids)[2])
+
+#define WT_IDL_RANGE( ids, f, l ) \
+ do { \
+ (ids)[0] = NOID; \
+ (ids)[1] = (f); \
+ (ids)[2] = (l); \
+ } while(0)
+
+#define WT_IDL_ZERO(ids) \
+ do { \
+ (ids)[0] = 0; \
+ (ids)[1] = 0; \
+ (ids)[2] = 0; \
+ } while(0)
+
+#define WT_IDL_IS_ZERO(ids) ( (ids)[0] == 0 )
+#define WT_IDL_IS_ALL( range, ids ) ( (ids)[0] == NOID \
+ && (ids)[1] <= (range)[1] && (range)[2] <= (ids)[2] )
+
+#define WT_IDL_CPY( dst, src ) (AC_MEMCPY( dst, src, WT_IDL_SIZEOF( src ) ))
+
+#define WT_IDL_ID( wi, ids, id ) WT_IDL_RANGE( ids, id, ((wi)->wi_lastid) )
+#define WT_IDL_ALL( wi, ids ) WT_IDL_RANGE( ids, 1, ((wi)->wi_lastid) )
+
+#define WT_IDL_FIRST( ids ) ( (ids)[1] )
+#define WT_IDL_LLAST( ids ) ( (ids)[(ids)[0]] )
+#define WT_IDL_LAST( ids ) ( WT_IDL_IS_RANGE(ids) \
+ ? (ids)[2] : (ids)[(ids)[0]] )
+
+#define WT_IDL_N( ids ) ( WT_IDL_IS_RANGE(ids) \
+ ? ((ids)[2]-(ids)[1])+1 : (ids)[0] )
+
+LDAP_BEGIN_DECL
+LDAP_END_DECL
+
+#endif
diff --git a/servers/slapd/back-wt/index.c b/servers/slapd/back-wt/index.c
new file mode 100644
index 0000000..3301977
--- /dev/null
+++ b/servers/slapd/back-wt/index.c
@@ -0,0 +1,423 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include "back-wt.h"
+#include "slap-config.h"
+
+static char presence_keyval[] = {0,0};
+static struct berval presence_key = BER_BVC(presence_keyval);
+
+AttrInfo *wt_index_mask(
+ Backend *be,
+ AttributeDescription *desc,
+ struct berval *atname )
+{
+ AttributeType *at;
+ AttrInfo *ai = wt_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 = wt_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 = wt_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 wt_index_param(
+ Backend *be,
+ AttributeDescription *desc,
+ int ftype,
+ slap_mask_t *maskp,
+ struct berval *prefixp )
+{
+ AttrInfo *ai;
+ slap_mask_t mask, type = 0;
+
+ ai = wt_index_mask( be, desc, prefixp );
+
+ if ( !ai ) {
+ /* TODO: add monitor */
+ 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;
+ *maskp = mask;
+ return LDAP_SUCCESS;
+ }
+ break;
+
+ case LDAP_FILTER_APPROX:
+ type = SLAP_INDEX_APPROX;
+ if ( desc->ad_type->sat_approx ) {
+ if( IS_SLAP_INDEX( mask, SLAP_INDEX_APPROX ) ) {
+ *maskp = mask;
+ return LDAP_SUCCESS;
+ }
+ 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 ) ) {
+ *maskp = mask;
+ return LDAP_SUCCESS;
+ }
+ break;
+
+ case LDAP_FILTER_SUBSTRINGS:
+ type = SLAP_INDEX_SUBSTR;
+ if( IS_SLAP_INDEX( mask, SLAP_INDEX_SUBSTR ) ) {
+ *maskp = mask;
+ return LDAP_SUCCESS;
+ }
+ break;
+
+ default:
+ return LDAP_OTHER;
+ }
+
+ /* TODO: add monitor index */
+ return LDAP_INAPPROPRIATE_MATCHING;
+}
+
+static int indexer(
+ Operation *op,
+ wt_ctx *wc,
+ AttributeDescription *ad,
+ struct berval *atname,
+ BerVarray vals,
+ ID id,
+ int opid,
+ slap_mask_t mask )
+{
+ int rc = LDAP_SUCCESS, i;
+ struct berval *keys;
+ WT_CURSOR *cursor = NULL;
+ assert( mask != 0 );
+
+ cursor = wt_index_open(wc, atname, 1);
+ if( !cursor ) {
+ Debug( LDAP_DEBUG_ANY,
+ "indexer: open index cursor failed: %s\n",
+ atname->bv_val );
+ goto done;
+ }
+
+ if( IS_SLAP_INDEX( mask, SLAP_INDEX_PRESENT ) ) {
+ rc = wt_key_change( op->o_bd, cursor, &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 = wt_key_change( op->o_bd, cursor, &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 = wt_key_change( op->o_bd, cursor, &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 = wt_key_change( op->o_bd, cursor, &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:
+ cursor->close(cursor);
+ return rc;
+}
+
+static int index_at_values(
+ Operation *op,
+ wt_ctx *wc,
+ AttributeDescription *ad,
+ AttributeType *type,
+ struct berval *tags,
+ BerVarray vals,
+ ID id,
+ int opid )
+{
+ int rc = LDAP_SUCCESS;
+ slap_mask_t mask = 0;
+ int ixop = opid;
+ AttrInfo *ai = NULL;
+
+ if ( opid == WT_INDEX_UPDATE_OP )
+ ixop = SLAP_INDEX_ADD_OP;
+
+ if( type->sat_sup ) {
+ /* recurse */
+ rc = index_at_values( op, wc, 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 = wt_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, wc, 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 == WT_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, wc, 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 = wt_attr_mask( op->o_bd->be_private, desc );
+
+ if( ai ) {
+ if ( opid == WT_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, wc, desc, &desc->ad_cname,
+ vals, id, ixop, mask );
+
+ if( rc ) {
+ return rc;
+ }
+ }
+ }
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
+int wt_index_values(
+ Operation *op,
+ wt_ctx *wc,
+ AttributeDescription *desc,
+ BerVarray vals,
+ ID id,
+ int opid )
+{
+ int rc;
+
+ /* Never index ID 0 */
+ if ( id == 0 )
+ return 0;
+
+ rc = index_at_values( op, wc, desc,
+ desc->ad_type, &desc->ad_tags,
+ vals, id, opid );
+
+ return rc;
+}
+
+int
+wt_index_entry( Operation *op, wt_ctx *wc, int opid, Entry *e )
+{
+ int rc;
+ Attribute *ap = e->e_attrs;
+
+ 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 ? e->e_dn : "" );
+
+ for ( ; ap != NULL; ap = ap->a_next ) {
+ rc = wt_index_values( op, wc, 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 ? e->e_dn : "" );
+ return 0;
+}
+
+WT_CURSOR *
+wt_index_open(wt_ctx *wc, struct berval *name, int create)
+{
+ WT_CURSOR *cursor = NULL;
+ WT_SESSION *session = wc->session;
+ char uri[1024];
+ int rc;
+
+ snprintf(uri, sizeof(uri), "table:%s", name->bv_val);
+
+ rc = session->open_cursor(session, uri, NULL, "overwrite=false", &cursor);
+ if (rc == ENOENT && create) {
+ rc = session->create(session, uri,
+ "key_format=uQ,"
+ "value_format=x,"
+ "columns=(key, id, none)");
+ if( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_index_open: table \"%s\": "
+ "cannot create index table: %s (%d)\n",
+ uri, wiredtiger_strerror(rc), rc);
+ return NULL;
+ }
+ rc = session->open_cursor(session, uri, NULL,
+ "overwrite=false", &cursor);
+ }
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_index_open: table \"%s\": "
+ ": open cursor failed: %s (%d)\n",
+ uri, wiredtiger_strerror(rc), rc);
+ return NULL;
+ }
+ return cursor;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/init.c b/servers/slapd/back-wt/init.c
new file mode 100644
index 0000000..cc0c53a
--- /dev/null
+++ b/servers/slapd/back-wt/init.c
@@ -0,0 +1,385 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include "back-wt.h"
+#include "slap-config.h"
+
+static int
+wt_db_init( BackendDB *be, ConfigReply *cr )
+{
+ struct wt_info *wi;
+
+ Debug( LDAP_DEBUG_TRACE, "wt_db_init: Initializing wt backend\n" );
+
+ /* allocate backend-database-specific stuff */
+ wi = ch_calloc( 1, sizeof(struct wt_info) );
+ wi->wi_home = ch_strdup( SLAPD_DEFAULT_DB_DIR );
+ wi->wi_config = ch_calloc( 1, WT_CONFIG_MAX + 1);
+ if ( slapMode & SLAP_TOOL_READONLY ) {
+ strcpy(wi->wi_config, "readonly");
+ } else {
+ strcpy(wi->wi_config, "create");
+ }
+ wi->wi_lastid = 0;
+ wi->wi_search_stack_depth = DEFAULT_SEARCH_STACK_DEPTH;
+ wi->wi_search_stack = NULL;
+ wi->wi_flags = WT_USE_IDLCACHE;
+
+ be->be_private = wi;
+ be->be_cf_ocs = be->bd_info->bi_cf_ocs;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+wt_db_open( BackendDB *be, ConfigReply *cr )
+{
+ struct wt_info *wi = (struct wt_info *) be->be_private;
+ int rc;
+ struct stat st;
+ WT_SESSION *session = NULL;
+ WT_SESSION *cache_session = NULL;
+
+ if ( be->be_suffix == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "wt_db_open: need suffix.\n" );
+ return -1;
+ }
+
+ Debug( LDAP_DEBUG_ARGS,
+ "wt_db_open: \"%s\", home=%s, config=%s\n",
+ be->be_suffix[0].bv_val, wi->wi_home, wi->wi_config );
+
+ /* Check existence of home. Any error means trouble */
+ rc = stat( wi->wi_home, &st );
+ if( rc ) {
+ int saved_errno = errno;
+ Debug( LDAP_DEBUG_ANY,
+ "wt_db_open: database \"%s\": "
+ "cannot access database directory \"%s\" (%d).\n",
+ be->be_suffix[0].bv_val, wi->wi_home, saved_errno );
+ return -1;
+ }
+
+ /* back-wt is always clean */
+ be->be_flags |= SLAP_DBFLAG_CLEAN;
+
+ /* Open and create database */
+ rc = wiredtiger_open(wi->wi_home, NULL,
+ wi->wi_config, &wi->wi_conn);
+ if( rc ) {
+ int saved_errno = errno;
+ Debug( LDAP_DEBUG_ANY,
+ "wt_db_open: database \"%s\": "
+ "cannot open database \"%s\" (%d).\n",
+ be->be_suffix[0].bv_val, wi->wi_home, saved_errno );
+ return -1;
+ }
+
+ rc = wi->wi_conn->open_session(wi->wi_conn, NULL, NULL, &session);
+ if( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_db_open: database \"%s\": "
+ "cannot open session: \"%s\"\n",
+ be->be_suffix[0].bv_val, wiredtiger_strerror(rc) );
+ return -1;
+ }
+
+ if ( slapMode & SLAP_TOOL_READONLY ) {
+ goto readonly;
+ }
+
+ /* checking for obsolete table */
+ rc = session->verify(session, WT_INDEX_REVDN, NULL);
+ if ( !rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_db_open: database \"%s\": "
+ "incompatible wiredtiger table, please restore from LDIF.\n",
+ be->be_suffix[0].bv_val );
+ return -1;
+ }
+
+ /* create tables and indexes */
+ rc = session->create(session,
+ WT_TABLE_ID2ENTRY,
+ "key_format=Q,"
+ "value_format=Su,"
+ "columns=(id,dn,entry)");
+ if( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_db_open: database \"%s\": "
+ "cannot create entry table: \"%s\"\n",
+ be->be_suffix[0].bv_val, wiredtiger_strerror(rc) );
+ return -1;
+ }
+
+ rc = session->create(session,
+ WT_TABLE_DN2ID,
+ "key_format=S,"
+ "value_format=SQQ,"
+ "columns=(revdn,ndn,id,pid)");
+ if( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_db_open: database \"%s\": "
+ "cannot create entry table: \"%s\"\n",
+ be->be_suffix[0].bv_val, wiredtiger_strerror(rc) );
+ return -1;
+ }
+
+ /* not using dn2id index for id2entry table */
+ rc = session->create(session, WT_INDEX_DN, "columns=(dn)");
+ if( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_db_open: database \"%s\": "
+ "cannot create dn index: \"%s\"\n",
+ be->be_suffix[0].bv_val, wiredtiger_strerror(rc) );
+ return -1;
+ }
+
+ rc = session->create(session, WT_INDEX_PID, "columns=(pid)");
+ if( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_db_open: database \"%s\": "
+ "cannot create pid index: \"%s\"\n",
+ be->be_suffix[0].bv_val, wiredtiger_strerror(rc) );
+ return -1;
+ }
+
+ rc = session->create(session, WT_INDEX_NDN, "columns=(ndn)");
+ if( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_db_open: database \"%s\": "
+ "cannot create ndn index: \"%s\"\n",
+ be->be_suffix[0].bv_val, wiredtiger_strerror(rc) );
+ return -1;
+ }
+
+ /* open in-memory database for idlcache */
+ rc = wiredtiger_open(be->be_suffix[0].bv_val, NULL,
+ "in_memory=true", &wi->wi_cache);
+ if( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_db_open: database \"%s\": "
+ "cannot open database for cache (%s).\n",
+ be->be_suffix[0].bv_val, wiredtiger_strerror(rc) );
+ return -1;
+ }
+
+ rc = wi->wi_cache->open_session(wi->wi_cache, NULL, NULL, &cache_session);
+ if( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_db_open: database \"%s\": "
+ "cannot open session for cache: \"%s\"\n",
+ be->be_suffix[0].bv_val, wiredtiger_strerror(rc) );
+ return -1;
+ }
+
+ rc = cache_session->create(cache_session,
+ WT_TABLE_IDLCACHE,
+ "key_format=Sb,"
+ "value_format=u,"
+ "columns=(ndn,scope,idl)");
+ if( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_db_open: database \"%s\": "
+ "cannot create idlcache table: \"%s\"\n",
+ be->be_suffix[0].bv_val, wiredtiger_strerror(rc) );
+ return -1;
+ }
+
+readonly:
+ rc = wt_last_id( be, session, &wi->wi_lastid);
+ if (rc) {
+ snprintf( cr->msg, sizeof(cr->msg), "database \"%s\": "
+ "last_id() failed: %s(%d).",
+ be->be_suffix[0].bv_val, wiredtiger_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY, "wt_db_open: %s\n", cr->msg );
+ return rc;
+ }
+
+ if (session) {
+ session->close(session, NULL);
+ }
+ if (cache_session) {
+ cache_session->close(cache_session, NULL);
+ }
+
+ wi->wi_flags |= WT_IS_OPEN;
+ return LDAP_SUCCESS;
+}
+
+static int
+wt_db_close( BackendDB *be, ConfigReply *cr )
+{
+ struct wt_info *wi = (struct wt_info *) be->be_private;
+ int rc;
+
+ if ( wi->wi_cache ) {
+ rc = wi->wi_cache->close(wi->wi_cache, NULL);
+ if( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_db_close: cannot close cache database (%d).\n", rc );
+ return -1;
+ }
+ }
+
+ if ( wi->wi_conn ) {
+ rc = wi->wi_conn->close(wi->wi_conn, NULL);
+ if( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_db_close: cannot close database (%d).\n", rc );
+ return -1;
+ }
+ wi->wi_flags &= ~WT_IS_OPEN;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+wt_db_destroy( Backend *be, ConfigReply *cr )
+{
+ struct wt_info *wi = (struct wt_info *) be->be_private;
+
+ if( wi->wi_home ) {
+ ch_free( wi->wi_home );
+ wi->wi_home = NULL;
+ }
+
+ if( wi->wi_config ) {
+ ch_free( wi->wi_config );
+ wi->wi_config = NULL;
+ }
+
+ wt_attr_index_destroy( wi );
+ ch_free( wi );
+ be->be_private = NULL;
+
+ return LDAP_SUCCESS;
+}
+
+int
+wt_back_initialize( BackendInfo *bi )
+{
+ static const 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 database system */
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_back_initialize: initialize WiredTiger backend\n" );
+
+ bi->bi_flags |=
+ SLAP_BFLAG_INCREMENT |
+ SLAP_BFLAG_SUBENTRIES |
+ SLAP_BFLAG_ALIASES |
+ SLAP_BFLAG_REFERRALS;
+
+ bi->bi_controls = (char **)controls;
+
+ /* version check */
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_back_initialize: %s\n",
+ wiredtiger_version(NULL, NULL, NULL) );
+
+ bi->bi_open = 0;
+ bi->bi_close = 0;
+ bi->bi_config = 0;
+ bi->bi_destroy = 0;
+
+ bi->bi_db_init = wt_db_init;
+ bi->bi_db_config = config_generic_wrapper;
+ bi->bi_db_open = wt_db_open;
+ bi->bi_db_close = wt_db_close;
+ bi->bi_db_destroy = wt_db_destroy;
+
+ bi->bi_op_add = wt_add;
+ bi->bi_op_bind = wt_bind;
+ bi->bi_op_unbind = 0;
+ bi->bi_op_search = wt_search;
+ bi->bi_op_compare = wt_compare;
+ bi->bi_op_modify = wt_modify;
+ bi->bi_op_modrdn = wt_modrdn;
+ bi->bi_op_delete = wt_delete;
+ bi->bi_op_abandon = 0;
+
+ bi->bi_extended = wt_extended;
+#ifdef LDAP_X_TXN
+ bi->bi_op_txn = 0;
+#endif
+
+ bi->bi_chk_referrals = 0;
+ bi->bi_operational = wt_operational;
+
+ bi->bi_has_subordinates = wt_hasSubordinates;
+ bi->bi_entry_release_rw = wt_entry_release;
+ bi->bi_entry_get_rw = wt_entry_get;
+
+ bi->bi_tool_entry_open = wt_tool_entry_open;
+ bi->bi_tool_entry_close = wt_tool_entry_close;
+ bi->bi_tool_entry_first = backend_tool_entry_first;
+ bi->bi_tool_entry_first_x = wt_tool_entry_first_x;
+ bi->bi_tool_entry_next = wt_tool_entry_next;
+ bi->bi_tool_entry_get = wt_tool_entry_get;
+ bi->bi_tool_entry_put = wt_tool_entry_put;
+ bi->bi_tool_entry_reindex = wt_tool_entry_reindex;
+ bi->bi_tool_sync = 0;
+ bi->bi_tool_dn2id_get = wt_tool_dn2id_get;
+ bi->bi_tool_entry_modify = wt_tool_entry_modify;
+
+#if LDAP_VENDOR_VERSION_MINOR == X || LDAP_VENDOR_VERSION_MINOR >= 5
+ bi->bi_tool_entry_delete = wt_tool_entry_delete;
+#endif
+
+ bi->bi_connection_init = 0;
+ bi->bi_connection_destroy = 0;
+
+ return wt_back_init_cf( bi );
+}
+
+#if SLAPD_WT == SLAPD_MOD_DYNAMIC
+
+/* conditionally define the init_module() function */
+SLAP_BACKEND_INIT_MODULE( wt )
+
+#endif /* SLAPD_WT == SLAPD_MOD_DYNAMIC */
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/key.c b/servers/slapd/back-wt/key.c
new file mode 100644
index 0000000..ea86829
--- /dev/null
+++ b/servers/slapd/back-wt/key.c
@@ -0,0 +1,162 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include "back-wt.h"
+#include "slap-config.h"
+#include "idl.h"
+
+/* read a key */
+int
+wt_key_read(
+ Backend *be,
+ WT_CURSOR *cursor,
+ struct berval *bkey,
+ ID *ids,
+ WT_CURSOR **saved_cursor,
+ int get_flag
+ )
+{
+ int rc;
+ WT_ITEM key;
+ int exact;
+ WT_ITEM key2;
+ ID id;
+ int comp;
+ long scanned = 0;
+
+ Debug( LDAP_DEBUG_TRACE, "=> key_read\n" );
+
+ WT_IDL_ZERO(ids);
+ bv2ITEM(bkey, &key);
+ cursor->set_key(cursor, &key, 0);
+ rc = cursor->search_near(cursor, &exact);
+ switch( rc ){
+ case 0:
+ break;
+ case WT_NOTFOUND:
+ rc = LDAP_SUCCESS;
+ goto done;
+ default:
+ Debug( LDAP_DEBUG_ANY,
+ "wt_key_read: search_near failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc);
+ goto done;
+ }
+ do {
+ scanned++;
+ rc = cursor->get_key(cursor, &key2, &id);
+ if( rc ){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_key_read: get_key failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ break;
+ }
+ comp = 0;
+ if (key.size != key2.size ||
+ (comp = memcmp(key2.data, key.data, key.size))) {
+ if(comp > 0){
+ break;
+ }
+ if(exact < 0){
+ rc = cursor->next(cursor);
+ if (rc) {
+ break;
+ }else{
+ continue;
+ }
+ }
+ break;
+ }
+ exact = 0;
+ wt_idl_append_one(ids, id);
+ rc = cursor->next(cursor);
+ } while(rc == 0);
+
+ if ( rc == WT_NOTFOUND && exact == 0 ) {
+ rc = LDAP_SUCCESS;
+ }
+
+done:
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "<= wt_key_read: failed (%d) %ld scanned\n",
+ rc, scanned );
+ } else {
+ Debug( LDAP_DEBUG_TRACE, "<= wt_key_read %ld candidates %ld scanned\n",
+ (long) WT_IDL_N(ids), scanned );
+ }
+
+ return rc;
+}
+
+/* Add or remove stuff from index files */
+int
+wt_key_change(
+ Backend *be,
+ WT_CURSOR *cursor,
+ struct berval *k,
+ ID id,
+ int op
+)
+{
+ int rc;
+ WT_ITEM item;
+
+ Debug( LDAP_DEBUG_TRACE, "=> key_change(%s,%lx)\n",
+ op == SLAP_INDEX_ADD_OP ? "ADD":"DELETE", (long) id );
+
+ bv2ITEM(k, &item);
+ cursor->set_key(cursor, &item, id);
+ cursor->set_value(cursor, NULL);
+
+ if (op == SLAP_INDEX_ADD_OP) {
+ /* Add values */
+ rc = cursor->insert(cursor);
+ if ( rc == WT_DUPLICATE_KEY ) rc = 0;
+ } else {
+ /* Delete values */
+ rc = cursor->remove(cursor);
+ if ( rc == WT_NOTFOUND ) rc = 0;
+ }
+ if( rc ) {
+ if ( rc != WT_ROLLBACK ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_key_change: error: %s (%d)\n",
+ wiredtiger_strerror(rc), rc);
+ }
+ return rc;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "<= key_change %d\n", rc );
+
+ return rc;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/modify.c b/servers/slapd/back-wt/modify.c
new file mode 100644
index 0000000..d78c438
--- /dev/null
+++ b/servers/slapd/back-wt/modify.c
@@ -0,0 +1,714 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 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
+B * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include "back-wt.h"
+#include "slap-config.h"
+
+static struct berval scbva[] = {
+ BER_BVC("glue"),
+ BER_BVNULL
+};
+
+static void
+wt_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 = wt_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 = wt_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 wt_modify_internal(
+ Operation *op,
+ wt_ctx *wc,
+ 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, "wt_modify_internal: 0x%08lx: %s\n",
+ e->e_id, e->e_dn );
+
+ if ( !acl_check_modlist( op, e, modlist )) {
+ return LDAP_INSUFFICIENT_ACCESS;
+ }
+
+ /* save_attrs will be disposed of by caller */
+ 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,
+ "wt_modify_internal: add %s\n",
+ mod->sm_desc->ad_cname.bv_val );
+ err = modify_add_values( e, mod, get_permissiveModify(op),
+ text, textbuf, textlen );
+ if( err != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_ARGS, "wt_modify_internal: %d %s\n",
+ err, *text );
+ }
+ break;
+
+ case LDAP_MOD_DELETE:
+ if ( glue_attr_delete ) {
+ err = LDAP_SUCCESS;
+ break;
+ }
+
+ Debug( LDAP_DEBUG_ARGS,
+ "wt_modify_internal: delete %s\n",
+ mod->sm_desc->ad_cname.bv_val );
+ err = modify_delete_values( e, mod, get_permissiveModify(op),
+ text, textbuf, textlen );
+ if( err != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_ARGS,
+ "wt_modify_internal: %d %s\n", err, *text );
+ } else {
+ got_delete = 1;
+ }
+ break;
+
+ case LDAP_MOD_REPLACE:
+ Debug( LDAP_DEBUG_ARGS,
+ "wt_modify_internal: replace %s\n",
+ mod->sm_desc->ad_cname.bv_val );
+ err = modify_replace_values( e, mod, get_permissiveModify(op),
+ text, textbuf, textlen );
+ if( err != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_ARGS,
+ "wt_modify_internal: %d %s\n", err, *text );
+ } else {
+ got_delete = 1;
+ }
+ break;
+
+ case LDAP_MOD_INCREMENT:
+ Debug( LDAP_DEBUG_ARGS,
+ "wt_modify_internal: increment %s\n",
+ mod->sm_desc->ad_cname.bv_val );
+ err = modify_increment_values( e, mod, get_permissiveModify(op),
+ text, textbuf, textlen );
+ if( err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ARGS,
+ "wt_modify_internal: %d %s\n", err, *text );
+ } else {
+ got_delete = 1;
+ }
+ break;
+
+ case SLAP_MOD_SOFTADD:
+ Debug( LDAP_DEBUG_ARGS,
+ "wt_modify_internal: softadd %s\n",
+ mod->sm_desc->ad_cname.bv_val );
+ /* 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, "wt_modify_internal: %d %s\n",
+ err, *text );
+ }
+ break;
+
+ case SLAP_MOD_SOFTDEL:
+ Debug( LDAP_DEBUG_ARGS,
+ "wt_modify_internal: softdel %s\n",
+ mod->sm_desc->ad_cname.bv_val );
+ /* 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_SUCCESS ) {
+ got_delete = 1;
+ } else if ( err == LDAP_NO_SUCH_ATTRIBUTE ) {
+ err = LDAP_SUCCESS;
+ }
+
+ if( err != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_ARGS, "wt_modify_internal: %d %s\n",
+ err, *text );
+ }
+ 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,
+ "wt_modify_internal: add_if_not_present %s\n",
+ mod->sm_desc->ad_cname.bv_val );
+ /* 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, "wt_modify_internal: %d %s\n",
+ err, *text );
+ }
+ break;
+
+ default:
+ Debug(LDAP_DEBUG_ANY, "wt_modify_internal: invalid op %d\n",
+ mod->sm_op );
+ *text = "Invalid modify operation";
+ err = LDAP_OTHER;
+ Debug(LDAP_DEBUG_ARGS, "wt_modify_internal: %d %s\n",
+ err, *text );
+ }
+
+ 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 ) {
+ wt_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 );
+ }
+
+ /* 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 ) {
+ wt_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;
+ /* let add know there were deletes */
+ if ( a2->a_flags & SLAP_ATTR_IXADD )
+ a2->a_flags |= SLAP_ATTR_IXDEL;
+ 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 = wt_index_values( op, wc, 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 );
+ 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;
+ if ( ap->a_flags & SLAP_ATTR_IXDEL ) {
+ /* if any values were deleted, we must readd index
+ * for all remaining values.
+ */
+ ap->a_flags &= ~SLAP_ATTR_IXDEL;
+ rc = wt_index_values( op, wc, ap->a_desc, ap->a_nvals,
+ e->e_id, SLAP_INDEX_ADD_OP );
+ } else {
+ int found = 0;
+ /* if this was only an add, we only need to index
+ * the added values.
+ */
+ for ( ml = modlist; ml != NULL; ml = ml->sml_next ) {
+ struct berval *vals;
+ if ( ml->sml_desc != ap->a_desc || !ml->sml_numvals )
+ continue;
+ found = 1;
+ switch( ml->sml_op ) {
+ case LDAP_MOD_ADD:
+ case LDAP_MOD_REPLACE:
+ case LDAP_MOD_INCREMENT:
+ case SLAP_MOD_SOFTADD:
+ case SLAP_MOD_ADD_IF_NOT_PRESENT:
+ if ( ml->sml_op == LDAP_MOD_INCREMENT )
+ vals = ap->a_nvals;
+ else if ( ml->sml_nvalues )
+ vals = ml->sml_nvalues;
+ else
+ vals = ml->sml_values;
+ rc = wt_index_values( op, wc, ap->a_desc,
+ vals, e->e_id, SLAP_INDEX_ADD_OP );
+ break;
+ }
+ if ( rc )
+ break;
+ }
+ /* This attr was affected by a modify of a subtype, so
+ * there was no direct match in the modlist. Just readd
+ * all of its values.
+ */
+ if ( !found ) {
+ rc = wt_index_values( op, wc, 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 );
+ attrs_free( e->e_attrs );
+ e->e_attrs = save_attrs;
+ return rc;
+ }
+ }
+ }
+
+ return rc;
+}
+
+int
+wt_modify( Operation *op, SlapReply *rs )
+{
+ struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+ wt_ctx *wc = NULL;
+ Entry *e = NULL;
+ int manageDSAit = get_manageDSAit( op );
+ char textbuf[SLAP_TEXT_BUFLEN];
+ size_t textlen = sizeof textbuf;
+ Entry dummy = {0};
+
+ LDAPControl **preread_ctrl = NULL;
+ LDAPControl **postread_ctrl = NULL;
+ LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
+ int num_ctrls = 0;
+
+ int rc;
+
+ Debug( LDAP_DEBUG_ARGS, "wt_modify: %s\n", op->o_req_dn.bv_val );
+
+#ifdef LDAP_X_TXN
+ if( op->o_txnSpec && txn_preop( op, rs ))
+ return rs->sr_err;
+#endif
+
+ ctrls[num_ctrls] = NULL;
+
+ wc = wt_ctx_get(op, wi);
+ if( !wc ){
+ Debug( LDAP_DEBUG_ANY, "wt_modify: wt_ctx_get failed\n" );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ send_ldap_result( op, rs );
+ return rs->sr_err;
+ }
+
+ /* 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 );
+ }
+
+retry:
+ /* begin transaction */
+ wc->is_begin_transaction = 0;
+ rc = wc->session->begin_transaction(wc->session, "isolation=snapshot");
+ if( rc ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_modify: begin_transaction failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "begin_transaction failed";
+ goto return_results;
+ }
+ wc->is_begin_transaction = 1;
+ Debug( LDAP_DEBUG_TRACE, "wt_modify: session id: %p\n", wc->session );
+
+ /* get entry */
+ rc = wt_dn2entry(op->o_bd, wc, &op->o_req_ndn, &e);
+ switch( rc ) {
+ case 0:
+ break;
+ case WT_NOTFOUND:
+ break;
+ default:
+ Debug( LDAP_DEBUG_ANY,
+ "<== wt_modify: wt_dn2entry failed (%d)\n", rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ if ( rc == WT_NOTFOUND ||
+ ( !manageDSAit && e && is_entry_glue( e ))) {
+ if ( !e ) {
+ rc = wt_dn2aentry(op->o_bd, wc, &op->o_req_ndn, &e);
+ switch( rc ) {
+ case 0:
+ break;
+ case WT_NOTFOUND:
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ goto return_results;
+ default:
+ Debug( LDAP_DEBUG_ANY, "wt_modify: wt_dna2entry failed (%d)\n", rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+ }
+
+ 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_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, "wt_modify: entry is referral\n" );
+
+ 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,
+ "<=- wt_modify: pre-read failed!\n" );
+ 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 = wt_modify_internal( op, wc, op->orm_modlist,
+ &dummy, &rs->sr_text, textbuf, textlen );
+ switch ( rs->sr_err ) {
+ case LDAP_SUCCESS:
+ break;
+ case WT_ROLLBACK:
+ Debug (LDAP_DEBUG_TRACE, "wt_modify: rollback wt_modify_internal failed.\n" );
+ wc->session->rollback_transaction(wc->session, NULL);
+ goto retry;
+ default:
+ Debug( LDAP_DEBUG_ANY, "wt_modify: modify failed (%d)\n", rs->sr_err );
+ /* 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 = wt_id2entry_update( op, wc, &dummy );
+ switch ( rs->sr_err ) {
+ case 0:
+ break;
+ case WT_ROLLBACK:
+ Debug (LDAP_DEBUG_TRACE, "wt_modify: rollback wt_id2entry_update failed.\n");
+ wc->session->rollback_transaction(wc->session, NULL);
+ goto retry;
+ case LDAP_ADMINLIMIT_EXCEEDED:
+ Debug( LDAP_DEBUG_ANY, "wt_modify: id2entry update failed (%d)\n",
+ rs->sr_err);
+ rs->sr_text = "entry too big";
+ goto return_results;
+ default:
+ Debug( LDAP_DEBUG_ANY, "wt_modify: id2entry update failed (%d)\n",
+ rs->sr_err);
+ rs->sr_err = LDAP_OTHER;
+ 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,
+ "<== wt_modify: post-read failed!\n");
+ if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
+ /* FIXME: is it correct to abort
+ * operation if control fails? */
+ goto return_results;
+ }
+ }
+ }
+
+ if( op->o_noop ) {
+ rs->sr_err = LDAP_X_NO_OPERATION;
+ goto return_results;
+ }
+
+ /* Only free attrs if they were dup'd. */
+ if ( dummy.e_attrs == e->e_attrs ) dummy.e_attrs = NULL;
+
+ rc = wc->session->commit_transaction(wc->session, NULL);
+ wc->is_begin_transaction = 0;
+ if( rc ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<== wt_modify: commit failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "commit failed";
+ goto return_results;
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_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 );
+
+done:
+ slap_graduate_commit_csn( op );
+
+ if( wc && wc->is_begin_transaction ){
+ Debug( LDAP_DEBUG_TRACE, "wt_modify: rollback transaction\n" );
+ wc->session->rollback_transaction(wc->session, NULL);
+ wc->is_begin_transaction = 0;
+ }
+
+ if( e != NULL ) {
+ wt_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 );
+ }
+
+ return rs->sr_err;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/modrdn.c b/servers/slapd/back-wt/modrdn.c
new file mode 100644
index 0000000..faef948
--- /dev/null
+++ b/servers/slapd/back-wt/modrdn.c
@@ -0,0 +1,552 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include "back-wt.h"
+#include "slap-config.h"
+
+int
+wt_modrdn( Operation *op, SlapReply *rs )
+{
+ struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+ AttributeDescription *children = slap_schema.si_ad_children;
+ AttributeDescription *entry = slap_schema.si_ad_entry;
+ wt_ctx *wc = NULL;
+ Entry *e = NULL;
+ Entry *p = NULL;
+ Entry *ne = NULL;
+ Entry dummy = {0};
+
+ struct berval p_dn, p_ndn;
+ struct berval new_dn = {0, NULL}, new_ndn = {0, NULL};
+
+ 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 );
+ char textbuf[SLAP_TEXT_BUFLEN];
+ size_t textlen = sizeof textbuf;
+ LDAPControl **preread_ctrl = NULL;
+ LDAPControl **postread_ctrl = NULL;
+ LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
+ int num_ctrls = 0;
+
+ int rc;
+
+ int parent_is_glue = 0;
+ int parent_is_leaf = 0;
+
+ Debug( LDAP_DEBUG_TRACE, "==> wt_modrdn(%s -> newrdn=%s - newsup=%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" );
+
+ ctrls[num_ctrls] = NULL;
+
+ slap_mods_opattrs( op, &op->orr_modlist, 1 );
+
+ wc = wt_ctx_get(op, wi);
+ if( !wc ){
+ Debug( LDAP_DEBUG_ANY, "wt_modrdn: wt_ctx_get failed\n");
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ send_ldap_result( op, rs );
+ return rs->sr_err;
+ }
+
+ /* get parent entry */
+ if ( be_issuffix( op->o_bd, &op->o_req_ndn ) ) {
+ rs->sr_err = LDAP_NAMING_VIOLATION;
+ rs->sr_text = "cannot rename suffix entry";
+ goto return_results;
+ } else {
+ dnParent( &op->o_req_ndn, &p_ndn );
+ }
+
+ rc = wt_dn2entry(op->o_bd, wc, &p_ndn, &p);
+ switch( rc ) {
+ case 0:
+ break;
+ case WT_NOTFOUND:
+ Debug( LDAP_DEBUG_ARGS,
+ "<== wt_modrdn: parent does not exist %s\n", p_ndn.bv_val);
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ goto return_results;
+ default:
+ Debug( LDAP_DEBUG_ANY,
+ "<== wt_modrdn: wt_dn2entry failed (%d)\n", rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ /* check parent for "children" acl */
+ rc = access_allowed( op, p, children, NULL,
+ op->oq_modrdn.rs_newSup == NULL ?
+ ACL_WRITE : ACL_WDEL, NULL );
+
+ if ( !rc ) {
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_modrdn: no access to parent\n");
+ rs->sr_text = "no write access to old parent's children";
+ goto return_results;
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_modrdn: wr to children of entry %s OK\n", p_ndn.bv_val );
+
+ 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,
+ "wt_modrdn: parent dn=%s\n", p_dn.bv_val );
+
+ /* get entry */
+ rc = wt_dn2entry(op->o_bd, wc, &op->o_req_ndn, &e);
+ switch( rc ) {
+ case 0:
+ break;
+ case WT_NOTFOUND:
+ break;
+ default:
+ Debug( LDAP_DEBUG_ANY,
+ "<== wt_modrdn: wt_dn2entry failed (%d)\n", rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ if ( rc == WT_NOTFOUND ||
+ ( !manageDSAit && e && is_entry_glue( e ) )) {
+
+ if ( !e ) {
+ Debug( LDAP_DEBUG_ARGS,
+ "<== wt_modrdn: no such object %s\n", op->o_req_dn.bv_val);
+ rc = wt_dn2aentry(op->o_bd, wc, &op->o_req_ndn, &e);
+ switch( rc ) {
+ case 0:
+ break;
+ case WT_NOTFOUND:
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ goto return_results;
+ default:
+ Debug( LDAP_DEBUG_ANY, "wt_modrdn: wt_dn2aentry failed (%d)\n", rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+ }
+
+ 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_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
+ rs->sr_err = LDAP_REFERRAL;
+ send_ldap_result( op, rs );
+ 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 */
+ rc = access_allowed( op, e, entry, NULL, ACL_WRITE, NULL );
+ if ( !rc ) {
+ Debug( LDAP_DEBUG_TRACE, "wt_modrdn: no access to entry\n");
+ rs->sr_text = "no write access to old entry";
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ goto return_results;
+ }
+
+ /* Can't do it if we have kids */
+ rc = wt_dn2id_has_children( op, wc, e->e_id );
+ if( rc != WT_NOTFOUND ) {
+ switch( rc ) {
+ case 0:
+ Debug(LDAP_DEBUG_ARGS, "<== wt_modrdn: non-leaf %s\n", op->o_req_dn.bv_val);
+ rs->sr_err = LDAP_NOT_ALLOWED_ON_NONLEAF;
+ rs->sr_text = "subtree rename not supported";
+ break;
+ default:
+ Debug(LDAP_DEBUG_ARGS,
+ "<== wt_modrdn: has_children failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ }
+ goto return_results;
+ }
+
+ if (!manageDSAit && is_entry_referral( e ) ) {
+ /* parent is a referral, don't allow add */
+ rs->sr_ref = get_entry_referrals( op, e );
+
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_modrdn: entry %s is referral\n", e->e_dn );
+
+ 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,
+ "wt_modrdn: new parent \"%s\" requested...\n",
+ op->oq_modrdn.rs_newSup->bv_val );
+
+ /* newSuperior == oldParent? */
+ if( dn_match( &p_ndn, op->oq_modrdn.rs_nnewSup ) ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_modrdn: new parent \"%s\" same as the old parent \"%s\"\n",
+ op->oq_modrdn.rs_newSup->bv_val, p_dn.bv_val );
+ op->oq_modrdn.rs_newSup = NULL; /* ignore newSuperior */
+ }
+ }
+
+ 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? */
+ rc = wt_dn2entry(op->o_bd, wc, np_ndn, &np);
+ switch( rc ) {
+ case 0:
+ break;
+ case WT_NOTFOUND:
+ Debug( LDAP_DEBUG_ANY,
+ "<== wt_modrdn: new superior not found: %s\n",
+ np_ndn->bv_val );
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ rs->sr_text = "new superior not found";
+ goto return_results;
+ default:
+ Debug( LDAP_DEBUG_ANY,
+ "<== wt_modrdn: wt_dn2entry failed %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_modrdn: wr to new parent OK np=%p, id=%ld\n",
+ (void *) np, (long) np->e_id );
+ rs->sr_err = access_allowed( op, np, children,
+ NULL, ACL_WADD, NULL );
+ if( ! rs->sr_err ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_modrdn: no wr to newSup children\n" );
+ rs->sr_text = "no write access to new superior's children";
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ goto return_results;
+ }
+ if ( is_entry_alias( np ) ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_modrdn: entry is alias\n" );
+ 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,
+ "wt_modrdn: entry is referral\n" );
+ rs->sr_text = "new superior is a referral";
+ rs->sr_err = LDAP_OTHER;
+ goto return_results;
+ }
+ } else {
+ /* no parent, modrdn entry directly under root */
+ /* TODO: */
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_modrdn: no parent, not implement yet\n" );
+ rs->sr_text = "not implement yet";
+ rs->sr_err = LDAP_OTHER;
+ goto return_results;
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_modrdn: wr to new parent's children OK\n" );
+ 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, NULL );
+ }
+
+ if (!new_ndn.bv_val) {
+ struct berval bv = {0, NULL};
+ dnNormalize( 0, NULL, NULL, &new_dn, &bv, op->o_tmpmemctx );
+ ber_dupbv( &new_ndn, &bv );
+ /* FIXME: why not call dnNormalize() w/o ctx? */
+ op->o_tmpfree( bv.bv_val, op->o_tmpmemctx );
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_modrdn: new ndn=%s\n", new_ndn.bv_val );
+
+ /* check new entry */
+ rc = wt_dn2entry(op->o_bd, wc, &new_ndn, &ne);
+ switch( rc ) {
+ case 0:
+ /* Allow rename to same DN */
+ if(e->e_id == ne->e_id){
+ break;
+ }
+ rs->sr_err = LDAP_ALREADY_EXISTS;
+ goto return_results;
+ break;
+ case WT_NOTFOUND:
+ break;
+ default:
+ Debug( LDAP_DEBUG_ANY,
+ "<== wt_modrdn: wt_dn2entry failed %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ 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,
+ "<== wt_modrdn: pre-read failed!\n" );
+ if ( op->o_preread & SLAP_CONTROL_CRITICAL ) {
+ /* FIXME: is it correct to abort
+ * operation if control fails? */
+ goto return_results;
+ }
+ }
+ }
+
+ /* begin transaction */
+ rc = wc->session->begin_transaction(wc->session, NULL);
+ if( rc ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_modrdn: begin_transaction failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "begin_transaction failed";
+ goto return_results;
+ }
+ wc->is_begin_transaction = 1;
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_modrdn: session id: %p\n", wc->session );
+
+ /* delete old DN */
+ rc = wt_dn2id_delete( op, wc, &e->e_nname);
+ if ( rc ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "<== wt_modrdn: delete failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "dn2id delete failed";
+ 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 */
+ rc = wt_dn2id_add( op, wc, np?np->e_id:p->e_id, &dummy );
+ if ( rc ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "<== wt_modrdn: add failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "DN add failed";
+ goto return_results;
+ }
+ dummy.e_attrs = e->e_attrs;
+
+ rc = wt_modify_internal( op, wc, op->orm_modlist,
+ &dummy, &rs->sr_text, textbuf, textlen );
+ if( rc != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "<== wt_modrdn: modify failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ if ( dummy.e_attrs == e->e_attrs ) dummy.e_attrs = NULL;
+ goto return_results;
+ }
+
+ /* update entry */
+ rc = wt_id2entry_update( op, wc, &dummy );
+ if ( rc != 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_modrdn: id2entry update failed(%d)\n", rc );
+ if ( rc == LDAP_ADMINLIMIT_EXCEEDED ) {
+ rs->sr_text = "entry too big";
+ } else {
+ 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);
+ /* TODO: glue entry handling */
+ }
+
+ 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,
+ "<== wt_modrdn: post-read failed!\n" );
+ if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
+ /* FIXME: is it correct to abort
+ * operation if control fails? */
+ goto return_results;
+ }
+ }
+ }
+
+ if( op->o_noop ) {
+ rs->sr_err = LDAP_X_NO_OPERATION;
+ goto return_results;
+ }
+
+ rc = wc->session->commit_transaction(wc->session, NULL);
+ wc->is_begin_transaction = 0;
+ if( rc ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<== wt_modrdn: commit failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "commit failed";
+ goto return_results;
+ }
+
+ Debug(LDAP_DEBUG_TRACE,
+ "wt_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_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 ( rs->sr_err == LDAP_SUCCESS && parent_is_glue && parent_is_leaf ) {
+ op->o_delete_glue_parent = 1;
+ }
+
+done:
+ if( wc && wc->is_begin_transaction ){
+ Debug( LDAP_DEBUG_TRACE, "wt_modrdn: rollback transaction\n" );
+ wc->session->rollback_transaction(wc->session, NULL);
+ wc->is_begin_transaction = 0;
+ }
+
+ slap_graduate_commit_csn( op );
+
+ if( new_dn.bv_val != NULL ) free( new_dn.bv_val );
+ if( new_ndn.bv_val != NULL ) free( new_ndn.bv_val );
+
+ /* free entry */
+ if( e != NULL ) {
+ wt_entry_return( e );
+ }
+ /* free parent entry */
+ if( p != NULL ) {
+ wt_entry_return( p );
+ }
+ /* free new entry */
+ if( ne != NULL ) {
+ wt_entry_return( ne );
+ }
+ /* free new parent entry */
+ if( np != NULL ) {
+ wt_entry_return( np );
+ }
+
+ 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;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/nextid.c b/servers/slapd/back-wt/nextid.c
new file mode 100644
index 0000000..99620e9
--- /dev/null
+++ b/servers/slapd/back-wt/nextid.c
@@ -0,0 +1,88 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include "back-wt.h"
+#include "slap-config.h"
+
+int wt_next_id(BackendDB *be, ID *out){
+ struct wt_info *wi = (struct wt_info *) be->be_private;
+ *out = __sync_add_and_fetch(&wi->wi_lastid, 1);
+ return 0;
+}
+
+int wt_last_id( BackendDB *be, WT_SESSION *session, ID *out )
+{
+ WT_CURSOR *cursor;
+ int rc;
+ uint64_t id;
+
+ rc = session->open_cursor(session, WT_TABLE_ID2ENTRY, NULL, NULL, &cursor);
+ if(rc){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_last_id: open_cursor failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ return rc;
+ }
+
+ rc = cursor->prev(cursor);
+ switch(rc) {
+ case 0:
+ rc = cursor->get_key(cursor, &id);
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_last_id: get_key failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ return rc;
+ }
+ *out = id;
+ break;
+ case WT_NOTFOUND:
+ /* no entry */
+ *out = 0;
+ break;
+ default:
+ Debug( LDAP_DEBUG_ANY,
+ "wt_last_id: prev failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ }
+
+ rc = cursor->close(cursor);
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_last_id: close failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ return rc;
+ }
+
+ return 0;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/operational.c b/servers/slapd/back-wt/operational.c
new file mode 100644
index 0000000..df9d0c6
--- /dev/null
+++ b/servers/slapd/back-wt/operational.c
@@ -0,0 +1,110 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-wt.h"
+#include "slap-config.h"
+
+int
+wt_hasSubordinates(
+ Operation *op,
+ Entry *e,
+ int *hasSubordinates )
+{
+ struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+ wt_ctx *wc = NULL;
+ int rc;
+
+ assert( e != NULL );
+
+ wc = wt_ctx_get(op, wi);
+ if( !wc ){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_hasSubordinates: wt_ctx_get failed\n" );
+ return LDAP_OTHER;
+ }
+
+ rc = wt_dn2id_has_children(op, wc, e->e_id);
+ switch(rc){
+ case 0:
+ *hasSubordinates = LDAP_COMPARE_TRUE;
+ break;
+ case WT_NOTFOUND:
+ *hasSubordinates = LDAP_COMPARE_FALSE;
+ rc = LDAP_SUCCESS;
+ break;
+ default:
+ Debug(LDAP_DEBUG_ANY,
+ "<=- wt_hasSubordinates: has_children failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ rc = LDAP_OTHER;
+ }
+ return rc;
+}
+
+/*
+ * sets the supported operational attributes (if required)
+ */
+int
+wt_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 = wt_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;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/proto-wt.h b/servers/slapd/back-wt/proto-wt.h
new file mode 100644
index 0000000..7fa2666
--- /dev/null
+++ b/servers/slapd/back-wt/proto-wt.h
@@ -0,0 +1,268 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#ifndef _PROTO_WT_H_
+#define _PROTO_WT_H_
+
+LDAP_BEGIN_DECL
+
+#define WT_UCTYPE "WT"
+
+/*
+ * attr.c
+ */
+
+AttrInfo *wt_attr_mask( struct wt_info *wi, AttributeDescription *desc );
+void wt_attr_flush( struct wt_info *wi );
+void wt_attr_index_unparse( struct wt_info *wi, BerVarray *bva );
+int wt_attr_index_config(
+ struct wt_info *wi,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv,
+ struct config_reply_s *c_reply);
+void wt_attr_index_destroy( struct wt_info *wi );
+
+/*
+ * id2entry.c
+ */
+int wt_id2entry(BackendDB *be, wt_ctx *wc, ID id, Entry **ep );
+int wt_id2entry_add(Operation *op, wt_ctx *wc, Entry *e );
+int wt_id2entry_update(Operation *op, wt_ctx *wc, Entry *e );
+int wt_id2entry_delete(Operation *op, wt_ctx *wc, Entry *e );
+
+BI_entry_release_rw wt_entry_release;
+BI_entry_get_rw wt_entry_get;
+
+int wt_entry_return(Entry *e);
+int wt_entry_release(Operation *op, Entry *e, int rw);
+
+/*
+ * idl.c
+ */
+
+unsigned wt_idl_search( ID *ids, ID id );
+
+ID wt_idl_first( ID *ids, ID *cursor );
+ID wt_idl_next( ID *ids, ID *cursor );
+int wt_idl_append_one( ID *ids, ID id );
+void wt_idl_sort( ID *ids, ID *tmp );
+int wt_idl_intersection( ID *a, ID *b );
+int wt_filter_candidates(
+ Operation *op,
+ wt_ctx *wc,
+ Filter *f,
+ ID *ids,
+ ID *tmp,
+ ID *stack );
+int
+wt_idl_union(
+ ID *a,
+ ID *b );
+
+/*
+ * index.c
+ */
+
+extern AttrInfo *
+wt_index_mask LDAP_P((
+ Backend *be,
+ AttributeDescription *desc,
+ struct berval *atname ));
+
+int wt_index_entry LDAP_P(( Operation *op, wt_ctx *wc, int r, Entry *e ));
+int wt_index_values(
+ Operation *op,
+ wt_ctx *wc,
+ AttributeDescription *desc,
+ BerVarray vals,
+ ID id,
+ int opid );
+int wt_index_param(
+ Backend *be,
+ AttributeDescription *desc,
+ int ftype,
+ slap_mask_t *maskp,
+ struct berval *prefixp );
+
+WT_CURSOR *wt_index_open(wt_ctx *wc, struct berval *name, int create);
+
+#define wt_index_entry_add(op,t,e) \
+ wt_index_entry((op),(t),SLAP_INDEX_ADD_OP,(e))
+#define wt_index_entry_del(op,t,e) \
+ wt_index_entry((op),(t),SLAP_INDEX_DELETE_OP,(e))
+
+/*
+ * key.c
+ */
+int
+wt_key_read( Backend *be,
+ WT_CURSOR *cursor,
+ struct berval *k,
+ ID *ids,
+ WT_CURSOR **saved_cursor,
+ int get_flag);
+
+int
+wt_key_change( Backend *be,
+ WT_CURSOR *cursor,
+ struct berval *k,
+ ID id,
+ int op);
+
+/*
+ * nextid.c
+ */
+int wt_next_id(BackendDB *be, ID *out);
+int wt_last_id( BackendDB *be, WT_SESSION *session, ID *out );
+
+/*
+ * modify.c
+ */
+int wt_modify_internal(
+ Operation *op,
+ wt_ctx *wc,
+ Modifications *modlist,
+ Entry *e,
+ const char **text,
+ char *textbuf,
+ size_t textlen );
+
+/*
+ * config.c
+ */
+int wt_back_init_cf( BackendInfo *bi );
+
+/*
+ * dn2id.c
+ */
+
+int
+wt_dn2id(
+ Operation *op,
+ wt_ctx *wc,
+ struct berval *ndn,
+ ID *id);
+
+int
+wt_dn2id_add(
+ Operation *op,
+ wt_ctx *wc,
+ ID pid,
+ Entry *e);
+
+int
+wt_dn2idl(
+ Operation *op,
+ wt_ctx *wc,
+ struct berval *ndn,
+ Entry *e,
+ ID *ids,
+ ID *stack);
+
+int
+wt_dn2id_delete(
+ Operation *op,
+ wt_ctx *wc,
+ struct berval *ndn);
+
+int
+wt_dn2id_has_children(
+ Operation *op,
+ wt_ctx *wc,
+ ID id );
+
+/*
+ * dn2entry.c
+ */
+int wt_dn2entry( BackendDB *be,
+ wt_ctx *wc,
+ struct berval *ndn,
+ Entry **ep );
+
+int wt_dn2pentry( BackendDB *be,
+ wt_ctx *wc,
+ struct berval *ndn,
+ Entry **ep );
+int wt_dn2aentry( BackendDB *be,
+ wt_ctx *wc,
+ struct berval *ndn,
+ Entry **ep );
+
+/*
+ * former ctx.c
+ */
+wt_ctx *wt_ctx_init(struct wt_info *wi);
+void wt_ctx_free(void *key, void *data);
+wt_ctx *wt_ctx_get(Operation *op, struct wt_info *wi);
+
+/*
+ * former cache.c
+ */
+int wt_idlcache_get(wt_ctx *wc, struct berval *ndn, int scope, ID *ids);
+int wt_idlcache_set(wt_ctx *wc, struct berval *ndn, int scope, ID *ids);
+int wt_idlcache_begin(wt_ctx *wc, struct berval *ndn, int scope);
+int wt_idlcache_clear(Operation *op, wt_ctx *wc, struct berval *ndn);
+
+/*
+ * former external.h
+ */
+
+extern BI_init wt_back_initialize;
+extern BI_db_config wt_db_config;
+extern BI_op_add wt_add;
+extern BI_op_bind wt_bind;
+extern BI_op_compare wt_compare;
+extern BI_op_delete wt_delete;
+extern BI_op_modify wt_modify;
+extern BI_op_modrdn wt_modrdn;
+extern BI_op_search wt_search;
+extern BI_op_extended wt_extended;
+
+extern BI_operational wt_operational;
+extern BI_has_subordinates wt_hasSubordinates;
+
+/* tools.c */
+int wt_entry_header(WT_ITEM *item, EntryHeader *eh);
+extern BI_tool_entry_open wt_tool_entry_open;
+extern BI_tool_entry_close wt_tool_entry_close;
+extern BI_tool_entry_first_x wt_tool_entry_first_x;
+extern BI_tool_entry_next wt_tool_entry_next;
+extern BI_tool_entry_get wt_tool_entry_get;
+extern BI_tool_entry_put wt_tool_entry_put;
+extern BI_tool_entry_reindex wt_tool_entry_reindex;
+extern BI_tool_dn2id_get wt_tool_dn2id_get;
+extern BI_tool_entry_modify wt_tool_entry_modify;
+extern BI_tool_entry_delete wt_tool_entry_delete;
+
+LDAP_END_DECL
+
+#endif /* _PROTO_WT_H */
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
diff --git a/servers/slapd/back-wt/search.c b/servers/slapd/back-wt/search.c
new file mode 100644
index 0000000..8a313b3
--- /dev/null
+++ b/servers/slapd/back-wt/search.c
@@ -0,0 +1,759 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-wt.h"
+#include "idl.h"
+
+static int search_aliases(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e,
+ WT_SESSION *session,
+ ID *ids,
+ ID *scopes,
+ ID *stack )
+{
+ /* TODO: search_aliases does not implement yet. */
+ WT_IDL_ZERO( ids );
+ return 0;
+}
+
+static int base_candidate(
+ BackendDB *be,
+ Entry *e,
+ ID *ids )
+{
+ Debug(LDAP_DEBUG_ARGS,
+ "base_candidate: base: \"%s\" (0x%08lx)\n",
+ e->e_nname.bv_val, (long) e->e_id );
+
+ 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 wt_info *wi = (struct wt_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 = wi->wi_search_stack;
+ }
+
+ if ( !ret ) {
+ ret = ch_malloc( wi->wi_search_stack_depth * WT_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 {
+ wi->wi_search_stack = ret;
+ }
+ }
+ return ret;
+}
+
+static int search_candidates(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e,
+ wt_ctx *wc,
+ ID *ids,
+ ID *scopes )
+{
+ struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+ int rc, depth = 1;
+ Filter f, rf, xf, nf;
+ ID *stack;
+ AttributeAssertion aa_ref = ATTRIBUTEASSERTION_INIT;
+ Filter sf;
+ AttributeAssertion aa_subentry = ATTRIBUTEASSERTION_INIT;
+
+ Debug(LDAP_DEBUG_TRACE,
+ "wt_search_candidates: base=\"%s\" (0x%08lx) scope=%d\n",
+ e->e_nname.bv_val, (long) e->e_id, op->oq_search.rs_scope );
+
+ xf.f_or = op->oq_search.rs_filter;
+ xf.f_choice = LDAP_FILTER_OR;
+ xf.f_next = NULL;
+
+ /* 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 = xf.f_or;
+ xf.f_or = &rf;
+ depth++;
+ }
+ }
+
+ f.f_next = NULL;
+ f.f_choice = LDAP_FILTER_AND;
+ f.f_and = &nf;
+ /* Dummy; we compute scope separately now */
+ nf.f_choice = SLAPD_FILTER_COMPUTED;
+ nf.f_result = LDAP_SUCCESS;
+ nf.f_next = ( xf.f_or == op->oq_search.rs_filter )
+ ? op->oq_search.rs_filter : &xf ;
+ /* Filter depth increased again, adding dummy clause */
+ 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 = nf.f_next;
+ nf.f_next = &sf;
+ }
+
+ /* Allocate IDL stack, plus 1 more for former tmp */
+ if ( depth+1 > wi->wi_search_stack_depth ) {
+ stack = ch_malloc( (depth + 1) * WT_IDL_UM_SIZE * sizeof( ID ) );
+ } else {
+ stack = search_stack( op );
+ }
+
+ if( op->ors_deref & LDAP_DEREF_SEARCHING ) {
+ rc = search_aliases( op, rs, e, wc->session, ids, scopes, stack );
+ if ( WT_IDL_IS_ZERO( ids ) && rc == LDAP_SUCCESS )
+ rc = wt_dn2idl( op, wc, &e->e_nname, e, ids, stack );
+ } else {
+ rc = wt_dn2idl(op, wc, &e->e_nname, e, ids, stack );
+ }
+
+ if ( rc == LDAP_SUCCESS ) {
+ rc = wt_filter_candidates( op, wc, &f, ids,
+ stack, stack+WT_IDL_UM_SIZE );
+ }
+
+ if ( depth+1 > wi->wi_search_stack_depth ) {
+ ch_free( stack );
+ }
+
+ if( rc ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "wt_search_candidates: failed (rc=%d)\n", rc );
+
+ } else {
+ Debug(LDAP_DEBUG_TRACE,
+ "wt_search_candidates: id=%ld first=%ld last=%ld\n",
+ (long) ids[0],
+ (long) WT_IDL_FIRST(ids),
+ (long) WT_IDL_LAST(ids));
+ }
+ return 0;
+}
+
+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;
+ }
+
+ 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 );
+
+ 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 );
+}
+
+int
+wt_search( Operation *op, SlapReply *rs )
+{
+ struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+ ID id, cursor;
+ ID lastid = NOID;
+ int manageDSAit;
+ wt_ctx *wc;
+ int rc = LDAP_OTHER;
+ Entry *e = NULL;
+ Entry *ae = NULL;
+ Entry *base = NULL;
+ slap_mask_t mask;
+ time_t stoptime;
+
+ ID candidates[WT_IDL_UM_SIZE];
+ ID scopes[WT_IDL_DB_SIZE];
+ int tentries = 0;
+ unsigned nentries = 0;
+
+ Debug( LDAP_DEBUG_ARGS, "==> wt_search: %s\n", op->o_req_dn.bv_val );
+
+ manageDSAit = get_manageDSAit( op );
+
+ wc = wt_ctx_get(op, wi);
+ if( !wc ){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_search: wt_ctx_get failed: %d\n", rc );
+ send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
+ return rc;
+ }
+
+ /* get entry */
+ rc = wt_dn2entry(op->o_bd, wc, &op->o_req_ndn, &e);
+ switch( rc ) {
+ case 0:
+ break;
+ case WT_NOTFOUND:
+ rc = wt_dn2aentry(op->o_bd, wc, &op->o_req_ndn, &ae);
+ break;
+ default:
+ /* TODO: error handling */
+ Debug( LDAP_DEBUG_ANY,
+ "<== wt_search: error at wt_dn2entry() rc=%d\n", rc );
+ send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
+ goto done;
+ }
+
+ if ( op->ors_deref & LDAP_DEREF_FINDING ) {
+ /* not implement yet */
+ }
+
+ if ( e == NULL ) {
+ if ( ae ) {
+ struct berval matched_dn = BER_BVNULL;
+ /* found ancestor entry */
+ if ( access_allowed( op, ae,
+ slap_schema.si_ad_entry,
+ NULL, ACL_DISCLOSE, NULL ) ) {
+ BerVarray erefs = NULL;
+ ber_dupbv( &matched_dn, &ae->e_name );
+ erefs = is_entry_referral( ae )
+ ? get_entry_referrals( op, ae )
+ : NULL;
+ rs->sr_err = LDAP_REFERRAL;
+ rs->sr_matched = matched_dn.bv_val;
+ if ( erefs ) {
+ rs->sr_ref = referral_rewrite( erefs, &matched_dn,
+ &op->o_req_dn, op->oq_search.rs_scope );
+ ber_bvarray_free( erefs );
+ }
+ Debug( LDAP_DEBUG_ARGS,
+ "wt_search: ancestor is referral\n");
+ rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
+ send_ldap_result( op, rs );
+ goto done;
+ }
+ }
+ Debug( LDAP_DEBUG_ARGS,
+ "wt_search: no such object %s\n",
+ op->o_req_dn.bv_val);
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ send_ldap_result( op, rs );
+ 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;
+ }
+
+ send_ldap_result( op, rs );
+ goto done;
+ }
+
+ if ( !manageDSAit && is_entry_referral( e ) ) {
+ 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;
+ 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_ARGS, "wt_search: entry is referral\n");
+ 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;
+ send_ldap_result( op, rs );
+ goto done;
+ }
+
+ /* compute it anyway; root does not use it */
+ stoptime = op->o_time + op->ors_tlimit;
+
+ base = 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{
+ WT_IDL_ZERO( candidates );
+ WT_IDL_ZERO( scopes );
+ rc = search_candidates( op, rs, base,
+ wc, candidates, scopes );
+ switch(rc){
+ case 0:
+ case WT_NOTFOUND:
+ break;
+ default:
+ Debug( LDAP_DEBUG_ANY, "wt_search: error search_candidates\n" );
+ send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
+ goto done;
+ }
+ }
+
+ /* start cursor at beginning of candidates.
+ */
+ cursor = 0;
+
+ if ( candidates[0] == 0 ) {
+ Debug( LDAP_DEBUG_TRACE, "wt_search: no candidates\n" );
+ goto nochange;
+ }
+
+ if ( op->ors_limit &&
+ op->ors_limit->lms_s_unchecked != -1 &&
+ WT_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 = WT_IDL_N(candidates);
+ }
+
+ if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) {
+ /* TODO: pageresult */
+ 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 = wt_idl_first( candidates, &cursor );
+ if ( id == NOID ) {
+ Debug( LDAP_DEBUG_TRACE, "wt_search: no paged results candidates\n" );
+ send_paged_response( op, rs, &lastid, 0 );
+
+ rs->sr_err = LDAP_OTHER;
+ goto done;
+ }
+ nentries = ps->ps_count;
+ if ( id == (ID)ps->ps_cookie )
+ id = wt_idl_next( candidates, &cursor );
+ goto loop_begin;
+ }
+
+ for ( id = wt_idl_first( candidates, &cursor );
+ id != NOID ; id = wt_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;
+ }
+
+ nentries++;
+
+ fetch_entry_retry:
+
+ rc = wt_id2entry(op->o_bd, wc, id, &e);
+ /* TODO: error handling */
+ if ( e == NULL ) {
+ /* TODO: */
+ 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;
+ }
+
+ scopeok = 0;
+ switch( op->ors_scope ) {
+ case LDAP_SCOPE_BASE:
+ /* This is always true, yes? */
+ if ( id == base->e_id ) scopeok = 1;
+ break;
+ case LDAP_SCOPE_ONELEVEL:
+ scopeok = 1;
+ break;
+ case LDAP_SCOPE_CHILDREN:
+ if ( id == base->e_id ) break;
+ /* Fall-thru */
+ case LDAP_SCOPE_SUBTREE:
+ scopeok = dnIsSuffix(&e->e_nname, &base->e_nname);
+ 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;
+ }
+ /* TODO: alias handling */
+ }
+
+ /* Not in scope, ignore it */
+ if ( !scopeok )
+ {
+ Debug( LDAP_DEBUG_TRACE, "wt_search: %ld scope not okay\n",
+ (long) id );
+ goto loop_continue;
+ }
+
+ /*
+ * 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;
+ send_search_reference( op, rs );
+ rs->sr_entry = NULL;
+ ber_bvarray_free( rs->sr_ref );
+ ber_bvarray_free( erefs );
+ goto loop_continue;
+ }
+
+ if ( !manageDSAit && is_entry_glue( e )) {
+ 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 ) {
+ wt_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 = REP_ENTRY_MUSTRELEASE;
+ rs->sr_err = LDAP_SUCCESS;
+ rs->sr_err = send_search_entry( op, rs );
+ rs->sr_attrs = NULL;
+ rs->sr_entry = NULL;
+ e = NULL;
+
+ switch ( rs->sr_err ) {
+ case LDAP_SUCCESS: /* entry sent ok */
+ break;
+ default: /* entry not sent */
+ break;
+ case LDAP_BUSY:
+ send_ldap_result( op, rs );
+ goto done;
+ case LDAP_UNAVAILABLE:
+ rs->sr_err = LDAP_OTHER;
+ goto done;
+ case LDAP_SIZELIMIT_EXCEEDED:
+ rs->sr_ref = rs->sr_v2ref;
+ send_ldap_result( op, rs );
+ rs->sr_err = LDAP_SUCCESS;
+ goto done;
+ }
+ }
+ } else {
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_search: %ld does not match filter\n", (long) id );
+ }
+
+ loop_continue:
+ if( e ) {
+ wt_entry_return( e );
+ e = 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( base ) {
+ wt_entry_return( base );
+ }
+
+ if( e ) {
+ wt_entry_return( e );
+ }
+
+ if( ae ) {
+ wt_entry_return( ae );
+ }
+
+ return rs->sr_err;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/tools.c b/servers/slapd/back-wt/tools.c
new file mode 100644
index 0000000..795e81d
--- /dev/null
+++ b/servers/slapd/back-wt/tools.c
@@ -0,0 +1,721 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include "back-wt.h"
+#include "slap-config.h"
+
+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 nholes;
+
+static struct berval *tool_base;
+static int tool_scope;
+static Filter *tool_filter;
+
+static wt_ctx *wc;
+static WT_CURSOR *reader;
+static WT_ITEM item;
+
+int
+wt_tool_entry_open( BackendDB *be, int mode )
+{
+ struct wt_info *wi = (struct wt_info *) be->be_private;
+ int rc;
+
+ if ( slapMode & SLAP_TOOL_DRYRUN )
+ return 0;
+
+ wc = wt_ctx_init(wi);
+ if( !wc ){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_tool_entry_open: wt_ctx_get failed\n" );
+ return -1;
+ }
+
+ rc = wc->session->open_cursor(wc->session, WT_TABLE_ID2ENTRY"(entry)"
+ ,NULL, NULL, &reader);
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_tool_entry_open: cursor open failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+wt_tool_entry_close( BackendDB *be )
+{
+ if ( slapMode & SLAP_TOOL_DRYRUN )
+ return 0;
+
+ if( reader ) {
+ reader->close(reader);
+ reader = NULL;
+ }
+
+ wt_ctx_free(NULL, wc);
+
+ if( nholes ) {
+ unsigned i;
+ fprintf( stderr, "Error, entries missing!\n");
+ for (i=0; i<nholes; i++) {
+ fprintf(stderr, " entry %ld: %s\n",
+ holes[i].id, holes[i].dn.bv_val);
+ }
+ return -1;
+ }
+
+ return 0;
+}
+
+ID
+wt_tool_entry_first_x( BackendDB *be,
+ struct berval *base,
+ int scope,
+ Filter *f )
+{
+ tool_base = base;
+ tool_scope = scope;
+ tool_filter = f;
+
+ return wt_tool_entry_next( be );
+}
+
+ID
+wt_tool_entry_next( BackendDB *be )
+{
+ int rc;
+ ID id;
+
+ rc = reader->next(reader);
+ switch( rc ){
+ case 0:
+ break;
+ case WT_NOTFOUND:
+ return NOID;
+ default:
+ Debug( LDAP_DEBUG_ANY,
+ "wt_tool_entry_next: next failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ return NOID;
+ }
+
+ rc = reader->get_key(reader, &id);
+ if( rc ){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_tool_entry_next: get_key failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ }
+ return id;
+}
+
+static ber_len_t
+entry_getlen(unsigned char **buf)
+{
+ ber_len_t len;
+ int i;
+
+ len = *(*buf)++;
+ if (len <= 0x7f)
+ return len;
+ i = len & 0x7f;
+ len = 0;
+ for (;i > 0; i--) {
+ len <<= 8;
+ len |= *(*buf)++;
+ }
+ return len;
+}
+
+int wt_entry_header(WT_ITEM *item, EntryHeader *eh)
+{
+ unsigned char *ptr = (unsigned char *)item->data;
+
+ /* Some overlays can create empty entries
+ * so don't check for zeros here.
+ */
+ eh->nattrs = entry_getlen(&ptr);
+ eh->nvals = entry_getlen(&ptr);
+ eh->data = (char *)ptr;
+ return LDAP_SUCCESS;
+}
+
+Entry *
+wt_tool_entry_get( BackendDB *be, ID id )
+{
+ Entry *e = NULL;
+ static EntryHeader eh;
+ int rc, eoff;
+
+ assert( be != NULL );
+ assert( slapMode & SLAP_TOOL_MODE );
+
+ reader->set_key(reader, id);
+ rc = reader->search(reader);
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_tool_entry_get: search failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+ rc = reader->get_value(reader, &item);
+ if( rc ){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_tool_entry_get: get_value failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+
+ rc = wt_entry_header( &item, &eh );
+ assert( rc == 0 );
+ eoff = eh.data - (char *)item.data;
+
+ eh.bv.bv_len = eh.nvals * sizeof( struct berval ) + item.size;
+ eh.bv.bv_val = ch_realloc( eh.bv.bv_val, eh.bv.bv_len );
+ memset(eh.bv.bv_val, 0xff, eh.bv.bv_len);
+ eh.data = eh.bv.bv_val + eh.nvals * sizeof( struct berval );
+ memcpy(eh.data, item.data, item.size);
+ eh.data += eoff;
+
+ rc = entry_decode( &eh, &e );
+ assert( rc == 0 );
+
+ if( rc == LDAP_SUCCESS ) {
+ e->e_id = id;
+ }
+
+done:
+ return e;
+}
+
+static int wt_tool_next_id(
+ Operation *op,
+ Entry *e,
+ struct berval *text,
+ int hole )
+{
+ struct berval dn = e->e_name;
+ struct berval ndn = e->e_nname;
+ struct berval pdn, npdn;
+ int rc;
+ ID id = 0;
+ ID pid = 0;
+
+ if(ndn.bv_len == 0){
+ e->e_id = 0;
+ return 0;
+ }
+
+ rc = wt_dn2id(op, wc, &ndn, &id);
+ if(rc == 0){
+ e->e_id = id;
+ }else if( rc == WT_NOTFOUND ){
+ if ( !be_issuffix( op->o_bd, &ndn ) ) {
+ ID eid = e->e_id;
+ dnParent( &dn, &pdn );
+ dnParent( &ndn, &npdn );
+ e->e_name = pdn;
+ e->e_nname = npdn;
+ rc = wt_tool_next_id( op, 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;
+ }
+ wt_next_id( op->o_bd, &e->e_id );
+ rc = wt_dn2id_add(op, wc, pid, e);
+ if( rc ){
+ snprintf( text->bv_val, text->bv_len,
+ "wt_dn2id_add failed: %s (%d)",
+ wiredtiger_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> wt_tool_next_id: %s\n", text->bv_val );
+ }
+
+ }else if ( !hole ) {
+ unsigned i, j;
+ e->e_id = id;
+
+ for ( i=0; i<nholes; i++) {
+ if ( holes[i].id == e->e_id ) {
+ free(holes[i].dn.bv_val);
+ for (j=i;j<nholes;j++) holes[j] = holes[j+1];
+ holes[j].id = 0;
+ nholes--;
+ break;
+ } else if ( holes[i].id > e->e_id ) {
+ break;
+ }
+ }
+ }
+ return rc;
+}
+
+static int
+wt_tool_index_add(
+ Operation *op,
+ wt_ctx *wc,
+ Entry *e )
+{
+ return wt_index_entry_add( op, wc, e );
+}
+
+ID
+wt_tool_entry_put( BackendDB *be, Entry *e, struct berval *text )
+{
+ int rc;
+ Operation op = {0};
+ Opheader ohdr = {0};
+
+ if ( slapMode & SLAP_TOOL_DRYRUN )
+ return 0;
+
+ assert( slapMode & SLAP_TOOL_MODE );
+ assert( text != NULL );
+ assert( text->bv_val != NULL );
+ assert( text->bv_val[0] == '\0' ); /* overconservative? */
+
+ Debug( LDAP_DEBUG_TRACE,
+ "=> wt_tool_entry_put: ( \"%s\" )\n", e->e_dn );
+
+ rc = wc->session->begin_transaction(wc->session, NULL);
+ if( rc ){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_dn2id_add: begin_transaction failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ return NOID;
+ }
+
+ op.o_hdr = &ohdr;
+ op.o_bd = be;
+ op.o_tmpmemctx = NULL;
+ op.o_tmpmfuncs = &ch_mfuncs;
+
+ rc = wt_tool_next_id( &op, e, text, 0 );
+ if( rc != 0 ) {
+ snprintf( text->bv_val, text->bv_len,
+ "wt_tool_next_id failed: %s (%d)",
+ wiredtiger_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> wt_tool_entry_put: %s\n", text->bv_val );
+ goto done;
+ }
+
+ rc = wt_id2entry_add( &op, wc, e );
+ if( rc != 0 ) {
+ snprintf( text->bv_val, text->bv_len,
+ "id2entry_add failed: %s (%d)",
+ wiredtiger_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> wt_tool_entry_put: %s\n",
+ text->bv_val );
+ goto done;
+ }
+
+ rc = wt_tool_index_add( &op, wc, e );
+ if( rc != 0 ) {
+ snprintf( text->bv_val, text->bv_len,
+ "index_entry_add failed: %s (%d)",
+ rc == LDAP_OTHER ? "Internal error" :
+ wiredtiger_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> wt_tool_entry_put: %s\n", text->bv_val );
+ goto done;
+ }
+
+done:
+ if ( rc == 0 ){
+ rc = wc->session->commit_transaction(wc->session, NULL);
+ if( rc != 0 ) {
+ snprintf( text->bv_val, text->bv_len,
+ "txn_commit failed: %s (%d)",
+ wiredtiger_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> wt_tool_entry_put: %s\n", text->bv_val );
+ e->e_id = NOID;
+ }
+ }else{
+ rc = wc->session->rollback_transaction(wc->session, NULL);
+ snprintf( text->bv_val, text->bv_len,
+ "txn_aborted! %s (%d)",
+ rc == LDAP_OTHER ? "Internal error" :
+ wiredtiger_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> wt_tool_entry_put: %s\n", text->bv_val );
+ e->e_id = NOID;
+ }
+
+ return e->e_id;
+}
+
+int wt_tool_entry_reindex(
+ BackendDB *be,
+ ID id,
+ AttributeDescription **adv )
+{
+ struct wt_info *wi = (struct wt_info *) be->be_private;
+ int rc;
+ Entry *e;
+ Operation op = {0};
+ Opheader ohdr = {0};
+
+ Debug( LDAP_DEBUG_ARGS,
+ "=> wt_tool_entry_reindex( %ld )\n", (long) id );
+ assert( tool_base == NULL );
+ assert( tool_filter == NULL );
+
+ /* No indexes configured, nothing to do. Could return an
+ * error here to shortcut things.
+ */
+ if (!wi->wi_attrs) {
+ return 0;
+ }
+
+ /* Check for explicit list of attrs to index */
+ if ( adv ) {
+ int i, j, n;
+
+ if ( wi->wi_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 ( wi->wi_attrs[i]->ai_desc != adv[i] ) {
+ for ( j = i+1; j < wi->wi_nattrs; j++ ) {
+ if ( wi->wi_attrs[j]->ai_desc == adv[i] ) {
+ AttrInfo *ai = wi->wi_attrs[i];
+ wi->wi_attrs[i] = wi->wi_attrs[j];
+ wi->wi_attrs[j] = ai;
+ break;
+ }
+ }
+ if ( j == wi->wi_nattrs ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_tool_entry_reindex: no index configured for %s\n",
+ adv[i]->ad_cname.bv_val );
+ return -1;
+ }
+ }
+ }
+ wi->wi_nattrs = i;
+ }
+
+ e = wt_tool_entry_get( be, id );
+
+ if( e == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "=> wt_tool_entry_reindex"
+ ": could not locate id=%ld\n",
+ (long) id );
+ return -1;
+ }
+
+ op.o_hdr = &ohdr;
+ op.o_bd = be;
+ op.o_tmpmemctx = NULL;
+ op.o_tmpmfuncs = &ch_mfuncs;
+
+ rc = wc->session->begin_transaction(wc->session, NULL);
+ if( rc ){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_tool_entry_reindex: begin_transaction failed %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+ Debug( LDAP_DEBUG_TRACE,
+ "=> wt_tool_entry_reindex( %ld, \"%s\" )\n",
+ (long) id, e->e_dn );
+
+ rc = wt_tool_index_add( &op, wc, e );
+
+done:
+ if ( rc == 0 ){
+ rc = wc->session->commit_transaction(wc->session, NULL);
+ if( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "=> wt_tool_entry_reindex: commit_transaction failed %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ }
+ }else{
+ rc = wc->session->rollback_transaction(wc->session, NULL);
+ Debug( LDAP_DEBUG_ANY,
+ "=> wt_tool_entry_reindex: rollback transaction %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ }
+
+ wt_entry_release( &op, e, 0 );
+
+ return rc;
+}
+
+ID wt_tool_dn2id_get(
+ Backend *be,
+ struct berval *dn
+)
+{
+ Operation op = {0};
+ Opheader ohdr = {0};
+ ID id;
+ int rc;
+
+ if ( BER_BVISEMPTY(dn) )
+ return 0;
+
+ op.o_hdr = &ohdr;
+ op.o_bd = be;
+ op.o_tmpmemctx = NULL;
+ op.o_tmpmfuncs = &ch_mfuncs;
+
+ rc = wt_dn2id(&op, wc, dn, &id);
+ switch( rc ){
+ case 0:
+ break;
+ case WT_NOTFOUND:
+ return NOID;
+ default:
+ Debug( LDAP_DEBUG_ANY,
+ "wt_tool_entry_get: entry get failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ return NOID;
+ }
+ return id;
+}
+
+ID wt_tool_entry_modify(
+ BackendDB *be,
+ Entry *e,
+ struct berval *text )
+{
+ int rc;
+ 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,
+ "=> wt_tool_entry_modify( %ld, \"%s\" )\n",
+ (long) e->e_id, e->e_dn );
+
+ rc = wc->session->begin_transaction(wc->session, NULL);
+ if( rc ){
+ Debug( LDAP_DEBUG_ANY, "=> wt_tool_entry_modify"
+ ": begin_transaction failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ return NOID;
+ }
+
+ op.o_hdr = &ohdr;
+ op.o_bd = be;
+ op.o_tmpmemctx = NULL;
+ op.o_tmpmfuncs = &ch_mfuncs;
+
+ rc = wt_id2entry_update( &op, wc, e );
+ if( rc != 0 ) {
+ snprintf( text->bv_val, text->bv_len,
+ "id2entry_update failed: %s (%d)",
+ wiredtiger_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY, "=> wt_tool_entry_modify: %s\n",
+ text->bv_val );
+ goto done;
+ }
+
+done:
+ if ( rc == 0 ){
+ rc = wc->session->commit_transaction(wc->session, NULL);
+ if( rc != 0 ) {
+ snprintf( text->bv_val, text->bv_len,
+ "txn_commit failed: %s (%d)",
+ wiredtiger_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY, "=> wt_tool_entry_modify: %s\n",
+ text->bv_val );
+ e->e_id = NOID;
+ }
+ }else{
+ rc = wc->session->rollback_transaction(wc->session, NULL);
+ snprintf( text->bv_val, text->bv_len,
+ "txn_aborted! %s (%d)",
+ rc == LDAP_OTHER ? "Internal error" :
+ wiredtiger_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY, "=> wt_tool_entry_modify: %s\n",
+ text->bv_val );
+ e->e_id = NOID;
+ }
+
+ return e->e_id;
+}
+
+int wt_tool_entry_delete(
+ BackendDB *be,
+ struct berval *ndn,
+ struct berval *text )
+{
+ struct wt_info *wi = (struct wt_info *) be->be_private;
+ int rc;
+ Operation op = {0};
+ Opheader ohdr = {0};
+ Entry *e = NULL;
+
+ assert( be != NULL );
+ assert( slapMode & SLAP_TOOL_MODE );
+
+ assert( text != NULL );
+ assert( text->bv_val != NULL );
+ assert( text->bv_val[0] == '\0' ); /* overconservative? */
+
+ assert ( ndn != NULL );
+ assert ( ndn->bv_val != NULL );
+
+ Debug( LDAP_DEBUG_TRACE,
+ "=> wt_tool_entry_delete( %s )\n",
+ ndn->bv_val );
+
+ op.o_hdr = &ohdr;
+ op.o_bd = be;
+ op.o_tmpmemctx = NULL;
+ op.o_tmpmfuncs = &ch_mfuncs;
+
+ /* get entry */
+ rc = wt_dn2entry(op.o_bd, wc, ndn, &e);
+ switch( rc ) {
+ case 0:
+ break;
+ case WT_NOTFOUND:
+ Debug( LDAP_DEBUG_ARGS,
+ "<== wt_tool_entry_delete: no such object %s\n",
+ ndn->bv_val);
+ goto done;
+ default:
+ Debug( LDAP_DEBUG_ANY,
+ "wt_tool_entry_delete: error at wt_dn2entry() rc=%d\n",
+ rc );
+ goto done;
+ }
+
+ rc = wt_dn2id_has_children( &op, wc, e->e_id );
+ if( rc != WT_NOTFOUND ) {
+ /* subordinate objects must be deleted first */
+ rc = -1;
+ goto done;
+ }
+
+ rc = wc->session->begin_transaction(wc->session, NULL);
+ if( rc ){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_tool_entry_delete: begin_transaction failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+
+ /* delete from dn2id */
+ rc = wt_dn2id_delete( &op, wc, &e->e_nname);
+ if ( rc ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<== wt_tool_entry_delete: dn2id failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ wc->session->rollback_transaction(wc->session, NULL);
+ goto done;
+ }
+
+ /* delete indices for old attributes */
+ rc = wt_index_entry_del( &op, wc, e );
+ if ( rc ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<== wt_tool_entry_delete: index delete failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ wc->session->rollback_transaction(wc->session, NULL);
+ goto done;
+ }
+
+ /* delete from id2entry */
+ rc = wt_id2entry_delete( &op, wc, e );
+ if ( rc ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<== wt_tool_entry_delete: id2entry failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ wc->session->rollback_transaction(wc->session, NULL);
+ goto done;
+ }
+
+ rc = wc->session->commit_transaction(wc->session, NULL);
+ if( rc != 0 ) {
+ snprintf( text->bv_val, text->bv_len,
+ "txn_commit failed: %s (%d)",
+ wiredtiger_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> wt_tool_entry_delete: %s\n",
+ text->bv_val );
+ goto done;
+ }
+
+done:
+ /* free entry */
+ if( e != NULL ) {
+ wt_entry_return( e );
+ }
+ return rc;
+}
+
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */