diff options
Diffstat (limited to 'servers/slapd/back-wt')
27 files changed, 9051 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..658837f --- /dev/null +++ b/servers/slapd/back-wt/tools.c @@ -0,0 +1,712 @@ +/* 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; + + 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( 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}; + + 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: + */ |