MySQL NDB Cluster backend (experimental)

This commit is contained in:
Howard Chu 2008-03-02 15:14:53 +00:00
parent fdbc12fa74
commit da6fdf70a4
16 changed files with 5532 additions and 0 deletions

View File

@ -0,0 +1,59 @@
# Makefile.in for back-ndb
# $OpenLDAP$
## This work is part of OpenLDAP Software <http://www.openldap.org/>.
##
## Copyright 2008 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 initially developed by Howard Chu for inclusion
## in OpenLDAP Software. This work was sponsored by MySQL.
SRCS = init.cpp tools.cpp config.cpp ndbio.cpp \
add.cpp bind.cpp compare.cpp delete.cpp modify.cpp modrdn.cpp search.cpp
OBJS = init.lo tools.lo config.lo ndbio.lo \
add.lo bind.lo compare.lo delete.lo modify.lo modrdn.lo search.lo
LDAP_INCDIR= ../../../include
LDAP_LIBDIR= ../../../libraries
BUILD_OPT = "--enable-ndb"
BUILD_MOD = @BUILD_NDB@
mod_DEFS = -DSLAPD_IMPORT
MOD_DEFS = $(@BUILD_NDB@_DEFS)
MOD_LIBS = $(SLAPD_NDB_LIBS)
shared_LDAP_LIBS = $(LDAP_LIBLDAP_R_LA) $(LDAP_LIBLBER_LA)
NT_LINK_LIBS = -L.. -lslapd $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS)
UNIX_LINK_LIBS = $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS)
LIBBASE = back_ndb
XINCPATH = -I.. -I$(srcdir)/.. @SLAPD_NDB_INCS@
XDEFS = $(MODULES_CPPFLAGS)
AC_CXX = g++
CXX = $(AC_CXX)
LTCXX_MOD = $(LIBTOOL) $(LTONLY_MOD) --mode=compile \
$(CXX) $(LT_CFLAGS) $(LT_CPPFLAGS) $(MOD_DEFS) -c
all-local-lib: ../.backend
.SUFFIXES: .c .o .lo .cpp
.cpp.lo:
$(LTCXX_MOD) $<
../.backend: lib$(LIBBASE).a
@touch $@

View File

@ -0,0 +1,9 @@
LDAP features not currently supported:
multi-valued attributes
tagged attributes
extensibleObjects
aliases
referrals
substring indexing
subtree rename

View File

@ -0,0 +1,265 @@
/* add.cpp - ldap NDB back-end add routine */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
* Copyright 2008 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 initially developed by Howard Chu for inclusion
* in OpenLDAP Software. This work was sponsored by MySQL.
*/
#include "portable.h"
#include <stdio.h>
#include <ac/string.h>
#include "back-ndb.h"
extern "C" int
ndb_back_add(Operation *op, SlapReply *rs )
{
struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
Entry p = {0};
char textbuf[SLAP_TEXT_BUFLEN];
size_t textlen = sizeof textbuf;
AttributeDescription *children = slap_schema.si_ad_children;
AttributeDescription *entry = slap_schema.si_ad_entry;
NdbArgs NA;
NdbRdns rdns;
struct berval matched;
int num_retries = 0;
int success;
LDAPControl **postread_ctrl = NULL;
LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
int num_ctrls = 0;
Debug(LDAP_DEBUG_ARGS, "==> " LDAP_XSTRING(ndb_back_add) ": %s\n",
op->oq_add.rs_e->e_name.bv_val, 0, 0);
ctrls[num_ctrls] = 0;
/* check entry's schema */
rs->sr_err = entry_schema_check( op, op->oq_add.rs_e, NULL,
get_relax(op), 1, &rs->sr_text, textbuf, textlen );
if ( rs->sr_err != LDAP_SUCCESS ) {
Debug( LDAP_DEBUG_TRACE,
LDAP_XSTRING(ndb_back_add) ": entry failed schema check: "
"%s (%d)\n", rs->sr_text, rs->sr_err, 0 );
goto return_results;
}
/* add opattrs to shadow as well, only missing attrs will actually
* be added; helps compatibility with older OL versions */
rs->sr_err = slap_add_opattrs( op, &rs->sr_text, textbuf, textlen, 1 );
if ( rs->sr_err != LDAP_SUCCESS ) {
Debug( LDAP_DEBUG_TRACE,
LDAP_XSTRING(ndb_back_add) ": entry failed op attrs add: "
"%s (%d)\n", rs->sr_text, rs->sr_err, 0 );
goto return_results;
}
/* Get our NDB handle */
rs->sr_err = ndb_thread_handle( op, &NA.ndb );
/*
* Get the parent dn and see if the corresponding entry exists.
*/
if ( be_issuffix( op->o_bd, &op->oq_add.rs_e->e_nname ) ) {
p.e_name = slap_empty_bv;
p.e_nname = slap_empty_bv;
} else {
dnParent( &op->oq_add.rs_e->e_nname, &p.e_nname );
dnParent( &op->oq_add.rs_e->e_name, &p.e_name );
}
op->ora_e->e_id = NOID;
rdns.nr_num = 0;
NA.rdns = &rdns;
if( 0 ) {
retry: /* transaction retry */
NA.txn->close();
NA.txn = NULL;
if ( op->o_abandon ) {
rs->sr_err = SLAPD_ABANDON;
goto return_results;
}
ndb_trans_backoff( ++num_retries );
}
NA.txn = NA.ndb->startTransaction();
rs->sr_text = NULL;
if( !NA.txn ) {
Debug( LDAP_DEBUG_TRACE,
LDAP_XSTRING(ndb_back_add) ": startTransaction failed: %s (%d)\n",
NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 );
rs->sr_err = LDAP_OTHER;
rs->sr_text = "internal error";
goto return_results;
}
/* get entry or parent */
{
Entry dummy;
dummy.e_name = op->ora_e->e_name;
NA.e = &dummy;
NA.ocs = &matched;
rs->sr_err = ndb_entry_get_info( op->o_bd, &NA, 0, &matched );
}
switch( rs->sr_err ) {
case 0:
rs->sr_err = LDAP_ALREADY_EXISTS;
goto return_results;
case LDAP_NO_SUCH_OBJECT:
break;
#if 0
case DB_LOCK_DEADLOCK:
case DB_LOCK_NOTGRANTED:
goto retry;
#endif
case LDAP_BUSY:
rs->sr_text = "ldap server busy";
goto return_results;
default:
rs->sr_err = LDAP_OTHER;
rs->sr_text = "internal error";
goto return_results;
}
if ( ber_bvstrcasecmp( &p.e_nname, &matched ) ) {
rs->sr_matched = matched.bv_val;
Debug( LDAP_DEBUG_TRACE,
LDAP_XSTRING(ndb_back_add) ": parent "
"does not exist\n", 0, 0, 0 );
rs->sr_text = "parent does not exist";
rs->sr_err = LDAP_NO_SUCH_OBJECT;
goto return_results;
}
rs->sr_err = access_allowed( op, &p,
children, NULL, ACL_WADD, NULL );
if ( ! rs->sr_err ) {
Debug( LDAP_DEBUG_TRACE,
LDAP_XSTRING(ndb_back_add) ": no write access to parent\n",
0, 0, 0 );
rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
rs->sr_text = "no write access to parent";
goto return_results;;
}
rs->sr_err = access_allowed( op, op->oq_add.rs_e,
entry, NULL, ACL_WADD, NULL );
if ( ! rs->sr_err ) {
Debug( LDAP_DEBUG_TRACE,
LDAP_XSTRING(ndb_back_add) ": no write access to entry\n",
0, 0, 0 );
rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
rs->sr_text = "no write access to entry";
goto return_results;;
}
/* acquire entry ID */
if ( op->ora_e->e_id == NOID ) {
rs->sr_err = ndb_next_id( op->o_bd, NA.ndb, &op->ora_e->e_id );
if( rs->sr_err != 0 ) {
Debug( LDAP_DEBUG_TRACE,
LDAP_XSTRING(ndb_back_add) ": next_id failed (%d)\n",
rs->sr_err, 0, 0 );
rs->sr_err = LDAP_OTHER;
rs->sr_text = "internal error";
goto return_results;
}
}
NA.e = op->ora_e;
/* dn2id index */
rs->sr_err = ndb_entry_put_info( op->o_bd, &NA, 0 );
/* id2entry index */
rs->sr_err = ndb_entry_put_data( op->o_bd, &NA, 0 );
/* 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->oq_add.rs_e,
&slap_post_read_bv, postread_ctrl ) )
{
Debug( LDAP_DEBUG_TRACE,
"<=- " LDAP_XSTRING(ndb_back_add) ": post-read "
"failed!\n", 0, 0, 0 );
if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
/* FIXME: is it correct to abort
* operation if control fails? */
goto return_results;
}
}
}
if ( op->o_noop ) {
if (( rs->sr_err=NA.txn->execute( Rollback )) != 0 ) {
rs->sr_text = "txn (no-op) failed";
} else {
rs->sr_err = LDAP_X_NO_OPERATION;
}
} else {
if(( rs->sr_err=NA.txn->execute( Commit )) != 0 ) {
rs->sr_text = "txn_commit failed";
} else {
rs->sr_err = LDAP_SUCCESS;
}
}
if ( rs->sr_err != LDAP_SUCCESS && rs->sr_err != LDAP_X_NO_OPERATION ) {
Debug( LDAP_DEBUG_TRACE,
LDAP_XSTRING(ndb_back_add) ": %s : %s (%d)\n",
rs->sr_text, NA.txn->getNdbError().message, NA.txn->getNdbError().code );
rs->sr_err = LDAP_OTHER;
goto return_results;
}
NA.txn->close();
NA.txn = NULL;
Debug(LDAP_DEBUG_TRACE,
LDAP_XSTRING(ndb_back_add) ": added%s id=%08lx dn=\"%s\"\n",
op->o_noop ? " (no-op)" : "",
op->oq_add.rs_e->e_id, op->oq_add.rs_e->e_dn );
rs->sr_text = NULL;
if( num_ctrls ) rs->sr_ctrls = ctrls;
return_results:
success = rs->sr_err;
send_ldap_result( op, rs );
slap_graduate_commit_csn( op );
if( NA.txn != NULL ) {
NA.txn->execute( Rollback );
NA.txn->close();
}
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;
}

View File

@ -0,0 +1,36 @@
# Definition of useful attribute sets
# from X.521 section 5
#
# TelecommunicationAttributeSet ATTRIBUTE ::= {
# facsimileTelephoneNumber |
# internationalISDNNumber |
# telephoneNumber |
# teletexTerminalIdentifier |
# telexNumber |
# preferredDeliveryMethod |
# destinationIndicator |
# registeredAddress |
# x121Address }
#
# PostalAttributeSet ATTRIBUTE ::= {
# physicalDeliveryOfficeName |
# postalAddress |
# postalCode |
# postOfficeBox |
# streetAddress }
#
# LocaleAttributeSet ATTRIBUTE ::= {
# localityName |
# stateOrProvinceName |
# streetAddress }
#
# OrganizationalAttributeSet ATTRIBUTE ::= {
# description |
# LocaleAttributeSet |
# PostalAttributeSet |
# TelecommunicationAttributeSet |
# businessCategory |
# seeAlso |
# searchGuide |
# userPassword }

View File

@ -0,0 +1,163 @@
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
* Copyright 2008 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 initially developed by Howard Chu for inclusion
* in OpenLDAP Software. This work was sponsored by MySQL.
*/
#ifndef SLAPD_NDB_H
#define SLAPD_NDB_H
#include "slap.h"
#include <mysql.h>
#include <NdbApi.hpp>
LDAP_BEGIN_DECL
/* The general design is to use one relational table per objectclass. This is
* complicated by objectclass inheritance and auxiliary classes though.
*
* Attributes must only occur in a single table. For objectclasses that inherit
* from other classes, attributes defined in the superior class are only stored
* in the superior class' table. When multiple unrelated classes define the same
* attributes, an attributeSet should be defined instead, containing all of the
* common attributes.
*
* The no_set table lists which other attributeSets apply to the current
* objectClass. The no_attrs table lists all of the non-inherited attributes of
* the class, including those residing in an attributeSet.
*
* Usually the table is named identically to the objectClass, but it can also
* be explicitly named something else if needed.
*/
#define NDB_MAX_OCSETS 8
struct ndb_attrinfo;
typedef struct ndb_ocinfo {
struct berval no_name; /* objectclass cname */
struct berval no_table;
ObjectClass *no_oc;
struct ndb_ocinfo *no_sets[NDB_MAX_OCSETS];
struct ndb_attrinfo **no_attrs;
int no_flag;
int no_nsets;
int no_nattrs;
} NdbOcInfo;
#define NDB_INFO_ATLEN 0x01
#define NDB_INFO_ATSET 0x02
#define NDB_INFO_INDEX 0x04
#define NDB_INFO_ACTIVE 0x08
typedef struct ndb_attrinfo {
struct berval na_name; /* attribute cname */
AttributeDescription *na_desc;
AttributeType *na_attr;
NdbOcInfo *na_oi;
int na_flag;
int na_len;
int na_column;
int na_ixcol;
} NdbAttrInfo;
typedef struct ListNode {
struct ListNode *ln_next;
void *ln_data;
} ListNode;
#define NDB_IS_OPEN(ni) (ni->ni_cluster != NULL)
struct ndb_info {
/* NDB connection */
char *ni_connectstr;
char *ni_dbname;
Ndb_cluster_connection **ni_cluster;
/* MySQL connection parameters */
MYSQL ni_sql;
char *ni_hostname;
char *ni_username;
char *ni_password;
char *ni_socket;
unsigned long ni_clflag;
unsigned int ni_port;
/* Search filter processing */
int ni_search_stack_depth;
void *ni_search_stack;
#define DEFAULT_SEARCH_STACK_DEPTH 16
#define MINIMUM_SEARCH_STACK_DEPTH 8
/* Schema config */
NdbOcInfo *ni_opattrs;
ListNode *ni_attridxs;
ListNode *ni_attrlens;
ListNode *ni_attrsets;
ldap_pvt_thread_rdwr_t ni_ai_rwlock;
Avlnode *ni_ai_tree;
ldap_pvt_thread_rdwr_t ni_oc_rwlock;
Avlnode *ni_oc_tree;
int ni_nconns; /* number of connections to open */
int ni_nextconn; /* next conn to use */
ldap_pvt_thread_mutex_t ni_conn_mutex;
};
#define NDB_MAX_RDNS 16
#define NDB_RDN_LEN 128
#define NDB_MAX_OCS 64
#define DN2ID_TABLE "OL_dn2id"
#define EID_COLUMN 0U
#define OCS_COLUMN 1U
#define RDN_COLUMN 2U
#define IDX_COLUMN (2U+NDB_MAX_RDNS)
#define NEXTID_TABLE "OL_nextid"
#define NDB_OC_BUFLEN 1026 /* 1024 data plus 2 len bytes */
#define INDEX_NAME "OL_index"
typedef struct NdbRdns {
short nr_num;
char nr_buf[NDB_MAX_RDNS][NDB_RDN_LEN+1];
} NdbRdns;
typedef struct NdbOcs {
int no_ninfo;
int no_ntext;
NdbOcInfo *no_info[NDB_MAX_OCS];
struct berval no_text[NDB_MAX_OCS];
} NdbOcs;
typedef struct NdbArgs {
Ndb *ndb;
NdbTransaction *txn;
Entry *e;
NdbRdns *rdns;
struct berval *ocs;
} NdbArgs;
#define NDB_NO_SUCH_OBJECT 626
#define NDB_ALREADY_EXISTS 630
LDAP_END_DECL
#include "proto-ndb.h"
#endif

View File

@ -0,0 +1,158 @@
/* bind.cpp - ndb backend bind routine */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
* Copyright 2008 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 initially developed by Howard Chu for inclusion
* in OpenLDAP Software. This work was sponsored by MySQL.
*/
#include "portable.h"
#include <stdio.h>
#include <ac/string.h>
#include <ac/unistd.h>
#include "back-ndb.h"
extern "C" int
ndb_back_bind( Operation *op, SlapReply *rs )
{
struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
Entry e = {0};
Attribute *a;
AttributeDescription *password = slap_schema.si_ad_userPassword;
NdbArgs NA;
Debug( LDAP_DEBUG_ARGS,
"==> " LDAP_XSTRING(ndb_back_bind) ": dn: %s\n",
op->o_req_dn.bv_val, 0, 0);
/* allow noauth binds */
switch ( be_rootdn_bind( op, NULL ) ) {
case SLAP_CB_CONTINUE:
break;
default:
return rs->sr_err;
}
/* Get our NDB handle */
rs->sr_err = ndb_thread_handle( op, &NA.ndb );
e.e_name = op->o_req_dn;
e.e_nname = op->o_req_ndn;
dn2entry_retry:
NA.txn = NA.ndb->startTransaction();
rs->sr_text = NULL;
if( !NA.txn ) {
Debug( LDAP_DEBUG_TRACE,
LDAP_XSTRING(ndb_back_bind) ": startTransaction failed: %s (%d)\n",
NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 );
rs->sr_err = LDAP_OTHER;
rs->sr_text = "internal error";
goto done;
}
/* get entry */
{
NdbRdns rdns;
rdns.nr_num = 0;
NA.rdns = &rdns;
NA.ocs = NULL;
rs->sr_err = ndb_entry_get_info( op->o_bd, &NA, 0, NULL );
}
switch(rs->sr_err) {
case 0:
break;
case LDAP_NO_SUCH_OBJECT:
rs->sr_err = LDAP_INVALID_CREDENTIALS;
goto done;
case LDAP_BUSY:
rs->sr_text = "ldap_server_busy";
goto done;
#if 0
case DB_LOCK_DEADLOCK:
case DB_LOCK_NOTGRANTED:
goto dn2entry_retry;
#endif
default:
rs->sr_err = LDAP_OTHER;
rs->sr_text = "internal error";
goto done;
}
rs->sr_err = ndb_entry_get_data( op->o_bd, &NA, 0 );
ber_bvarray_free( NA.ocs );
ber_dupbv( &op->oq_bind.rb_edn, &e.e_name );
/* check for deleted */
if ( is_entry_subentry( &e ) ) {
/* entry is an subentry, don't allow bind */
Debug( LDAP_DEBUG_TRACE, "entry is subentry\n", 0,
0, 0 );
rs->sr_err = LDAP_INVALID_CREDENTIALS;
goto done;
}
if ( is_entry_alias( &e ) ) {
/* entry is an alias, don't allow bind */
Debug( LDAP_DEBUG_TRACE, "entry is alias\n", 0, 0, 0 );
rs->sr_err = LDAP_INVALID_CREDENTIALS;
goto done;
}
if ( is_entry_referral( &e ) ) {
Debug( LDAP_DEBUG_TRACE, "entry is referral\n", 0,
0, 0 );
rs->sr_err = LDAP_INVALID_CREDENTIALS;
goto done;
}
switch ( op->oq_bind.rb_method ) {
case LDAP_AUTH_SIMPLE:
a = attr_find( e.e_attrs, password );
if ( a == NULL ) {
rs->sr_err = LDAP_INVALID_CREDENTIALS;
goto done;
}
if ( slap_passwd_check( op, &e, a, &op->oq_bind.rb_cred,
&rs->sr_text ) != 0 )
{
/* failure; stop front end from sending result */
rs->sr_err = LDAP_INVALID_CREDENTIALS;
goto done;
}
rs->sr_err = 0;
break;
default:
assert( 0 ); /* should not be reachable */
rs->sr_err = LDAP_STRONG_AUTH_NOT_SUPPORTED;
rs->sr_text = "authentication method not supported";
}
done:
NA.txn->close();
if ( rs->sr_err ) {
send_ldap_result( op, rs );
}
/* front end will send result on success (rs->sr_err==0) */
return rs->sr_err;
}

View File

@ -0,0 +1,168 @@
/* compare.cpp - ndb backend compare routine */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
* Copyright 2008 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 initially developed by Howard Chu for inclusion
* in OpenLDAP Software. This work was sponsored by MySQL.
*/
#include "portable.h"
#include <stdio.h>
#include <ac/string.h>
#include "back-ndb.h"
int
ndb_back_compare( Operation *op, SlapReply *rs )
{
struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
Entry e = {0};
Attribute *a;
int manageDSAit = get_manageDSAit( op );
NdbArgs NA;
NdbRdns rdns;
struct berval matched;
/* Get our NDB handle */
rs->sr_err = ndb_thread_handle( op, &NA.ndb );
rdns.nr_num = 0;
NA.rdns = &rdns;
e.e_name = op->o_req_dn;
e.e_nname = op->o_req_ndn;
dn2entry_retry:
NA.txn = NA.ndb->startTransaction();
rs->sr_text = NULL;
if( !NA.txn ) {
Debug( LDAP_DEBUG_TRACE,
LDAP_XSTRING(ndb_compare) ": startTransaction failed: %s (%d)\n",
NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 );
rs->sr_err = LDAP_OTHER;
rs->sr_text = "internal error";
goto return_results;
}
NA.ocs = NULL;
/* get entry */
rs->sr_err = ndb_entry_get_info( op->o_bd, &NA, 0, &matched );
switch( rs->sr_err ) {
case 0:
break;
case LDAP_NO_SUCH_OBJECT:
rs->sr_matched = matched.bv_val;
goto return_results;
case LDAP_BUSY:
rs->sr_text = "ldap server busy";
goto return_results;
#if 0
case DB_LOCK_DEADLOCK:
case DB_LOCK_NOTGRANTED:
goto dn2entry_retry;
#endif
default:
rs->sr_err = LDAP_OTHER;
rs->sr_text = "internal error";
goto return_results;
}
rs->sr_err = ndb_entry_get_data( op->o_bd, &NA, 0 );
ber_bvarray_free( NA.ocs );
if (!manageDSAit && is_entry_referral( &e ) ) {
/* return referral only if "disclose" is granted on the object */
if ( !access_allowed( op, &e, slap_schema.si_ad_entry,
NULL, ACL_DISCLOSE, NULL ) )
{
rs->sr_err = LDAP_NO_SUCH_OBJECT;
} else {
/* entry is a referral, don't allow compare */
rs->sr_ref = get_entry_referrals( op, &e );
rs->sr_err = LDAP_REFERRAL;
rs->sr_matched = e.e_name.bv_val;
}
Debug( LDAP_DEBUG_TRACE, "entry is referral\n", 0, 0, 0 );
goto return_results;
}
if ( get_assert( op ) &&
( test_filter( op, &e, (Filter *)get_assertion( op )) != LDAP_COMPARE_TRUE ))
{
if ( !access_allowed( op, &e, slap_schema.si_ad_entry,
NULL, ACL_DISCLOSE, NULL ) )
{
rs->sr_err = LDAP_NO_SUCH_OBJECT;
} else {
rs->sr_err = LDAP_ASSERTION_FAILED;
}
goto return_results;
}
if ( !access_allowed( op, &e, op->oq_compare.rs_ava->aa_desc,
&op->oq_compare.rs_ava->aa_value, ACL_COMPARE, NULL ) )
{
/* return error 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_err = LDAP_INSUFFICIENT_ACCESS;
}
goto return_results;
}
rs->sr_err = LDAP_NO_SUCH_ATTRIBUTE;
for ( a = attrs_find( e.e_attrs, op->oq_compare.rs_ava->aa_desc );
a != NULL;
a = attrs_find( a->a_next, op->oq_compare.rs_ava->aa_desc ) )
{
rs->sr_err = LDAP_COMPARE_FALSE;
if ( attr_valfind( a,
SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
&op->oq_compare.rs_ava->aa_value, NULL,
op->o_tmpmemctx ) == 0 )
{
rs->sr_err = LDAP_COMPARE_TRUE;
break;
}
}
return_results:
NA.txn->close();
if ( e.e_attrs ) {
attrs_free( e.e_attrs );
e.e_attrs = NULL;
}
send_ldap_result( op, rs );
ber_bvarray_free( rs->sr_ref );
rs->sr_ref = NULL;
switch ( rs->sr_err ) {
case LDAP_COMPARE_FALSE:
case LDAP_COMPARE_TRUE:
rs->sr_err = LDAP_SUCCESS;
break;
}
return rs->sr_err;
}

View File

@ -0,0 +1,287 @@
/* config.cpp - ndb backend configuration file routine */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
* Copyright 2008 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 initially developed by Howard Chu for inclusion
* in OpenLDAP Software. This work was sponsored by MySQL.
*/
#include "portable.h"
#include "lutil.h"
#include "back-ndb.h"
#include "config.h"
extern "C" {
static ConfigDriver ndb_cf_gen;
};
enum {
NDB_ATLEN = 1,
NDB_ATSET,
NDB_INDEX
};
static ConfigTable ndbcfg[] = {
{ "dbhost", "hostname", 2, 2, 0, ARG_STRING|ARG_OFFSET,
(void *)offsetof(struct ndb_info, ni_hostname),
"( OLcfgDbAt:6.1 NAME 'olcDbHost' "
"DESC 'Hostname of SQL server' "
"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
{ "dbname", "name", 2, 2, 0, ARG_STRING|ARG_OFFSET,
(void *)offsetof(struct ndb_info, ni_dbname),
"( OLcfgDbAt:6.2 NAME 'olcDbName' "
"DESC 'Name of SQL database' "
"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
{ "dbuser", "username", 2, 2, 0, ARG_STRING|ARG_OFFSET,
(void *)offsetof(struct ndb_info, ni_username),
"( OLcfgDbAt:6.3 NAME 'olcDbUser' "
"DESC 'Username for SQL session' "
"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
{ "dbpass", "password", 2, 2, 0, ARG_STRING|ARG_OFFSET,
(void *)offsetof(struct ndb_info, ni_password),
"( OLcfgDbAt:6.4 NAME 'olcDbPass' "
"DESC 'Password for SQL session' "
"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
{ "dbport", "port", 2, 2, 0, ARG_UINT|ARG_OFFSET,
(void *)offsetof(struct ndb_info, ni_port),
"( OLcfgDbAt:6.5 NAME 'olcDbPort' "
"DESC 'Port number of SQL server' "
"SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
{ "dbsocket", "path", 2, 2, 0, ARG_STRING|ARG_OFFSET,
(void *)offsetof(struct ndb_info, ni_socket),
"( OLcfgDbAt:6.6 NAME 'olcDbSocket' "
"DESC 'Local socket path of SQL server' "
"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
{ "dbflag", "flag", 2, 2, 0, ARG_LONG|ARG_OFFSET,
(void *)offsetof(struct ndb_info, ni_clflag),
"( OLcfgDbAt:6.7 NAME 'olcDbFlag' "
"DESC 'Flags for SQL session' "
"SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
{ "dbconnect", "hostname", 2, 2, 0, ARG_STRING|ARG_OFFSET,
(void *)offsetof(struct ndb_info, ni_connectstr),
"( OLcfgDbAt:6.8 NAME 'olcDbConnect' "
"DESC 'Hostname of NDB server' "
"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
{ "dbconnections", "number", 2, 2, 0, ARG_INT|ARG_OFFSET,
(void *)offsetof(struct ndb_info, ni_nconns),
"( OLcfgDbAt:6.9 NAME 'olcDbConnections' "
"DESC 'Number of cluster connections to open' "
"SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
{ "attrlen", "attr> <len", 3, 3, 0, ARG_MAGIC|NDB_ATLEN,
(void *)ndb_cf_gen,
"( OLcfgDbAt:6.10 NAME 'olcNdbAttrLen' "
"DESC 'Column length of a specific attribute' "
"EQUALITY caseIgnoreMatch "
"SYNTAX OMsDirectoryString )", NULL, NULL },
{ "attrset", "set> <attrs", 3, 3, 0, ARG_MAGIC|NDB_ATSET,
(void *)ndb_cf_gen,
"( OLcfgDbAt:6.11 NAME 'olcNdbAttrSet' "
"DESC 'Set of common attributes' "
"EQUALITY caseIgnoreMatch "
"SYNTAX OMsDirectoryString )", NULL, NULL },
{ "index", "attr", 2, 2, 0, ARG_MAGIC|NDB_INDEX,
(void *)ndb_cf_gen, "( OLcfgDbAt:0.2 NAME 'olcDbIndex' "
"DESC 'Attribute to index' "
"EQUALITY caseIgnoreMatch "
"SYNTAX OMsDirectoryString )", NULL, NULL },
{ NULL, NULL, 0, 0, 0, ARG_IGNORED,
NULL, NULL, NULL, NULL }
};
static ConfigOCs ndbocs[] = {
{
"( OLcfgDbOc:6.2 "
"NAME 'olcNdbConfig' "
"DESC 'NDB backend configuration' "
"SUP olcDatabaseConfig "
"MUST ( olcDbHost $ olcDbName $ olcDbConnect ) "
"MAY ( olcDbUser $ olcDbPass $ olcDbPort $ olcDbSocket $ "
"olcDbFlag $ olcDbConnections $ olcNdbAttrLen $ "
"olcDbIndex $ olcNdbAttrSet ) )",
Cft_Database, ndbcfg },
{ NULL, Cft_Abstract, NULL }
};
static int
ndb_cf_gen( ConfigArgs *c )
{
struct ndb_info *ni = (struct ndb_info *)c->be->be_private;
int i, rc;
NdbAttrInfo *ai;
NdbOcInfo *oci;
ListNode *ln, **l2;
struct berval bv, *bva;
if ( c->op == SLAP_CONFIG_EMIT ) {
char buf[BUFSIZ];
rc = 0;
bv.bv_val = buf;
switch( c->type ) {
case NDB_ATLEN:
if ( ni->ni_attrlens ) {
for ( ln = ni->ni_attrlens; ln; ln=ln->ln_next ) {
ai = (NdbAttrInfo *)ln->ln_data;
bv.bv_len = snprintf( buf, sizeof(buf),
"%s %d", ai->na_name.bv_val,
ai->na_len );
value_add_one( &c->rvalue_vals, &bv );
}
} else {
rc = 1;
}
break;
case NDB_ATSET:
if ( ni->ni_attrsets ) {
char *ptr, *end = buf+sizeof(buf);
for ( ln = ni->ni_attrsets; ln; ln=ln->ln_next ) {
oci = (NdbOcInfo *)ln->ln_data;
ptr = lutil_strcopy( buf, oci->no_name.bv_val );
*ptr++ = ' ';
for ( i=0; i<oci->no_nattrs; i++ ) {
if ( end - ptr < oci->no_attrs[i]->na_name.bv_len+1 )
break;
if ( i )
*ptr++ = ',';
ptr = lutil_strcopy(ptr,
oci->no_attrs[i]->na_name.bv_val );
}
bv.bv_len = ptr - buf;
value_add_one( &c->rvalue_vals, &bv );
}
} else {
rc = 1;
}
break;
case NDB_INDEX:
if ( ni->ni_attridxs ) {
for ( ln = ni->ni_attridxs; ln; ln=ln->ln_next ) {
ai = (NdbAttrInfo *)ln->ln_data;
value_add_one( &c->rvalue_vals, &ai->na_name );
}
} else {
rc = 1;
}
break;
}
return rc;
} else if ( c->op == LDAP_MOD_DELETE ) { /* FIXME */
rc = 0;
switch( c->type ) {
case NDB_INDEX:
if ( c->valx == -1 ) {
int i;
/* delete all */
} else {
}
break;
}
return rc;
}
switch( c->type ) {
case NDB_ATLEN:
ber_str2bv( c->argv[1], 0, 0, &bv );
ai = ndb_ai_get( ni, &bv );
if ( !ai ) {
snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: invalid attr %s",
c->log, c->argv[1] );
Debug( LDAP_DEBUG_ANY, "%s\n", c->cr_msg, 0, 0 );
return -1;
}
for ( ln = ni->ni_attrlens; ln; ln = ln->ln_next ) {
if ( ln->ln_data == (void *)ai ) {
snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: attr len already set for %s",
c->log, c->argv[1] );
Debug( LDAP_DEBUG_ANY, "%s\n", c->cr_msg, 0, 0 );
return -1;
}
}
ai->na_len = atoi( c->argv[2] );
ai->na_flag |= NDB_INFO_ATLEN;
ln = (ListNode *)ch_malloc( sizeof(ListNode));
ln->ln_data = ai;
ln->ln_next = NULL;
for ( l2 = &ni->ni_attrlens; *l2; l2 = &(*l2)->ln_next );
*l2 = ln;
break;
case NDB_INDEX:
ber_str2bv( c->argv[1], 0, 0, &bv );
ai = ndb_ai_get( ni, &bv );
if ( !ai ) {
snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: invalid attr %s",
c->log, c->argv[1] );
Debug( LDAP_DEBUG_ANY, "%s\n", c->cr_msg, 0, 0 );
return -1;
}
for ( ln = ni->ni_attridxs; ln; ln = ln->ln_next ) {
if ( ln->ln_data == (void *)ai ) {
snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: attr index already set for %s",
c->log, c->argv[1] );
Debug( LDAP_DEBUG_ANY, "%s\n", c->cr_msg, 0, 0 );
return -1;
}
}
ai->na_flag |= NDB_INFO_INDEX;
ln = (ListNode *)ch_malloc( sizeof(ListNode));
ln->ln_data = ai;
ln->ln_next = NULL;
for ( l2 = &ni->ni_attridxs; *l2; l2 = &(*l2)->ln_next );
*l2 = ln;
break;
case NDB_ATSET:
ber_str2bv( c->argv[1], 0, 0, &bv );
bva = ndb_str2bvarray( c->argv[2], strlen( c->argv[2] ), ',' );
rc = ndb_aset_get( ni, &bv, bva, &oci );
ch_free( bva );
if ( rc ) {
if ( rc == LDAP_ALREADY_EXISTS ) {
snprintf( c->cr_msg, sizeof( c->cr_msg ),
"%s: attrset %s already defined",
c->log, c->argv[1] );
} else {
snprintf( c->cr_msg, sizeof( c->cr_msg ),
"%s: invalid attrset %s (%d)",
c->log, c->argv[1], rc );
}
Debug( LDAP_DEBUG_ANY, "%s\n", c->cr_msg, 0, 0 );
return -1;
}
ln = (ListNode *)ch_malloc( sizeof(ListNode));
ln->ln_data = oci;
ln->ln_next = NULL;
for ( l2 = &ni->ni_attrsets; *l2; l2 = &(*l2)->ln_next );
*l2 = ln;
break;
}
return 0;
}
extern "C"
int ndb_back_init_cf( BackendInfo *bi )
{
bi->bi_cf_ocs = ndbocs;
return config_register_schema( ndbcfg, ndbocs );
}

View File

@ -0,0 +1,318 @@
/* delete.cpp - ndb backend delete routine */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
* Copyright 2008 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 initially developed by Howard Chu for inclusion
* in OpenLDAP Software. This work was sponsored by MySQL.
*/
#include "portable.h"
#include <stdio.h>
#include <ac/string.h>
#include "lutil.h"
#include "back-ndb.h"
static struct berval glue_bv = BER_BVC("glue");
int
ndb_back_delete( Operation *op, SlapReply *rs )
{
struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
Entry e = {0};
Entry p = {0};
int manageDSAit = get_manageDSAit( op );
AttributeDescription *children = slap_schema.si_ad_children;
AttributeDescription *entry = slap_schema.si_ad_entry;
NdbArgs NA;
NdbRdns rdns;
struct berval matched;
int num_retries = 0;
int rc;
LDAPControl **preread_ctrl = NULL;
LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
int num_ctrls = 0;
Debug( LDAP_DEBUG_ARGS, "==> " LDAP_XSTRING(ndb_back_delete) ": %s\n",
op->o_req_dn.bv_val, 0, 0 );
ctrls[num_ctrls] = 0;
/* allocate CSN */
if ( BER_BVISNULL( &op->o_csn ) ) {
struct berval csn;
char csnbuf[LDAP_LUTIL_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_dn, &p.e_name );
dnParent( &op->o_req_ndn, &p.e_nname );
}
/* Get our NDB handle */
rs->sr_err = ndb_thread_handle( op, &NA.ndb );
rdns.nr_num = 0;
NA.rdns = &rdns;
NA.ocs = NULL;
NA.e = &e;
e.e_name = op->o_req_dn;
e.e_nname = op->o_req_ndn;
if( 0 ) {
retry: /* transaction retry */
NA.txn->close();
NA.txn = NULL;
Debug( LDAP_DEBUG_TRACE,
"==> " LDAP_XSTRING(ndb_back_delete) ": retrying...\n",
0, 0, 0 );
if ( op->o_abandon ) {
rs->sr_err = SLAPD_ABANDON;
goto return_results;
}
if ( NA.ocs ) {
ber_bvarray_free( NA.ocs );
NA.ocs = NULL;
}
ndb_trans_backoff( ++num_retries );
}
/* begin transaction */
NA.txn = NA.ndb->startTransaction();
rs->sr_text = NULL;
if( !NA.txn ) {
Debug( LDAP_DEBUG_TRACE,
LDAP_XSTRING(ndb_back_delete) ": startTransaction failed: %s (%d)\n",
NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 );
rs->sr_err = LDAP_OTHER;
rs->sr_text = "internal error";
goto return_results;
}
/* get entry */
rs->sr_err = ndb_entry_get_info( op->o_bd, &NA, 1, &matched );
switch( rs->sr_err ) {
case 0:
case LDAP_NO_SUCH_OBJECT:
break;
#if 0
case DB_LOCK_DEADLOCK:
case DB_LOCK_NOTGRANTED:
goto retry;
#endif
case LDAP_BUSY:
rs->sr_text = "ldap server busy";
goto return_results;
default:
rs->sr_err = LDAP_OTHER;
rs->sr_text = "internal error";
goto return_results;
}
if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ||
( !manageDSAit && bvmatch( NA.ocs, &glue_bv ))) {
Debug( LDAP_DEBUG_ARGS,
"<=- " LDAP_XSTRING(ndb_back_delete) ": no such object %s\n",
op->o_req_dn.bv_val, 0, 0);
if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
rs->sr_matched = matched.bv_val;
} else {
rs->sr_matched = p.e_name.bv_val;
rs->sr_err = LDAP_NO_SUCH_OBJECT;
}
goto return_results;
}
/* check parent for "children" acl */
rs->sr_err = access_allowed( op, &p,
children, NULL, ACL_WDEL, NULL );
if ( !rs->sr_err ) {
Debug( LDAP_DEBUG_TRACE,
"<=- " LDAP_XSTRING(ndb_back_delete) ": no write "
"access to parent\n", 0, 0, 0 );
rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
rs->sr_text = "no write access to parent";
goto return_results;
}
rs->sr_err = ndb_entry_get_data( op->o_bd, &NA, 1 );
rs->sr_err = access_allowed( op, &e,
entry, NULL, ACL_WDEL, NULL );
if ( !rs->sr_err ) {
Debug( LDAP_DEBUG_TRACE,
"<=- " LDAP_XSTRING(ndb_back_delete) ": no write access "
"to entry\n", 0, 0, 0 );
rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
rs->sr_text = "no write access to entry";
goto return_results;
}
if ( !manageDSAit && is_entry_referral( &e ) ) {
/* entry is a referral, don't allow delete */
rs->sr_ref = get_entry_referrals( op, &e );
Debug( LDAP_DEBUG_TRACE,
LDAP_XSTRING(ndb_back_delete) ": entry is referral\n",
0, 0, 0 );
rs->sr_err = LDAP_REFERRAL;
rs->sr_matched = e.e_name.bv_val;
rs->sr_flags = REP_REF_MUSTBEFREED;
goto return_results;
}
if ( get_assert( op ) &&
( test_filter( op, &e, (Filter *)get_assertion( op )) != LDAP_COMPARE_TRUE ))
{
rs->sr_err = LDAP_ASSERTION_FAILED;
goto return_results;
}
/* pre-read */
if( op->o_preread ) {
if( preread_ctrl == NULL ) {
preread_ctrl = &ctrls[num_ctrls++];
ctrls[num_ctrls] = NULL;
}
if( slap_read_controls( op, rs, &e,
&slap_pre_read_bv, preread_ctrl ) )
{
Debug( LDAP_DEBUG_TRACE,
"<=- " LDAP_XSTRING(ndb_back_delete) ": pre-read "
"failed!\n", 0, 0, 0 );
if ( op->o_preread & SLAP_CONTROL_CRITICAL ) {
/* FIXME: is it correct to abort
* operation if control fails? */
goto return_results;
}
}
}
/* Can't do it if we have kids */
rs->sr_err = ndb_has_children( &NA, &rc );
if ( rs->sr_err ) {
Debug(LDAP_DEBUG_ARGS,
"<=- " LDAP_XSTRING(ndb_back_delete)
": has_children failed: %s (%d)\n",
NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 );
rs->sr_err = LDAP_OTHER;
rs->sr_text = "internal error";
goto return_results;
}
if ( rc == LDAP_COMPARE_TRUE ) {
Debug(LDAP_DEBUG_ARGS,
"<=- " LDAP_XSTRING(ndb_back_delete)
": non-leaf %s\n",
op->o_req_dn.bv_val, 0, 0);
rs->sr_err = LDAP_NOT_ALLOWED_ON_NONLEAF;
rs->sr_text = "subordinate objects must be deleted first";
goto return_results;
}
/* delete info */
rs->sr_err = ndb_entry_del_info( op->o_bd, &NA );
if ( rs->sr_err != 0 ) {
Debug(LDAP_DEBUG_TRACE,
"<=- " LDAP_XSTRING(ndb_back_delete) ": del_info failed: %s (%d)\n",
NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 );
rs->sr_text = "DN index delete failed";
rs->sr_err = LDAP_OTHER;
goto return_results;
}
/* delete data */
rs->sr_err = ndb_entry_del_data( op->o_bd, &NA );
if ( rs->sr_err != 0 ) {
Debug( LDAP_DEBUG_TRACE,
"<=- " LDAP_XSTRING(ndb_back_delete) ": del_data failed: %s (%d)\n",
NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 );
rs->sr_text = "entry delete failed";
rs->sr_err = LDAP_OTHER;
goto return_results;
}
if( op->o_noop ) {
if (( rs->sr_err=NA.txn->execute( Rollback )) != 0 ) {
rs->sr_text = "txn (no-op) failed";
} else {
rs->sr_err = LDAP_X_NO_OPERATION;
}
} else {
if(( rs->sr_err=NA.txn->execute( Commit )) != 0 ) {
rs->sr_text = "txn_commit failed";
} else {
rs->sr_err = LDAP_SUCCESS;
}
}
if( rs->sr_err != LDAP_SUCCESS && rs->sr_err != LDAP_X_NO_OPERATION ) {
Debug( LDAP_DEBUG_TRACE,
LDAP_XSTRING(ndb_back_delete) ": txn_%s failed: %s (%d)\n",
op->o_noop ? "abort (no-op)" : "commit",
NA.txn->getNdbError().message, NA.txn->getNdbError().code );
rs->sr_err = LDAP_OTHER;
rs->sr_text = "commit failed";
goto return_results;
}
NA.txn->close();
NA.txn = NULL;
Debug( LDAP_DEBUG_TRACE,
LDAP_XSTRING(ndb_back_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 ( NA.ocs ) {
ber_bvarray_free( NA.ocs );
NA.ocs = NULL;
}
/* free entry */
if( e.e_attrs != NULL ) {
attrs_free( e.e_attrs );
e.e_attrs = NULL;
}
if( NA.txn != NULL ) {
NA.txn->execute( Rollback );
NA.txn->close();
}
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 );
}
return rs->sr_err;
}

View File

@ -0,0 +1,403 @@
/* init.cpp - initialize ndb backend */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
* Copyright 2008 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 initially developed by Howard Chu for inclusion
* in OpenLDAP Software. This work was sponsored by MySQL.
*/
#include "portable.h"
#include <stdio.h>
#include <ac/string.h>
#include <ac/unistd.h>
#include <ac/stdlib.h>
#include <ac/errno.h>
#include <sys/stat.h>
#include "back-ndb.h"
#include <lutil.h>
#include "config.h"
extern "C" {
static BI_db_init ndb_db_init;
static BI_db_close ndb_db_close;
static BI_db_open ndb_db_open;
static BI_db_destroy ndb_db_destroy;
}
static struct berval ndb_optable = BER_BVC("OL_opattrs");
static struct berval ndb_opattrs[] = {
BER_BVC("structuralObjectClass"),
BER_BVC("entryUUID"),
BER_BVC("creatorsName"),
BER_BVC("createTimestamp"),
BER_BVC("entryCSN"),
BER_BVC("modifiersName"),
BER_BVC("modifyTimestamp"),
BER_BVNULL
};
static int ndb_oplens[] = {
0, /* structuralOC, default */
36, /* entryUUID */
0, /* creatorsName, default */
26, /* createTimestamp */
40, /* entryCSN */
0, /* modifiersName, default */
26, /* modifyTimestamp */
-1
};
static int
ndb_db_init( BackendDB *be, ConfigReply *cr )
{
struct ndb_info *ni;
int rc = 0;
Debug( LDAP_DEBUG_TRACE,
LDAP_XSTRING(ndb_db_init) ": Initializing ndb database\n",
0, 0, 0 );
/* allocate backend-database-specific stuff */
ni = (struct ndb_info *) ch_calloc( 1, sizeof(struct ndb_info) );
be->be_private = ni;
be->be_cf_ocs = be->bd_info->bi_cf_ocs;
ni->ni_search_stack_depth = DEFAULT_SEARCH_STACK_DEPTH;
ldap_pvt_thread_rdwr_init( &ni->ni_ai_rwlock );
ldap_pvt_thread_rdwr_init( &ni->ni_oc_rwlock );
ldap_pvt_thread_mutex_init( &ni->ni_conn_mutex );
#ifdef DO_MONITORING
rc = ndb_monitor_db_init( be );
#endif
return rc;
}
static int
ndb_db_close( BackendDB *be, ConfigReply *cr );
static int
ndb_db_open( BackendDB *be, ConfigReply *cr )
{
struct ndb_info *ni = (struct ndb_info *) be->be_private;
char sqlbuf[BUFSIZ], *ptr;
int rc, i;
if ( be->be_suffix == NULL ) {
snprintf( cr->msg, sizeof( cr->msg ),
"ndb_db_open: need suffix" );
Debug( LDAP_DEBUG_ANY, "%s\n",
cr->msg, 0, 0 );
return -1;
}
Debug( LDAP_DEBUG_ARGS,
LDAP_XSTRING(ndb_db_open) ": \"%s\"\n",
be->be_suffix[0].bv_val, 0, 0 );
if ( ni->ni_nconns < 1 )
ni->ni_nconns = 1;
ni->ni_cluster = (Ndb_cluster_connection **)ch_malloc( ni->ni_nconns * sizeof( Ndb_cluster_connection *));
for ( i=0; i<ni->ni_nconns; i++ ) {
ni->ni_cluster[i] = new Ndb_cluster_connection( ni->ni_connectstr );
rc = ni->ni_cluster[i]->connect( 4, 5, 1 );
}
for ( i=0; i<ni->ni_nconns; i++ ) {
rc = ni->ni_cluster[i]->wait_until_ready( 30, 0 );
}
mysql_init( &ni->ni_sql );
if ( !mysql_real_connect( &ni->ni_sql, ni->ni_hostname, ni->ni_username, ni->ni_password,
"", ni->ni_port, ni->ni_socket, ni->ni_clflag )) {
snprintf( cr->msg, sizeof( cr->msg ),
"ndb_db_open: mysql_real_connect failed, %s (%d)",
mysql_error(&ni->ni_sql), mysql_errno(&ni->ni_sql) );
rc = -1;
goto fail;
}
sprintf( sqlbuf, "CREATE DATABASE IF NOT EXISTS %s", ni->ni_dbname );
rc = mysql_query( &ni->ni_sql, sqlbuf );
if ( rc ) {
snprintf( cr->msg, sizeof( cr->msg ),
"ndb_db_open: CREATE DATABASE %s failed, %s (%d)",
ni->ni_dbname, mysql_error(&ni->ni_sql), mysql_errno(&ni->ni_sql) );
goto fail;
}
sprintf( sqlbuf, "USE %s", ni->ni_dbname );
rc = mysql_query( &ni->ni_sql, sqlbuf );
if ( rc ) {
snprintf( cr->msg, sizeof( cr->msg ),
"ndb_db_open: USE DATABASE %s failed, %s (%d)",
ni->ni_dbname, mysql_error(&ni->ni_sql), mysql_errno(&ni->ni_sql) );
goto fail;
}
ptr = sqlbuf;
ptr += sprintf( ptr, "CREATE TABLE IF NOT EXISTS " DN2ID_TABLE " ("
"eid bigint unsigned NOT NULL, "
"object_classes VARCHAR(1024) NOT NULL, "
"a0 VARCHAR(128) NOT NULL DEFAULT '', "
"a1 VARCHAR(128) NOT NULL DEFAULT '', "
"a2 VARCHAR(128) NOT NULL DEFAULT '', "
"a3 VARCHAR(128) NOT NULL DEFAULT '', "
"a4 VARCHAR(128) NOT NULL DEFAULT '', "
"a5 VARCHAR(128) NOT NULL DEFAULT '', "
"a6 VARCHAR(128) NOT NULL DEFAULT '', "
"a7 VARCHAR(128) NOT NULL DEFAULT '', "
"a8 VARCHAR(128) NOT NULL DEFAULT '', "
"a9 VARCHAR(128) NOT NULL DEFAULT '', "
"a10 VARCHAR(128) NOT NULL DEFAULT '', "
"a11 VARCHAR(128) NOT NULL DEFAULT '', "
"a12 VARCHAR(128) NOT NULL DEFAULT '', "
"a13 VARCHAR(128) NOT NULL DEFAULT '', "
"a14 VARCHAR(128) NOT NULL DEFAULT '', "
"a15 VARCHAR(128) NOT NULL DEFAULT '', "
"PRIMARY KEY (a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15), "
"UNIQUE KEY eid (eid) USING HASH" );
/* Create index columns */
if ( ni->ni_attridxs ) {
ListNode *ln;
int newcol = 0;
*ptr++ = ',';
*ptr++ = ' ';
for ( ln = ni->ni_attridxs; ln; ln=ln->ln_next ) {
NdbAttrInfo *ai = (NdbAttrInfo *)ln->ln_data;
ptr += sprintf( ptr, "`%s` VARCHAR(%d), ",
ai->na_name.bv_val, ai->na_len );
}
ptr = lutil_strcopy(ptr, "KEY " INDEX_NAME " (" );
for ( ln = ni->ni_attridxs; ln; ln=ln->ln_next ) {
NdbAttrInfo *ai = (NdbAttrInfo *)ln->ln_data;
if ( newcol ) *ptr++ = ',';
*ptr++ = '`';
ptr = lutil_strcopy( ptr, ai->na_name.bv_val );
*ptr++ = '`';
ai->na_ixcol = newcol + 18;
newcol++;
}
*ptr++ = ')';
}
strcpy( ptr, ") ENGINE=ndb" );
rc = mysql_query( &ni->ni_sql, sqlbuf );
if ( rc ) {
snprintf( cr->msg, sizeof( cr->msg ),
"ndb_db_open: CREATE TABLE " DN2ID_TABLE " failed, %s (%d)",
mysql_error(&ni->ni_sql), mysql_errno(&ni->ni_sql) );
goto fail;
}
rc = mysql_query( &ni->ni_sql, "CREATE TABLE IF NOT EXISTS " NEXTID_TABLE " ("
"a bigint unsigned AUTO_INCREMENT PRIMARY KEY ) ENGINE=ndb" );
if ( rc ) {
snprintf( cr->msg, sizeof( cr->msg ),
"ndb_db_open: CREATE TABLE " NEXTID_TABLE " failed, %s (%d)",
mysql_error(&ni->ni_sql), mysql_errno(&ni->ni_sql) );
goto fail;
}
{
NdbOcInfo *oci;
rc = ndb_aset_get( ni, &ndb_optable, ndb_opattrs, &oci );
if ( rc ) {
snprintf( cr->msg, sizeof( cr->msg ),
"ndb_db_open: ndb_aset_get( %s ) failed (%d)",
ndb_optable.bv_val, rc );
goto fail;
}
for ( i=0; ndb_oplens[i] >= 0; i++ ) {
if ( ndb_oplens[i] )
oci->no_attrs[i]->na_len = ndb_oplens[i];
}
rc = ndb_aset_create( ni, oci );
if ( rc ) {
snprintf( cr->msg, sizeof( cr->msg ),
"ndb_db_open: ndb_aset_create( %s ) failed (%d)",
ndb_optable.bv_val, rc );
goto fail;
}
ni->ni_opattrs = oci;
}
/* Create attribute sets */
{
ListNode *ln;
for ( ln = ni->ni_attrsets; ln; ln=ln->ln_next ) {
NdbOcInfo *oci = (NdbOcInfo *)ln->ln_data;
rc = ndb_aset_create( ni, oci );
if ( rc ) {
snprintf( cr->msg, sizeof( cr->msg ),
"ndb_db_open: ndb_aset_create( %s ) failed (%d)",
oci->no_name.bv_val, rc );
goto fail;
}
}
}
/* Initialize any currently used objectClasses */
{
Ndb *ndb;
const NdbDictionary::Dictionary *myDict;
ndb = new Ndb( ni->ni_cluster[0], ni->ni_dbname );
ndb->init(1024);
myDict = ndb->getDictionary();
ndb_oc_read( ni, myDict );
delete ndb;
}
#ifdef DO_MONITORING
/* monitor setup */
rc = ndb_monitor_db_open( be );
if ( rc != 0 ) {
goto fail;
}
#endif
return 0;
fail:
Debug( LDAP_DEBUG_ANY, "%s\n",
cr->msg, 0, 0 );
ndb_db_close( be, NULL );
return rc;
}
static int
ndb_db_close( BackendDB *be, ConfigReply *cr )
{
int i, rc;
struct ndb_info *ni = (struct ndb_info *) be->be_private;
mysql_close( &ni->ni_sql );
for ( i=0; i<ni->ni_nconns; i++ ) {
delete ni->ni_cluster[i];
ni->ni_cluster[i] = NULL;
}
ch_free( ni->ni_cluster );
ni->ni_cluster = NULL;
#ifdef DO_MONITORING
/* monitor handling */
(void)ndb_monitor_db_close( be );
#endif
return 0;
}
static int
ndb_db_destroy( BackendDB *be, ConfigReply *cr )
{
struct ndb_info *ni = (struct ndb_info *) be->be_private;
#ifdef DO_MONITORING
/* monitor handling */
(void)ndb_monitor_db_destroy( be );
#endif
ldap_pvt_thread_mutex_destroy( &ni->ni_conn_mutex );
ldap_pvt_thread_rdwr_destroy( &ni->ni_ai_rwlock );
ldap_pvt_thread_rdwr_destroy( &ni->ni_oc_rwlock );
ch_free( ni );
be->be_private = NULL;
return 0;
}
extern "C" int
ndb_back_initialize(
BackendInfo *bi )
{
int rc = 0;
/* initialize the underlying database system */
Debug( LDAP_DEBUG_TRACE,
LDAP_XSTRING(ndb_back_initialize) ": initialize ndb backend\n", 0, 0, 0 );
ndb_init();
bi->bi_open = 0;
bi->bi_close = 0;
bi->bi_config = 0;
bi->bi_destroy = 0;
bi->bi_db_init = ndb_db_init;
bi->bi_db_config = config_generic_wrapper;
bi->bi_db_open = ndb_db_open;
bi->bi_db_close = ndb_db_close;
bi->bi_db_destroy = ndb_db_destroy;
bi->bi_op_add = ndb_back_add;
bi->bi_op_bind = ndb_back_bind;
bi->bi_op_compare = ndb_back_compare;
bi->bi_op_delete = ndb_back_delete;
bi->bi_op_modify = ndb_back_modify;
bi->bi_op_modrdn = ndb_back_modrdn;
bi->bi_op_search = ndb_back_search;
bi->bi_op_unbind = 0;
#if 0
bi->bi_extended = ndb_extended;
bi->bi_chk_referrals = ndb_referrals;
#endif
bi->bi_operational = ndb_operational;
bi->bi_has_subordinates = ndb_has_subordinates;
bi->bi_entry_release_rw = 0;
bi->bi_entry_get_rw = ndb_entry_get;
/*
* hooks for slap tools
*/
bi->bi_tool_entry_open = ndb_tool_entry_open;
bi->bi_tool_entry_close = ndb_tool_entry_close;
bi->bi_tool_entry_first = ndb_tool_entry_first;
bi->bi_tool_entry_next = ndb_tool_entry_next;
bi->bi_tool_entry_get = ndb_tool_entry_get;
bi->bi_tool_entry_put = ndb_tool_entry_put;
#if 0
bi->bi_tool_entry_reindex = ndb_tool_entry_reindex;
bi->bi_tool_sync = 0;
bi->bi_tool_dn2id_get = ndb_tool_dn2id_get;
bi->bi_tool_entry_modify = ndb_tool_entry_modify;
#endif
bi->bi_connection_init = 0;
bi->bi_connection_destroy = 0;
rc = ndb_back_init_cf( bi );
return rc;
}
#if SLAPD_NDB == SLAPD_MOD_DYNAMIC
/* conditionally define the init_module() function */
SLAP_BACKEND_INIT_MODULE( ndb )
#endif /* SLAPD_NDB == SLAPD_MOD_DYNAMIC */

View File

@ -0,0 +1,458 @@
/* modify.cpp - ndb backend modify routine */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
* Copyright 2008 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 initially developed by Howard Chu for inclusion
* in OpenLDAP Software. This work was sponsored by MySQL.
*/
#include "portable.h"
#include <stdio.h>
#include <ac/string.h>
#include <ac/time.h>
#include "back-ndb.h"
int ndb_modify_internal(
Operation *op,
NdbArgs *NA,
const char **text,
char *textbuf,
size_t textlen )
{
struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
Modification *mod;
Modifications *ml;
Modifications *modlist = op->orm_modlist;
NdbAttrInfo **modai, *atmp;
const NdbDictionary::Dictionary *myDict;
const NdbDictionary::Table *myTable;
int got_oc = 0, nmods = 0, nai = 0, i;
int rc, err, indexed = 0;
Debug( LDAP_DEBUG_TRACE, "ndb_modify_internal: 0x%08lx: %s\n",
NA->e->e_id, NA->e->e_dn, 0);
if ( !acl_check_modlist( op, NA->e, modlist )) {
return LDAP_INSUFFICIENT_ACCESS;
}
for ( ml = modlist; ml != NULL; ml = ml->sml_next ) {
mod = &ml->sml_mod;
nmods++;
switch ( mod->sm_op ) {
case LDAP_MOD_ADD:
Debug(LDAP_DEBUG_ARGS,
"ndb_modify_internal: add %s\n",
mod->sm_desc->ad_cname.bv_val, 0, 0);
err = modify_add_values( NA->e, mod, get_permissiveModify(op),
text, textbuf, textlen );
if( err != LDAP_SUCCESS ) {
Debug(LDAP_DEBUG_ARGS, "ndb_modify_internal: %d %s\n",
err, *text, 0);
}
break;
case LDAP_MOD_DELETE:
Debug(LDAP_DEBUG_ARGS,
"ndb_modify_internal: delete %s\n",
mod->sm_desc->ad_cname.bv_val, 0, 0);
err = modify_delete_values( NA->e, mod, get_permissiveModify(op),
text, textbuf, textlen );
assert( err != LDAP_TYPE_OR_VALUE_EXISTS );
if( err != LDAP_SUCCESS ) {
Debug(LDAP_DEBUG_ARGS, "ndb_modify_internal: %d %s\n",
err, *text, 0);
}
break;
case LDAP_MOD_REPLACE:
Debug(LDAP_DEBUG_ARGS,
"ndb_modify_internal: replace %s\n",
mod->sm_desc->ad_cname.bv_val, 0, 0);
err = modify_replace_values( NA->e, mod, get_permissiveModify(op),
text, textbuf, textlen );
if( err != LDAP_SUCCESS ) {
Debug(LDAP_DEBUG_ARGS, "ndb_modify_internal: %d %s\n",
err, *text, 0);
}
break;
case LDAP_MOD_INCREMENT:
Debug(LDAP_DEBUG_ARGS,
"ndb_modify_internal: increment %s\n",
mod->sm_desc->ad_cname.bv_val, 0, 0);
err = modify_increment_values( NA->e, mod, get_permissiveModify(op),
text, textbuf, textlen );
if( err != LDAP_SUCCESS ) {
Debug(LDAP_DEBUG_ARGS,
"ndb_modify_internal: %d %s\n",
err, *text, 0);
}
break;
case SLAP_MOD_SOFTADD:
Debug(LDAP_DEBUG_ARGS,
"ndb_modify_internal: softadd %s\n",
mod->sm_desc->ad_cname.bv_val, 0, 0);
mod->sm_op = LDAP_MOD_ADD;
err = modify_add_values( NA->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, "ndb_modify_internal: %d %s\n",
err, *text, 0);
}
break;
default:
Debug(LDAP_DEBUG_ANY, "ndb_modify_internal: invalid op %d\n",
mod->sm_op, 0, 0);
*text = "Invalid modify operation";
err = LDAP_OTHER;
Debug(LDAP_DEBUG_ARGS, "ndb_modify_internal: %d %s\n",
err, *text, 0);
}
if ( err != LDAP_SUCCESS ) {
return err;
}
/* If objectClass was modified, reset the flags */
if ( mod->sm_desc == slap_schema.si_ad_objectClass ) {
NA->e->e_ocflags = 0;
got_oc = 1;
}
}
/* check that the entry still obeys the schema */
rc = entry_schema_check( op, NA->e, NULL, get_relax(op), 0,
text, textbuf, textlen );
if ( rc != LDAP_SUCCESS || op->o_noop ) {
if ( rc != LDAP_SUCCESS ) {
Debug( LDAP_DEBUG_ANY,
"entry failed schema check: %s\n",
*text, 0, 0 );
}
return rc;
}
/* apply modifications to DB */
modai = (NdbAttrInfo **)op->o_tmpalloc( nmods * sizeof(NdbAttrInfo*), op->o_tmpmemctx );
/* Get the unique list of modified attributes */
ldap_pvt_thread_rdwr_rlock( &ni->ni_ai_rwlock );
for ( ml = modlist; ml != NULL; ml = ml->sml_next ) {
/* Already took care of objectclass */
if ( ml->sml_desc == slap_schema.si_ad_objectClass )
continue;
for ( i=0; i<nai; i++ ) {
if ( ml->sml_desc->ad_type == modai[i]->na_attr )
break;
}
/* This attr was already updated */
if ( i < nai )
continue;
modai[nai] = ndb_ai_find( ni, ml->sml_desc->ad_type );
if ( modai[nai]->na_flag & NDB_INFO_INDEX )
indexed++;
nai++;
}
ldap_pvt_thread_rdwr_runlock( &ni->ni_ai_rwlock );
if ( got_oc || indexed ) {
rc = ndb_entry_put_info( op->o_bd, NA, 1 );
if ( rc ) return rc;
}
myDict = NA->ndb->getDictionary();
/* One operation per table... */
for ( i=0; i<nai; i++ ) {
NdbOperation *myOp;
int j;
if ( !modai[i] ) continue;
atmp = modai[i];
modai[i] = NULL;
myTable = myDict->getTable( atmp->na_oi->no_table.bv_val );
if ( !myTable ) continue;
myOp = NULL;
nmods = 0;
rc = ndb_oc_attrs( NA->txn, myTable, NA->e, atmp->na_oi, &atmp, 1, 1, &nmods, &myOp );
if ( rc ) return rc;
for ( j=i+1; j<nai; j++ ) {
if ( !modai[j] ) continue;
if ( modai[j]->na_oi == atmp->na_oi ) {
atmp = modai[j];
modai[j] = NULL;
rc = ndb_oc_attrs( NA->txn, myTable, NA->e, atmp->na_oi, &atmp, 1, 1, &nmods, &myOp );
if ( rc ) return rc;
}
}
}
return 0;
}
int
ndb_back_modify( Operation *op, SlapReply *rs )
{
struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
Entry e = {0};
int manageDSAit = get_manageDSAit( op );
char textbuf[SLAP_TEXT_BUFLEN];
size_t textlen = sizeof textbuf;
int num_retries = 0;
NdbArgs NA;
NdbRdns rdns;
struct berval matched;
LDAPControl **preread_ctrl = NULL;
LDAPControl **postread_ctrl = NULL;
LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
int num_ctrls = 0;
int rc;
Debug( LDAP_DEBUG_ARGS, LDAP_XSTRING(ndb_back_modify) ": %s\n",
op->o_req_dn.bv_val, 0, 0 );
ctrls[num_ctrls] = NULL;
slap_mods_opattrs( op, &op->orm_modlist, 1 );
e.e_name = op->o_req_dn;
e.e_nname = op->o_req_ndn;
/* Get our NDB handle */
rs->sr_err = ndb_thread_handle( op, &NA.ndb );
rdns.nr_num = 0;
NA.rdns = &rdns;
NA.e = &e;
if( 0 ) {
retry: /* transaction retry */
NA.txn->close();
NA.txn = NULL;
if( e.e_attrs ) {
attrs_free( e.e_attrs );
e.e_attrs = NULL;
}
Debug(LDAP_DEBUG_TRACE,
LDAP_XSTRING(ndb_back_modify) ": retrying...\n", 0, 0, 0);
if ( op->o_abandon ) {
rs->sr_err = SLAPD_ABANDON;
goto return_results;
}
if ( NA.ocs ) {
ber_bvarray_free( NA.ocs );
}
ndb_trans_backoff( ++num_retries );
}
NA.ocs = NULL;
/* begin transaction */
NA.txn = NA.ndb->startTransaction();
rs->sr_text = NULL;
if( !NA.txn ) {
Debug( LDAP_DEBUG_TRACE,
LDAP_XSTRING(ndb_back_modify) ": startTransaction failed: %s (%d)\n",
NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 );
rs->sr_err = LDAP_OTHER;
rs->sr_text = "internal error";
goto return_results;
}
/* get entry or ancestor */
rs->sr_err = ndb_entry_get_info( op->o_bd, &NA, 0, &matched );
switch( rs->sr_err ) {
case 0:
break;
case LDAP_NO_SUCH_OBJECT:
Debug( LDAP_DEBUG_ARGS,
"<=- ndb_back_modify: no such object %s\n",
op->o_req_dn.bv_val, 0, 0 );
rs->sr_matched = matched.bv_val;
goto return_results;
#if 0
case DB_LOCK_DEADLOCK:
case DB_LOCK_NOTGRANTED:
goto retry;
#endif
case LDAP_BUSY:
rs->sr_text = "ldap server busy";
goto return_results;
default:
rs->sr_err = LDAP_OTHER;
rs->sr_text = "internal error";
goto return_results;
}
/* acquire and lock entry */
rs->sr_err = ndb_entry_get_data( op->o_bd, &NA, 1 );
if ( !manageDSAit && is_entry_referral( &e ) ) {
/* entry is a referral, don't allow modify */
rs->sr_ref = get_entry_referrals( op, &e );
Debug( LDAP_DEBUG_TRACE,
LDAP_XSTRING(ndb_back_modify) ": entry is referral\n",
0, 0, 0 );
rs->sr_err = LDAP_REFERRAL;
rs->sr_matched = e.e_name.bv_val;
rs->sr_flags = REP_REF_MUSTBEFREED;
goto return_results;
}
if ( get_assert( op ) &&
( test_filter( op, &e, (Filter*)get_assertion( op )) != LDAP_COMPARE_TRUE ))
{
rs->sr_err = LDAP_ASSERTION_FAILED;
goto return_results;
}
if( op->o_preread ) {
if( preread_ctrl == NULL ) {
preread_ctrl = &ctrls[num_ctrls++];
ctrls[num_ctrls] = NULL;
}
if ( slap_read_controls( op, rs, &e,
&slap_pre_read_bv, preread_ctrl ) )
{
Debug( LDAP_DEBUG_TRACE,
"<=- " LDAP_XSTRING(ndb_back_modify) ": pre-read "
"failed!\n", 0, 0, 0 );
if ( op->o_preread & SLAP_CONTROL_CRITICAL ) {
/* FIXME: is it correct to abort
* operation if control fails? */
goto return_results;
}
}
}
/* Modify the entry */
rs->sr_err = ndb_modify_internal( op, &NA, &rs->sr_text, textbuf, textlen );
if( rs->sr_err != LDAP_SUCCESS ) {
Debug( LDAP_DEBUG_TRACE,
LDAP_XSTRING(ndb_back_modify) ": modify failed (%d)\n",
rs->sr_err, 0, 0 );
#if 0
switch( rs->sr_err ) {
case DB_LOCK_DEADLOCK:
case DB_LOCK_NOTGRANTED:
goto retry;
}
#endif
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, &e,
&slap_post_read_bv, postread_ctrl ) )
{
Debug( LDAP_DEBUG_TRACE,
"<=- " LDAP_XSTRING(ndb_back_modify)
": post-read failed!\n", 0, 0, 0 );
if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
/* FIXME: is it correct to abort
* operation if control fails? */
goto return_results;
}
}
}
if( op->o_noop ) {
if ( ( rs->sr_err = NA.txn->execute( Rollback ) ) != 0 ) {
rs->sr_text = "txn_abort (no-op) failed";
} else {
rs->sr_err = LDAP_X_NO_OPERATION;
}
} else {
if ( ( rs->sr_err = NA.txn->execute( Commit ) ) != 0 ) {
rs->sr_text = "txn_commit failed";
} else {
rs->sr_err = LDAP_SUCCESS;
}
}
if( rs->sr_err != LDAP_SUCCESS && rs->sr_err != LDAP_X_NO_OPERATION ) {
Debug( LDAP_DEBUG_TRACE,
LDAP_XSTRING(ndb_back_modify) ": txn_%s failed: %s (%d)\n",
op->o_noop ? "abort (no-op)" : "commit",
NA.txn->getNdbError().message, NA.txn->getNdbError().code );
rs->sr_err = LDAP_OTHER;
goto return_results;
}
NA.txn->close();
NA.txn = NULL;
Debug( LDAP_DEBUG_TRACE,
LDAP_XSTRING(ndb_back_modify) ": updated%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 ( NA.ocs ) {
ber_bvarray_free( NA.ocs );
NA.ocs = NULL;
}
if ( e.e_attrs != NULL ) {
attrs_free( e.e_attrs );
e.e_attrs = NULL;
}
if( NA.txn != NULL ) {
NA.txn->execute( Rollback );
NA.txn->close();
}
send_ldap_result( op, rs );
slap_graduate_commit_csn( op );
if( preread_ctrl != NULL && (*preread_ctrl) != NULL ) {
slap_sl_free( (*preread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
slap_sl_free( *preread_ctrl, op->o_tmpmemctx );
}
if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) {
slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
slap_sl_free( *postread_ctrl, op->o_tmpmemctx );
}
rs->sr_text = NULL;
return rs->sr_err;
}

View File

@ -0,0 +1,504 @@
/* modrdn.cpp - ndb backend modrdn routine */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
* Copyright 2008 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 initially developed by Howard Chu for inclusion
* in OpenLDAP Software. This work was sponsored by MySQL.
*/
#include "portable.h"
#include <stdio.h>
#include <ac/string.h>
#include "back-ndb.h"
int
ndb_back_modrdn( Operation *op, SlapReply *rs )
{
struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
AttributeDescription *children = slap_schema.si_ad_children;
AttributeDescription *entry = slap_schema.si_ad_entry;
struct berval new_dn = BER_BVNULL, new_ndn = BER_BVNULL;
Entry e = {0};
Entry e2 = {0};
char textbuf[SLAP_TEXT_BUFLEN];
size_t textlen = sizeof textbuf;
struct berval *np_dn = NULL; /* newSuperior dn */
struct berval *np_ndn = NULL; /* newSuperior ndn */
int manageDSAit = get_manageDSAit( op );
int num_retries = 0;
NdbArgs NA, NA2;
NdbRdns rdns, rdn2;
struct berval matched;
LDAPControl **preread_ctrl = NULL;
LDAPControl **postread_ctrl = NULL;
LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
int num_ctrls = 0;
int rc;
Debug( LDAP_DEBUG_ARGS, "==>" LDAP_XSTRING(ndb_back_modrdn) "(%s,%s,%s)\n",
op->o_req_dn.bv_val,op->oq_modrdn.rs_newrdn.bv_val,
op->oq_modrdn.rs_newSup ? op->oq_modrdn.rs_newSup->bv_val : "NULL" );
ctrls[num_ctrls] = NULL;
slap_mods_opattrs( op, &op->orr_modlist, 1 );
e.e_name = op->o_req_dn;
e.e_nname = op->o_req_ndn;
/* Get our NDB handle */
rs->sr_err = ndb_thread_handle( op, &NA.ndb );
rdns.nr_num = 0;
NA.rdns = &rdns;
NA.e = &e;
NA2.ndb = NA.ndb;
NA2.e = &e2;
NA2.rdns = &rdn2;
if( 0 ) {
retry: /* transaction retry */
NA.txn->close();
NA.txn = NULL;
if ( e.e_attrs ) {
attrs_free( e.e_attrs );
e.e_attrs = NULL;
}
Debug( LDAP_DEBUG_TRACE, "==>" LDAP_XSTRING(ndb_back_modrdn)
": retrying...\n", 0, 0, 0 );
if ( op->o_abandon ) {
rs->sr_err = SLAPD_ABANDON;
goto return_results;
}
if ( NA.ocs ) {
ber_bvarray_free( NA.ocs );
}
ndb_trans_backoff( ++num_retries );
}
NA.ocs = NULL;
/* begin transaction */
NA.txn = NA.ndb->startTransaction();
rs->sr_text = NULL;
if( !NA.txn ) {
Debug( LDAP_DEBUG_TRACE,
LDAP_XSTRING(ndb_back_modrdn) ": startTransaction failed: %s (%d)\n",
NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 );
rs->sr_err = LDAP_OTHER;
rs->sr_text = "internal error";
goto return_results;
}
NA2.txn = NA.txn;
/* get entry */
rs->sr_err = ndb_entry_get_info( op->o_bd, &NA, 1, &matched );
switch( rs->sr_err ) {
case 0:
break;
case LDAP_NO_SUCH_OBJECT:
Debug( LDAP_DEBUG_ARGS,
"<=- ndb_back_modrdn: no such object %s\n",
op->o_req_dn.bv_val, 0, 0 );
rs->sr_matched = matched.bv_val;
goto return_results;
#if 0
case DB_LOCK_DEADLOCK:
case DB_LOCK_NOTGRANTED:
goto retry;
#endif
case LDAP_BUSY:
rs->sr_text = "ldap server busy";
goto return_results;
default:
rs->sr_err = LDAP_OTHER;
rs->sr_text = "internal error";
goto return_results;
}
/* acquire and lock entry */
rs->sr_err = ndb_entry_get_data( op->o_bd, &NA, 1 );
if ( get_assert( op ) &&
( test_filter( op, &e, (Filter *)get_assertion( op )) != LDAP_COMPARE_TRUE ))
{
rs->sr_err = LDAP_ASSERTION_FAILED;
goto return_results;
}
/* check write on old entry */
rs->sr_err = access_allowed( op, &e, entry, NULL, ACL_WRITE, NULL );
if ( ! rs->sr_err ) {
Debug( LDAP_DEBUG_TRACE, "no access to entry\n", 0,
0, 0 );
rs->sr_text = "no write access to old entry";
rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
goto return_results;
}
/* Can't do it if we have kids */
rs->sr_err = ndb_has_children( &NA, &rc );
if ( rs->sr_err ) {
Debug(LDAP_DEBUG_ARGS,
"<=- " LDAP_XSTRING(ndb_back_modrdn)
": has_children failed: %s (%d)\n",
NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 );
rs->sr_err = LDAP_OTHER;
rs->sr_text = "internal error";
goto return_results;
}
if ( rc == LDAP_COMPARE_TRUE ) {
Debug(LDAP_DEBUG_ARGS,
"<=- " LDAP_XSTRING(ndb_back_modrdn)
": non-leaf %s\n",
op->o_req_dn.bv_val, 0, 0);
rs->sr_err = LDAP_NOT_ALLOWED_ON_NONLEAF;
rs->sr_text = "subtree rename not supported";
goto return_results;
}
if (!manageDSAit && is_entry_referral( &e ) ) {
/* entry is a referral, don't allow modrdn */
rs->sr_ref = get_entry_referrals( op, &e );
Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(ndb_back_modrdn)
": entry %s is referral\n", e.e_dn, 0, 0 );
rs->sr_err = LDAP_REFERRAL,
rs->sr_matched = op->o_req_dn.bv_val;
rs->sr_flags = REP_REF_MUSTBEFREED;
goto return_results;
}
if ( be_issuffix( op->o_bd, &e.e_nname ) ) {
/* There can only be one suffix entry */
rs->sr_err = LDAP_NAMING_VIOLATION;
rs->sr_text = "cannot rename suffix entry";
goto return_results;
} else {
dnParent( &e.e_nname, &e2.e_nname );
dnParent( &e.e_name, &e2.e_name );
}
/* check parent for "children" acl */
rs->sr_err = access_allowed( op, &e2,
children, NULL,
op->oq_modrdn.rs_newSup == NULL ?
ACL_WRITE : ACL_WDEL,
NULL );
if ( ! rs->sr_err ) {
rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
Debug( LDAP_DEBUG_TRACE, "no access to parent\n", 0,
0, 0 );
rs->sr_text = "no write access to old parent's children";
goto return_results;
}
Debug( LDAP_DEBUG_TRACE,
LDAP_XSTRING(ndb_back_modrdn) ": wr to children "
"of entry %s OK\n", e2.e_name.bv_val, 0, 0 );
if ( op->oq_modrdn.rs_newSup != NULL ) {
Debug( LDAP_DEBUG_TRACE,
LDAP_XSTRING(ndb_back_modrdn)
": new parent \"%s\" requested...\n",
op->oq_modrdn.rs_newSup->bv_val, 0, 0 );
/* newSuperior == oldParent? */
if( dn_match( &e2.e_nname, op->oq_modrdn.rs_nnewSup ) ) {
Debug( LDAP_DEBUG_TRACE, "bdb_back_modrdn: "
"new parent \"%s\" same as the old parent \"%s\"\n",
op->oq_modrdn.rs_newSup->bv_val, e2.e_name.bv_val, 0 );
op->oq_modrdn.rs_newSup = NULL; /* ignore newSuperior */
}
}
if ( op->oq_modrdn.rs_newSup != NULL ) {
if ( op->oq_modrdn.rs_newSup->bv_len ) {
rdn2.nr_num = 0;
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? */
e2.e_name = *np_dn;
e2.e_nname = *np_ndn;
NA2.ocs = &matched;
rs->sr_err = ndb_entry_get_info( op->o_bd, &NA2, 1, NULL );
switch( rs->sr_err ) {
case 0:
break;
case LDAP_NO_SUCH_OBJECT:
Debug( LDAP_DEBUG_TRACE,
LDAP_XSTRING(ndb_back_modrdn)
": newSup(ndn=%s) not here!\n",
np_ndn->bv_val, 0, 0);
rs->sr_text = "new superior not found";
goto return_results;
#if 0
case DB_LOCK_DEADLOCK:
case DB_LOCK_NOTGRANTED:
goto retry;
#endif
case LDAP_BUSY:
rs->sr_text = "ldap server busy";
goto return_results;
default:
rs->sr_err = LDAP_OTHER;
rs->sr_text = "internal error";
goto return_results;
}
}
/* check newSuperior for "children" acl */
rs->sr_err = access_allowed( op, &e2, children,
NULL, ACL_WADD, NULL );
if( ! rs->sr_err ) {
Debug( LDAP_DEBUG_TRACE,
LDAP_XSTRING(ndb_back_modrdn)
": no wr to newSup children\n",
0, 0, 0 );
rs->sr_text = "no write access to new superior's children";
rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
goto return_results;
}
Debug( LDAP_DEBUG_TRACE,
LDAP_XSTRING(ndb_back_modrdn)
": wr to new parent OK id=%ld\n",
(long) e2.e_id, 0, 0 );
}
/* Build target dn and make sure target entry doesn't exist already. */
if (!new_dn.bv_val) {
build_new_dn( &new_dn, &e2.e_name, &op->oq_modrdn.rs_newrdn, NULL );
}
if (!new_ndn.bv_val) {
build_new_dn( &new_ndn, &e2.e_nname, &op->oq_modrdn.rs_nnewrdn, NULL );
}
Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(ndb_back_modrdn) ": new ndn=%s\n",
new_ndn.bv_val, 0, 0 );
/* Allow rename to same DN */
if ( !bvmatch ( &new_ndn, &e.e_nname )) {
rdn2.nr_num = 0;
e2.e_name = new_dn;
e2.e_nname = new_ndn;
NA2.ocs = &matched;
rs->sr_err = ndb_entry_get_info( op->o_bd, &NA2, 1, NULL );
switch( rs->sr_err ) {
#if 0
case DB_LOCK_DEADLOCK:
case DB_LOCK_NOTGRANTED:
goto retry;
#endif
case LDAP_NO_SUCH_OBJECT:
break;
case 0:
rs->sr_err = LDAP_ALREADY_EXISTS;
goto return_results;
default:
rs->sr_err = LDAP_OTHER;
rs->sr_text = "internal error";
goto return_results;
}
}
assert( op->orr_modlist != NULL );
if( op->o_preread ) {
if( preread_ctrl == NULL ) {
preread_ctrl = &ctrls[num_ctrls++];
ctrls[num_ctrls] = NULL;
}
if( slap_read_controls( op, rs, &e,
&slap_pre_read_bv, preread_ctrl ) )
{
Debug( LDAP_DEBUG_TRACE,
"<=- " LDAP_XSTRING(ndb_back_modrdn)
": pre-read failed!\n", 0, 0, 0 );
if ( op->o_preread & SLAP_CONTROL_CRITICAL ) {
/* FIXME: is it correct to abort
* operation if control fails? */
goto return_results;
}
}
}
/* delete old DN */
rs->sr_err = ndb_entry_del_info( op->o_bd, &NA );
if ( rs->sr_err != 0 ) {
Debug(LDAP_DEBUG_TRACE,
"<=- " LDAP_XSTRING(ndb_back_modrdn)
": dn2id del failed: %s (%d)\n",
NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 );
#if 0
switch( rs->sr_err ) {
case DB_LOCK_DEADLOCK:
case DB_LOCK_NOTGRANTED:
goto retry;
}
#endif
rs->sr_err = LDAP_OTHER;
rs->sr_text = "DN index delete fail";
goto return_results;
}
/* copy entry fields */
e2.e_attrs = e.e_attrs;
e2.e_id = e.e_id;
/* add new DN */
rs->sr_err = ndb_entry_put_info( op->o_bd, &NA2, 0 );
if ( rs->sr_err != 0 ) {
Debug(LDAP_DEBUG_TRACE,
"<=- " LDAP_XSTRING(ndb_back_modrdn)
": dn2id add failed: %s (%d)\n",
NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 );
#if 0
switch( rs->sr_err ) {
case DB_LOCK_DEADLOCK:
case DB_LOCK_NOTGRANTED:
goto retry;
}
#endif
rs->sr_err = LDAP_OTHER;
rs->sr_text = "DN index add failed";
goto return_results;
}
/* modify entry */
rs->sr_err = ndb_modify_internal( op, &NA2,
&rs->sr_text, textbuf, textlen );
if( rs->sr_err != LDAP_SUCCESS ) {
Debug(LDAP_DEBUG_TRACE,
"<=- " LDAP_XSTRING(ndb_back_modrdn)
": modify failed: %s (%d)\n",
NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 );
#if 0
switch( rs->sr_err ) {
case DB_LOCK_DEADLOCK:
case DB_LOCK_NOTGRANTED:
goto retry;
}
#endif
goto return_results;
}
e.e_attrs = e2.e_attrs;
if( op->o_postread ) {
if( postread_ctrl == NULL ) {
postread_ctrl = &ctrls[num_ctrls++];
ctrls[num_ctrls] = NULL;
}
if( slap_read_controls( op, rs, &e2,
&slap_post_read_bv, postread_ctrl ) )
{
Debug( LDAP_DEBUG_TRACE,
"<=- " LDAP_XSTRING(ndb_back_modrdn)
": post-read failed!\n", 0, 0, 0 );
if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
/* FIXME: is it correct to abort
* operation if control fails? */
goto return_results;
}
}
}
if( op->o_noop ) {
if(( rs->sr_err = NA.txn->execute( Rollback )) != 0 ) {
rs->sr_text = "txn_abort (no-op) failed";
} else {
rs->sr_err = LDAP_X_NO_OPERATION;
}
} else {
if(( rs->sr_err = NA.txn->execute( Commit )) != 0 ) {
rs->sr_text = "txn_commit failed";
} else {
rs->sr_err = LDAP_SUCCESS;
}
}
if( rs->sr_err != LDAP_SUCCESS && rs->sr_err != LDAP_X_NO_OPERATION ) {
Debug( LDAP_DEBUG_TRACE,
LDAP_XSTRING(ndb_back_modrdn) ": txn_%s failed: %s (%d)\n",
op->o_noop ? "abort (no-op)" : "commit",
NA.txn->getNdbError().message, NA.txn->getNdbError().code );
rs->sr_err = LDAP_OTHER;
goto return_results;
}
NA.txn->close();
NA.txn = NULL;
Debug(LDAP_DEBUG_TRACE,
LDAP_XSTRING(ndb_back_modrdn)
": rdn modified%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 ( NA.ocs ) {
ber_bvarray_free( NA.ocs );
NA.ocs = NULL;
}
if ( e.e_attrs ) {
attrs_free( e.e_attrs );
e.e_attrs = NULL;
}
if( NA.txn != NULL ) {
NA.txn->execute( Rollback );
NA.txn->close();
}
send_ldap_result( op, rs );
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 );
if( preread_ctrl != NULL && (*preread_ctrl) != NULL ) {
slap_sl_free( (*preread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
slap_sl_free( *preread_ctrl, op->o_tmpmemctx );
}
if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) {
slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
slap_sl_free( *postread_ctrl, op->o_tmpmemctx );
}
rs->sr_text = NULL;
return rs->sr_err;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,164 @@
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
* Copyright 2008 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 initially developed by Howard Chu for inclusion
* in OpenLDAP Software. This work was sponsored by MySQL.
*/
#ifndef _PROTO_NDB_H
#define _PROTO_NDB_H
LDAP_BEGIN_DECL
extern BI_init ndb_back_initialize;
extern BI_open ndb_back_open;
extern BI_close ndb_back_close;
extern BI_destroy ndb_back_destroy;
extern BI_db_init ndb_back_db_init;
extern BI_db_destroy ndb_back_db_destroy;
extern BI_op_bind ndb_back_bind;
extern BI_op_unbind ndb_back_unbind;
extern BI_op_search ndb_back_search;
extern BI_op_compare ndb_back_compare;
extern BI_op_modify ndb_back_modify;
extern BI_op_modrdn ndb_back_modrdn;
extern BI_op_add ndb_back_add;
extern BI_op_delete ndb_back_delete;
extern BI_operational ndb_operational;
extern BI_has_subordinates ndb_has_subordinates;
extern BI_entry_get_rw ndb_entry_get;
extern BI_tool_entry_open ndb_tool_entry_open;
extern BI_tool_entry_close ndb_tool_entry_close;
extern BI_tool_entry_first ndb_tool_entry_first;
extern BI_tool_entry_next ndb_tool_entry_next;
extern BI_tool_entry_get ndb_tool_entry_get;
extern BI_tool_entry_put ndb_tool_entry_put;
extern BI_tool_dn2id_get ndb_tool_dn2id_get;
extern int ndb_modify_internal(
Operation *op,
NdbArgs *NA,
const char **text,
char *textbuf,
size_t textlen );
extern int
ndb_entry_get_data(
BackendDB *be,
NdbArgs *args,
int update );
extern int
ndb_entry_put_data(
BackendDB *be,
NdbArgs *args,
int update );
extern int
ndb_entry_del_data(
BackendDB *be,
NdbArgs *args );
extern int
ndb_entry_put_info(
BackendDB *be,
NdbArgs *args,
int update );
extern int
ndb_entry_get_info(
BackendDB *be,
NdbArgs *args,
int update,
struct berval *matched );
extern "C" int
ndb_entry_del_info(
BackendDB *be,
NdbArgs *args );
extern int
ndb_dn2rdns(
struct berval *dn,
NdbRdns *buf );
extern NdbAttrInfo *
ndb_ai_find( struct ndb_info *ni, AttributeType *at );
extern NdbAttrInfo *
ndb_ai_get( struct ndb_info *ni, struct berval *at );
extern int
ndb_aset_get( struct ndb_info *ni, struct berval *sname, struct berval *attrs, NdbOcInfo **ret );
extern int
ndb_aset_create( struct ndb_info *ni, NdbOcInfo *oci );
extern int
ndb_oc_read( struct ndb_info *ni, const NdbDictionary::Dictionary *dict );
extern int
ndb_oc_attrs(
NdbTransaction *txn,
const NdbDictionary::Table *myTable,
Entry *e,
NdbOcInfo *no,
NdbAttrInfo **attrs,
int nattrs,
int update,
int *num,
NdbOperation **retop );
extern int
ndb_has_children(
NdbArgs *NA,
int *hasChildren );
extern struct berval *
ndb_str2bvarray(
char *str,
int len,
char delim );
extern struct berval *
ndb_ref2oclist(
const char *ref );
extern int
ndb_next_id(
BackendDB *be,
Ndb *ndb,
ID *id );
extern int
ndb_thread_handle(
Operation *op,
Ndb **ndb );
extern int
ndb_back_init_cf(
BackendInfo *bi );
extern void
ndb_trans_backoff( int num_retries );
LDAP_END_DECL
#endif /* _PROTO_NDB_H */

View File

@ -0,0 +1,660 @@
/* search.cpp - tools for slap tools */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
* Copyright 2008 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 initially developed by Howard Chu for inclusion
* in OpenLDAP Software. This work was sponsored by MySQL.
*/
#include "portable.h"
#include <stdio.h>
#include <ac/string.h>
#include <ac/errno.h>
#include "lutil.h"
#include "back-ndb.h"
static int
ndb_dn2bound(
NdbIndexScanOperation *myop,
NdbRdns *rdns
)
{
unsigned int i;
/* Walk thru RDNs */
for ( i=0; i<rdns->nr_num; i++ ) {
/* Note: RDN_COLUMN offset not needed here */
if ( myop->setBound( i, NdbIndexScanOperation::BoundEQ, rdns->nr_buf[i] ))
return LDAP_OTHER;
}
return i;
}
/* Check that all filter terms reside in the same table.
*
* If any of the filter terms are indexed, then only an IndexScan of the OL_index
* will be performed. If none are indexed, but all the terms reside in a single
* table, a Scan can be performed with the LDAP filter transformed into a ScanFilter.
*
* Otherwise, a full scan of the DB must be done with all filtering done by slapd.
*/
static int ndb_filter_check( struct ndb_info *ni, Filter *f,
NdbOcInfo **oci, int *indexed, int *ocfilter )
{
AttributeDescription *ad = NULL;
ber_tag_t choice = f->f_choice;
int rc = 0, undef = 0;
if ( choice & SLAPD_FILTER_UNDEFINED ) {
choice &= SLAPD_FILTER_MASK;
undef = 1;
}
switch( choice ) {
case LDAP_FILTER_AND:
case LDAP_FILTER_OR:
case LDAP_FILTER_NOT:
for ( f = f->f_list; f; f=f->f_next ) {
rc = ndb_filter_check( ni, f, oci, indexed, ocfilter );
if ( rc ) return rc;
}
break;
case LDAP_FILTER_PRESENT:
ad = f->f_desc;
break;
case LDAP_FILTER_EQUALITY:
case LDAP_FILTER_SUBSTRINGS:
case LDAP_FILTER_GE:
case LDAP_FILTER_LE:
case LDAP_FILTER_APPROX:
ad = f->f_av_desc;
break;
default:
break;
}
if ( ad && !undef ) {
NdbAttrInfo *ai;
/* ObjectClass filtering is in dn2id table */
if ( ad == slap_schema.si_ad_objectClass ) {
if ( choice == LDAP_FILTER_EQUALITY )
(*ocfilter)++;
return 0;
}
ai = ndb_ai_find( ni, ad->ad_type );
if ( ai ) {
if ( ai->na_flag & NDB_INFO_INDEX )
(*indexed)++;
if ( *oci ) {
if ( ai->na_oi != *oci )
rc = -1;
} else {
*oci = ai->na_oi;
}
}
}
return rc;
}
static int ndb_filter_set( Operation *op, struct ndb_info *ni, Filter *f, int indexed,
NdbIndexScanOperation *scan, NdbScanFilter **sf, int *bounds )
{
AttributeDescription *ad = NULL;
ber_tag_t choice = f->f_choice;
int undef = 0;
if ( choice & SLAPD_FILTER_UNDEFINED ) {
choice &= SLAPD_FILTER_MASK;
undef = 1;
}
switch( choice ) {
case LDAP_FILTER_NOT:
/* no indexing for these */
break;
case LDAP_FILTER_OR:
/* FIXME: these bounds aren't right. */
if ( indexed ) {
scan->end_of_bound( (*bounds)++ );
}
case LDAP_FILTER_AND:
for ( f = f->f_list; f; f=f->f_next ) {
if ( ndb_filter_set( op, ni, f, indexed, scan, sf, bounds ))
return -1;
}
break;
case LDAP_FILTER_PRESENT:
ad = f->f_desc;
break;
case LDAP_FILTER_EQUALITY:
case LDAP_FILTER_SUBSTRINGS:
case LDAP_FILTER_GE:
case LDAP_FILTER_LE:
case LDAP_FILTER_APPROX:
ad = f->f_av_desc;
break;
default:
break;
}
if ( ad && !undef ) {
NdbAttrInfo *ai;
/* ObjectClass filtering is in dn2id table */
if ( ad == slap_schema.si_ad_objectClass ) {
return 0;
}
ai = ndb_ai_find( ni, ad->ad_type );
if ( ai ) {
if ( ai->na_flag & NDB_INFO_INDEX ) {
char *buf, *ptr;
NdbIndexScanOperation::BoundType bt;
int rc;
switch(choice) {
case LDAP_FILTER_PRESENT:
rc = scan->setBound( ai->na_ixcol - IDX_COLUMN,
NdbIndexScanOperation::BoundGT, NULL );
break;
case LDAP_FILTER_EQUALITY:
case LDAP_FILTER_APPROX:
bt = NdbIndexScanOperation::BoundEQ;
goto setit;
case LDAP_FILTER_GE:
bt = NdbIndexScanOperation::BoundGE;
goto setit;
case LDAP_FILTER_LE:
bt = NdbIndexScanOperation::BoundLE;
setit:
rc = f->f_av_value.bv_len+1;
if ( ai->na_len > 255 )
rc++;
buf = (char *)op->o_tmpalloc( rc, op->o_tmpmemctx );
rc = f->f_av_value.bv_len;
buf[0] = rc & 0xff;
ptr = buf+1;
if ( ai->na_len > 255 ) {
buf[1] = (rc >> 8);
ptr++;
}
memcpy( ptr, f->f_av_value.bv_val, f->f_av_value.bv_len );
rc = scan->setBound( ai->na_ixcol - IDX_COLUMN, bt, buf );
op->o_tmpfree( buf, op->o_tmpmemctx );
break;
default:
break;
}
} else {
}
}
}
return 0;
}
static int ndb_oc_search( Operation *op, SlapReply *rs, Ndb *ndb, NdbTransaction *txn,
NdbRdns *rbase, NdbOcInfo *oci, int indexed )
{
struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
const NdbDictionary::Dictionary *myDict = ndb->getDictionary();
const NdbDictionary::Table *myTable;
const NdbDictionary::Index *myIndex;
NdbIndexScanOperation *scan;
NdbIndexOperation *ixop;
NdbScanFilter *sf = NULL;
struct berval *ocs, matched;
NdbRecAttr *scanID, *scanOC, *scanDN[NDB_MAX_RDNS];
char dnBuf[2048], *ptr;
NdbRdns rdns;
NdbArgs NA;
char idbuf[2*sizeof(ID)];
char ocbuf[NDB_OC_BUFLEN];
int i, rc, bounds;
Entry e = {0};
Uint64 eid;
myTable = myDict->getTable( oci->no_table.bv_val );
if ( indexed ) {
scan = txn->getNdbIndexScanOperation( INDEX_NAME, DN2ID_TABLE );
if ( !scan )
return LDAP_OTHER;
} else {
myIndex = myDict->getIndex( "eid$unique", DN2ID_TABLE );
if ( !myIndex ) {
Debug( LDAP_DEBUG_ANY, DN2ID_TABLE " eid index is missing!\n", 0, 0, 0 );
rs->sr_err = LDAP_OTHER;
goto leave;
}
scan = (NdbIndexScanOperation *)txn->getNdbScanOperation( myTable );
if ( !scan )
return LDAP_OTHER;
sf = new NdbScanFilter(scan);
if ( !sf )
return LDAP_OTHER;
switch ( op->ors_filter->f_choice ) {
case LDAP_FILTER_AND:
case LDAP_FILTER_OR:
case LDAP_FILTER_NOT:
break;
default:
if ( sf->begin() < 0 ) {
rc = LDAP_OTHER;
goto leave;
}
}
}
scan->readTuples( NdbOperation::LM_CommittedRead );
bounds = 0;
rc = ndb_filter_set( op, ni, op->ors_filter, indexed, scan, &sf, &bounds );
if ( rc )
goto leave;
scanID = scan->getValue( EID_COLUMN, idbuf );
if ( indexed ) {
scanOC = scan->getValue( OCS_COLUMN, ocbuf );
for ( i=0; i<NDB_MAX_RDNS; i++ ) {
rdns.nr_buf[i][0] = '\0';
scanDN[i] = scan->getValue( RDN_COLUMN+i, rdns.nr_buf[i] );
}
}
if ( txn->execute( NdbTransaction::NoCommit, NdbOperation::AbortOnError, 1 )) {
rs->sr_err = LDAP_OTHER;
goto leave;
}
e.e_name.bv_val = dnBuf;
NA.e = &e;
NA.ndb = ndb;
while ( scan->nextResult() == 0 ) {
NdbTransaction *tx2;
if ( op->o_abandon )
break;
eid = scanID->u_64_value();
e.e_id = eid;
if ( !indexed ) {
tx2 = ndb->startTransaction( myTable );
if ( !tx2 ) {
rs->sr_err = LDAP_OTHER;
goto leave;
}
ixop = tx2->getNdbIndexOperation( myIndex );
if ( !ixop ) {
tx2->close();
rs->sr_err = LDAP_OTHER;
goto leave;
}
ixop->readTuple( NdbOperation::LM_CommittedRead );
ixop->equal( EID_COLUMN, eid );
scanOC = ixop->getValue( OCS_COLUMN, ocbuf );
for ( i=0; i<NDB_MAX_RDNS; i++ ) {
rdns.nr_buf[i][0] = '\0';
scanDN[i] = ixop->getValue( RDN_COLUMN+i, rdns.nr_buf[i] );
}
rc = tx2->execute( NdbTransaction::Commit, NdbOperation::AbortOnError, 1 );
tx2->close();
if ( rc ) {
rs->sr_err = LDAP_OTHER;
goto leave;
}
}
ocs = ndb_ref2oclist( ocbuf );
for ( i=0; i<NDB_MAX_RDNS; i++ ) {
if ( scanDN[i]->isNULL() || !rdns.nr_buf[i][0] )
break;
}
rdns.nr_num = i;
/* entry must be subordinate to the base */
if ( i < rbase->nr_num ) {
continue;
}
ptr = dnBuf;
for ( --i; i>=0; i-- ) {
char *buf;
int len;
buf = rdns.nr_buf[i];
len = *buf++;
ptr = lutil_strncopy( ptr, buf, len );
if ( i ) *ptr++ = ',';
}
*ptr = '\0';
e.e_name.bv_len = ptr - dnBuf;
/* More scope checks */
/* If indexed, these can be moved into the ScanFilter */
switch( op->ors_scope ) {
case LDAP_SCOPE_ONELEVEL:
if ( rdns.nr_num != rbase->nr_num+1 )
continue;
case LDAP_SCOPE_SUBORDINATE:
if ( rdns.nr_num == rbase->nr_num )
continue;
case LDAP_SCOPE_SUBTREE:
default:
if ( e.e_name.bv_len <= op->o_req_dn.bv_len ) {
if ( op->ors_scope != LDAP_SCOPE_SUBTREE ||
strcasecmp( op->o_req_dn.bv_val, e.e_name.bv_val ))
continue;
} else if ( strcasecmp( op->o_req_dn.bv_val, e.e_name.bv_val +
e.e_name.bv_len - op->o_req_dn.bv_len ))
continue;
}
dnNormalize( 0, NULL, NULL, &e.e_name, &e.e_nname, op->o_tmpmemctx );
{
#ifdef notdef /* NDBapi is broken here */
Ndb::Key_part_ptr keys[2];
char xbuf[32];
keys[0].ptr = &eid;
keys[0].len = sizeof(eid);
keys[1].ptr = NULL;
keys[1].len = 0;
tx2 = ndb->startTransaction( myTable, keys, xbuf, sizeof(xbuf));
#else
tx2 = ndb->startTransaction( myTable );
#endif
if ( !tx2 ) {
rs->sr_err = LDAP_OTHER;
goto leave;
}
NA.txn = tx2;
NA.ocs = ocs;
rc = ndb_entry_get_data( op->o_bd, &NA, 0 );
tx2->close();
}
ber_bvarray_free( ocs );
rc = test_filter( op, &e, op->ors_filter );
if ( rc == LDAP_COMPARE_TRUE ) {
rs->sr_entry = &e;
rs->sr_attrs = op->ors_attrs;
rs->sr_flags = 0;
rc = send_search_entry( op, rs );
rs->sr_entry = NULL;
} else {
rc = 0;
}
attrs_free( e.e_attrs );
e.e_attrs = NULL;
op->o_tmpfree( e.e_nname.bv_val, op->o_tmpmemctx );
if ( rc ) break;
}
leave:
if ( sf ) delete sf;
return rc;
}
extern "C"
int ndb_back_search( Operation *op, SlapReply *rs )
{
struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
NdbTransaction *txn, *tx2;
NdbIndexScanOperation *scan;
NdbScanFilter *sf = NULL;
Entry e = {0};
int rc, i, ocfilter, indexed;
struct berval matched;
NdbRecAttr *scanID, *scanOC, *scanDN[NDB_MAX_RDNS];
char dnBuf[2048], *ptr;
char idbuf[2*sizeof(ID)];
char ocbuf[NDB_OC_BUFLEN];
NdbRdns rdns;
NdbOcInfo *oci;
NdbArgs NA;
rc = ndb_thread_handle( op, &NA.ndb );
rdns.nr_num = 0;
txn = NA.ndb->startTransaction();
if ( !txn ) {
Debug( LDAP_DEBUG_TRACE,
LDAP_XSTRING(ndb_back_search) ": startTransaction failed: %s (%d)\n",
NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 );
rs->sr_err = LDAP_OTHER;
rs->sr_text = "internal error";
goto leave;
}
NA.txn = txn;
e.e_name = op->o_req_dn;
NA.e = &e;
NA.rdns = &rdns;
NA.ocs = op->ors_scope == LDAP_SCOPE_BASE ? NULL : &matched;
rs->sr_err = ndb_entry_get_info( op->o_bd, &NA, 0, &matched );
if ( rs->sr_err ) {
if ( rs->sr_err == LDAP_NO_SUCH_OBJECT )
rs->sr_matched = matched.bv_val;
goto leave;
}
if ( op->ors_scope == LDAP_SCOPE_BASE ) {
e.e_name = op->o_req_dn;
e.e_nname = op->o_req_ndn;
rc = ndb_entry_get_data( op->o_bd, &NA, 0 );
ber_bvarray_free( NA.ocs );
rc = test_filter( op, &e, op->ors_filter );
if ( rc == LDAP_COMPARE_TRUE ) {
rs->sr_entry = &e;
rs->sr_attrs = op->ors_attrs;
rs->sr_flags = 0;
send_search_entry( op, rs );
rs->sr_entry = NULL;
}
attrs_free( e.e_attrs );
e.e_attrs = NULL;
rs->sr_err = LDAP_SUCCESS;
goto leave;
} else if ( rdns.nr_num == NDB_MAX_RDNS ) {
if ( op->ors_scope == LDAP_SCOPE_ONELEVEL ||
op->ors_scope == LDAP_SCOPE_CHILDREN )
rs->sr_err = LDAP_SUCCESS;
goto leave;
}
/* See if we can handle the filter. Filtering on objectClass is only done
* in the DN2ID table scan. If all other filter terms reside in one table,
* then we scan the OC table instead of the DN2ID table.
*/
oci = NULL;
indexed = 0;
ocfilter = 0;
rc = ndb_filter_check( ni, op->ors_filter, &oci, &indexed, &ocfilter );
if ( rc ) {
Debug( LDAP_DEBUG_TRACE, "ndb_back_search: "
"filter attributes from multiple tables, indexing ignored\n",
0, 0, 0 );
} else if ( oci ) {
rc = ndb_oc_search( op, rs, NA.ndb, txn, &rdns, oci, indexed );
goto leave;
}
scan = txn->getNdbIndexScanOperation( "PRIMARY", DN2ID_TABLE );
scan->readTuples( NdbOperation::LM_CommittedRead );
rc = ndb_dn2bound( scan, &rdns );
switch( op->ors_scope ) {
case LDAP_SCOPE_ONELEVEL:
sf = new NdbScanFilter(scan);
if ( sf->begin() < 0 ||
sf->cmp(NdbScanFilter::COND_NOT_LIKE, rc+3, "_%",
STRLENOF("_%")) < 0 ||
sf->end() < 0 ) {
rs->sr_err = LDAP_OTHER;
goto leave;
}
/* FALLTHRU */
case LDAP_SCOPE_CHILDREN:
/* Note: RDN_COLUMN offset not needed here */
scan->setBound( rc, NdbIndexScanOperation::BoundLT, "\0" );
/* FALLTHRU */
case LDAP_SCOPE_SUBTREE:
break;
}
scanID = scan->getValue( EID_COLUMN, idbuf );
scanOC = scan->getValue( OCS_COLUMN, ocbuf );
for ( i=0; i<NDB_MAX_RDNS; i++ ) {
rdns.nr_buf[i][0] = '\0';
scanDN[i] = scan->getValue( RDN_COLUMN+i, rdns.nr_buf[i] );
}
if ( txn->execute( NdbTransaction::NoCommit, NdbOperation::AbortOnError, 1 )) {
rs->sr_err = LDAP_OTHER;
goto leave;
}
e.e_name.bv_val = dnBuf;
while ( scan->nextResult() == 0 ) {
if ( op->o_abandon )
break;
e.e_id = scanID->u_64_value();
NA.ocs = ndb_ref2oclist( ocbuf );
for ( i=0; i<NDB_MAX_RDNS; i++ ) {
if ( scanDN[i]->isNULL() || !rdns.nr_buf[i][0] )
break;
}
ptr = dnBuf;
rdns.nr_num = i;
for ( --i; i>=0; i-- ) {
char *buf;
int len;
buf = rdns.nr_buf[i];
len = *buf++;
ptr = lutil_strncopy( ptr, buf, len );
if ( i ) *ptr++ = ',';
}
*ptr = '\0';
e.e_name.bv_len = ptr - dnBuf;
dnNormalize( 0, NULL, NULL, &e.e_name, &e.e_nname, op->o_tmpmemctx );
NA.txn = NA.ndb->startTransaction();
rc = ndb_entry_get_data( op->o_bd, &NA, 0 );
NA.txn->close();
ber_bvarray_free( NA.ocs );
rc = test_filter( op, &e, op->ors_filter );
if ( rc == LDAP_COMPARE_TRUE ) {
rs->sr_entry = &e;
rs->sr_attrs = op->ors_attrs;
rs->sr_flags = 0;
rc = send_search_entry( op, rs );
rs->sr_entry = NULL;
} else {
rc = 0;
}
attrs_free( e.e_attrs );
e.e_attrs = NULL;
op->o_tmpfree( e.e_nname.bv_val, op->o_tmpmemctx );
if ( rc ) break;
}
leave:
if ( sf )
delete sf;
txn->close();
send_ldap_result( op, rs );
return rs->sr_err;
}
extern "C" int
ndb_has_children(
NdbArgs *NA,
int *hasChildren
)
{
NdbIndexScanOperation *scan;
char idbuf[2*sizeof(ID)];
int rc;
if ( NA->rdns->nr_num >= NDB_MAX_RDNS ) {
*hasChildren = LDAP_COMPARE_FALSE;
return 0;
}
scan = NA->txn->getNdbIndexScanOperation( "PRIMARY", DN2ID_TABLE );
if ( !scan )
return LDAP_OTHER;
scan->readTuples( NdbOperation::LM_Read, 0U, 0U, 1U );
rc = ndb_dn2bound( scan, NA->rdns );
if ( rc < NDB_MAX_RDNS ) {
scan->setBound( rc, NdbIndexScanOperation::BoundLT, "\0" );
}
scan->interpret_exit_last_row();
scan->getValue( EID_COLUMN, idbuf );
if ( NA->txn->execute( NdbTransaction::NoCommit, NdbOperation::AO_IgnoreError, 1 )) {
return LDAP_OTHER;
}
if (rc < NDB_MAX_RDNS && scan->nextResult() == 0 )
*hasChildren = LDAP_COMPARE_TRUE;
else
*hasChildren = LDAP_COMPARE_FALSE;
return 0;
}
extern "C" int
ndb_has_subordinates(
Operation *op,
Entry *e,
int *hasSubordinates )
{
NdbArgs NA;
NdbRdns rdns;
int rc;
NA.rdns = &rdns;
rc = ndb_dn2rdns( &e->e_nname, &rdns );
if ( rc == 0 ) {
rc = ndb_thread_handle( op, &NA.ndb );
NA.txn = NA.ndb->startTransaction();
if ( NA.txn ) {
rc = ndb_has_children( &NA, hasSubordinates );
NA.txn->close();
}
}
return rc;
}
/*
* sets the supported operational attributes (if required)
*/
extern "C" int
ndb_operational(
Operation *op,
SlapReply *rs )
{
Attribute **ap;
assert( rs->sr_entry != NULL );
for ( ap = &rs->sr_operational_attrs; *ap; ap = &(*ap)->a_next )
/* just count */ ;
if ( SLAP_OPATTRS( rs->sr_attr_flags ) ||
ad_inlist( slap_schema.si_ad_hasSubordinates, rs->sr_attrs ) )
{
int hasSubordinates, rc;
rc = ndb_has_subordinates( 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;
}

View File

@ -0,0 +1,524 @@
/* tools.cpp - tools for slap tools */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
* Copyright 2008 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 initially developed by Howard Chu for inclusion
* in OpenLDAP Software. This work was sponsored by MySQL.
*/
#include "portable.h"
#include <stdio.h>
#include <ac/string.h>
#include <ac/errno.h>
#include "lutil.h"
#include "back-ndb.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 nhmax = HOLE_SIZE;
static unsigned nholes;
static Avlnode *myParents;
static Ndb *myNdb;
static NdbTransaction *myScanTxn;
static NdbIndexScanOperation *myScanOp;
static NdbRecAttr *myScanID, *myScanOC;
static NdbRecAttr *myScanDN[NDB_MAX_RDNS];
static char myDNbuf[2048];
static char myIdbuf[2*sizeof(ID)];
static char myOcbuf[NDB_OC_BUFLEN];
static NdbRdns myRdns;
static NdbTransaction *myPutTxn;
static int myPutCnt;
static struct berval *myOcList;
static struct berval myDn;
extern "C"
int ndb_tool_entry_open(
BackendDB *be, int mode )
{
struct ndb_info *ni = (struct ndb_info *) be->be_private;
myNdb = new Ndb( ni->ni_cluster[0], ni->ni_dbname );
return myNdb->init(1024);
}
extern "C"
int ndb_tool_entry_close(
BackendDB *be )
{
if ( myPutTxn ) {
int rc = myPutTxn->execute(NdbTransaction::Commit);
if( rc != 0 ) {
char text[1024];
snprintf( text, sizeof(text),
"txn_commit failed: %s (%d)",
myPutTxn->getNdbError().message, myPutTxn->getNdbError().code );
Debug( LDAP_DEBUG_ANY,
"=> " LDAP_XSTRING(ndb_tool_entry_put) ": %s\n",
text, 0, 0 );
}
myPutTxn->close();
myPutTxn = NULL;
}
myPutCnt = 0;
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;
}
extern "C"
ID ndb_tool_entry_next(
BackendDB *be )
{
struct ndb_info *ni = (struct ndb_info *) be->be_private;
char *ptr;
ID id;
int i;
assert( be != NULL );
assert( slapMode & SLAP_TOOL_MODE );
if ( myScanOp->nextResult() ) {
myScanOp->close();
myScanOp = NULL;
myScanTxn->close();
myScanTxn = NULL;
return NOID;
}
id = myScanID->u_64_value();
if ( myOcList ) {
ber_bvarray_free( myOcList );
}
myOcList = ndb_ref2oclist( myOcbuf );
for ( i=0; i<NDB_MAX_RDNS; i++ ) {
if ( myScanDN[i]->isNULL() || !myRdns.nr_buf[i] )
break;
}
myRdns.nr_num = i;
ptr = myDNbuf;
for ( --i; i>=0; i-- ) {
char *buf;
int len;
buf = myRdns.nr_buf[i];
len = *buf++;
ptr = lutil_strncopy( ptr, buf, len );
if ( i )
*ptr++ = ',';
}
*ptr = '\0';
myDn.bv_val = myDNbuf;
myDn.bv_len = ptr - myDNbuf;
return id;
}
extern "C"
ID ndb_tool_entry_first(
BackendDB *be )
{
struct ndb_info *ni = (struct ndb_info *) be->be_private;
int i, rc;
myScanTxn = myNdb->startTransaction();
if ( !myScanTxn )
return NOID;
myScanOp = myScanTxn->getNdbIndexScanOperation( "PRIMARY", DN2ID_TABLE );
if ( !myScanOp )
return NOID;
if ( myScanOp->readTuples( NdbOperation::LM_CommittedRead, NdbScanOperation::SF_KeyInfo ))
return NOID;
myScanID = myScanOp->getValue( EID_COLUMN, myIdbuf );
myScanOC = myScanOp->getValue( OCS_COLUMN, myOcbuf );
for ( i=0; i<NDB_MAX_RDNS; i++ ) {
myScanDN[i] = myScanOp->getValue( i+RDN_COLUMN, myRdns.nr_buf[i] );
}
if ( myScanTxn->execute( NdbTransaction::NoCommit, NdbOperation::AbortOnError, 1 ))
return NOID;
return ndb_tool_entry_next( be );
}
extern "C"
ID ndb_tool_dn2id_get(
Backend *be,
struct berval *dn
)
{
struct ndb_info *ni = (struct ndb_info *) be->be_private;
NdbArgs NA;
NdbRdns rdns;
Entry e;
char text[1024];
int rc;
if ( BER_BVISEMPTY(dn) )
return 0;
NA.ndb = myNdb;
NA.txn = myNdb->startTransaction();
if ( !NA.txn ) {
snprintf( text, sizeof(text),
"startTransaction failed: %s (%d)",
myNdb->getNdbError().message, myNdb->getNdbError().code );
Debug( LDAP_DEBUG_ANY,
"=> " LDAP_XSTRING(ndb_tool_dn2id_get) ": %s\n",
text, 0, 0 );
return NOID;
}
if ( myOcList ) {
ber_bvarray_free( myOcList );
myOcList = NULL;
}
NA.e = &e;
e.e_name = *dn;
NA.rdns = &rdns;
NA.ocs = NULL;
rc = ndb_entry_get_info( be, &NA, 0, NULL );
myOcList = NA.ocs;
NA.txn->close();
if ( rc )
return NOID;
myDn = *dn;
return e.e_id;
}
extern "C"
Entry* ndb_tool_entry_get( BackendDB *be, ID id )
{
NdbArgs NA;
int rc;
char text[1024];
assert( be != NULL );
assert( slapMode & SLAP_TOOL_MODE );
NA.txn = myNdb->startTransaction();
if ( !NA.txn ) {
snprintf( text, sizeof(text),
"start_transaction failed: %s (%d)",
myNdb->getNdbError().message, myNdb->getNdbError().code );
Debug( LDAP_DEBUG_ANY,
"=> " LDAP_XSTRING(ndb_tool_entry_get) ": %s\n",
text, 0, 0 );
return NULL;
}
NA.e = entry_alloc();
NA.e->e_id = id;
ber_dupbv( &NA.e->e_name, &myDn );
dnNormalize( 0, NULL, NULL, &NA.e->e_name, &NA.e->e_nname, NULL );
NA.ndb = myNdb;
NA.ocs = myOcList;
rc = ndb_entry_get_data( be, &NA, 0 );
if ( rc ) {
entry_free( NA.e );
NA.e = NULL;
}
NA.txn->close();
return NA.e;
}
static struct berval glueval[] = {
BER_BVC("glue"),
BER_BVNULL
};
static int ndb_dnid_cmp( const void *v1, const void *v2 )
{
struct dn_id *dn1 = (struct dn_id *)v1,
*dn2 = (struct dn_id *)v2;
return ber_bvcmp( &dn1->dn, &dn2->dn );
}
static int ndb_tool_next_id(
BackendDB *be,
NdbArgs *NA,
struct berval *text,
int hole )
{
struct berval ndn = NA->e->e_nname;
int rc;
if (ndn.bv_len == 0) {
NA->e->e_id = 0;
return 0;
}
rc = ndb_entry_get_info( be, NA, 0, NULL );
if ( rc ) {
Attribute *a, tmp = {0};
if ( !be_issuffix( be, &ndn ) ) {
struct dn_id *dptr;
struct berval npdn;
dnParent( &ndn, &npdn );
NA->e->e_nname = npdn;
NA->rdns->nr_num--;
rc = ndb_tool_next_id( be, NA, text, 1 );
NA->e->e_nname = ndn;
NA->rdns->nr_num++;
if ( rc ) {
return rc;
}
/* If parent didn't exist, it was created just now
* and its ID is now in e->e_id.
*/
dptr = (struct dn_id *)ch_malloc( sizeof( struct dn_id ) + npdn.bv_len + 1);
dptr->id = NA->e->e_id;
dptr->dn.bv_val = (char *)(dptr+1);
strcpy(dptr->dn.bv_val, npdn.bv_val );
dptr->dn.bv_len = npdn.bv_len;
if ( avl_insert( &myParents, dptr, ndb_dnid_cmp, avl_dup_error )) {
ch_free( dptr );
}
}
rc = ndb_next_id( be, myNdb, &NA->e->e_id );
if ( rc ) {
snprintf( text->bv_val, text->bv_len,
"next_id failed: %s (%d)",
myNdb->getNdbError().message, myNdb->getNdbError().code );
Debug( LDAP_DEBUG_ANY,
"=> ndb_tool_next_id: %s\n", text->bv_val, 0, 0 );
return rc;
}
if ( hole ) {
a = NA->e->e_attrs;
NA->e->e_attrs = &tmp;
tmp.a_desc = slap_schema.si_ad_objectClass;
tmp.a_vals = glueval;
tmp.a_nvals = tmp.a_vals;
tmp.a_numvals = 1;
}
rc = ndb_entry_put_info( be, NA, 0 );
if ( hole ) {
NA->e->e_attrs = a;
}
if ( rc ) {
snprintf( text->bv_val, text->bv_len,
"ndb_entry_put_info failed: %s (%d)",
myNdb->getNdbError().message, myNdb->getNdbError().code );
Debug( LDAP_DEBUG_ANY,
"=> ndb_tool_next_id: %s\n", text->bv_val, 0, 0 );
} else if ( hole ) {
if ( nholes == nhmax - 1 ) {
if ( holes == hbuf ) {
holes = (dn_id *)ch_malloc( nhmax * sizeof(dn_id) * 2 );
AC_MEMCPY( holes, hbuf, sizeof(hbuf) );
} else {
holes = (dn_id *)ch_realloc( holes, nhmax * sizeof(dn_id) * 2 );
}
nhmax *= 2;
}
ber_dupbv( &holes[nholes].dn, &ndn );
holes[nholes++].id = NA->e->e_id;
}
} else if ( !hole ) {
unsigned i;
for ( i=0; i<nholes; i++) {
if ( holes[i].id == NA->e->e_id ) {
int j;
free(holes[i].dn.bv_val);
for (j=i;j<nholes;j++) holes[j] = holes[j+1];
holes[j].id = 0;
nholes--;
rc = ndb_entry_put_info( be, NA, 1 );
break;
} else if ( holes[i].id > NA->e->e_id ) {
break;
}
}
}
return rc;
}
extern "C"
ID ndb_tool_entry_put(
BackendDB *be,
Entry *e,
struct berval *text )
{
struct ndb_info *ni = (struct ndb_info *) be->be_private;
struct dn_id dtmp, *dptr;
NdbArgs NA;
NdbRdns rdns;
int rc, slow = 0;
assert( be != NULL );
assert( slapMode & SLAP_TOOL_MODE );
assert( text != NULL );
assert( text->bv_val != NULL );
assert( text->bv_val[0] == '\0' ); /* overconservative? */
Debug( LDAP_DEBUG_TRACE, "=> " LDAP_XSTRING(ndb_tool_entry_put)
"( %ld, \"%s\" )\n", (long) e->e_id, e->e_dn, 0 );
if ( !be_issuffix( be, &e->e_nname )) {
dnParent( &e->e_nname, &dtmp.dn );
dptr = (struct dn_id *)avl_find( myParents, &dtmp, ndb_dnid_cmp );
if ( !dptr )
slow = 1;
}
rdns.nr_num = 0;
if ( !slow ) {
rc = ndb_next_id( be, myNdb, &e->e_id );
if ( rc ) {
snprintf( text->bv_val, text->bv_len,
"next_id failed: %s (%d)",
myNdb->getNdbError().message, myNdb->getNdbError().code );
Debug( LDAP_DEBUG_ANY,
"=> ndb_tool_next_id: %s\n", text->bv_val, 0, 0 );
return rc;
}
}
if ( !myPutTxn )
myPutTxn = myNdb->startTransaction();
if ( !myPutTxn ) {
snprintf( text->bv_val, text->bv_len,
"start_transaction failed: %s (%d)",
myNdb->getNdbError().message, myNdb->getNdbError().code );
Debug( LDAP_DEBUG_ANY,
"=> " LDAP_XSTRING(ndb_tool_entry_put) ": %s\n",
text->bv_val, 0, 0 );
return NOID;
}
/* add dn2id indices */
ndb_dn2rdns( &e->e_name, &rdns );
NA.rdns = &rdns;
NA.e = e;
NA.ndb = myNdb;
NA.txn = myPutTxn;
if ( slow ) {
rc = ndb_tool_next_id( be, &NA, text, 0 );
if( rc != 0 ) {
goto done;
}
} else {
rc = ndb_entry_put_info( be, &NA, 0 );
if ( rc != 0 ) {
goto done;
}
}
/* id2entry index */
rc = ndb_entry_put_data( be, &NA, 0 );
if( rc != 0 ) {
snprintf( text->bv_val, text->bv_len,
"ndb_entry_put_data failed: %s (%d)",
myNdb->getNdbError().message, myNdb->getNdbError().code );
Debug( LDAP_DEBUG_ANY,
"=> " LDAP_XSTRING(ndb_tool_entry_put) ": %s\n",
text->bv_val, 0, 0 );
goto done;
}
done:
if( rc == 0 ) {
myPutCnt++;
if ( !( myPutCnt & 0x0f )) {
rc = myPutTxn->execute(NdbTransaction::Commit);
if( rc != 0 ) {
snprintf( text->bv_val, text->bv_len,
"txn_commit failed: %s (%d)",
myPutTxn->getNdbError().message, myPutTxn->getNdbError().code );
Debug( LDAP_DEBUG_ANY,
"=> " LDAP_XSTRING(ndb_tool_entry_put) ": %s\n",
text->bv_val, 0, 0 );
e->e_id = NOID;
}
myPutTxn->close();
myPutTxn = NULL;
}
} else {
snprintf( text->bv_val, text->bv_len,
"txn_aborted! %s (%d)",
myPutTxn->getNdbError().message, myPutTxn->getNdbError().code );
Debug( LDAP_DEBUG_ANY,
"=> " LDAP_XSTRING(ndb_tool_entry_put) ": %s\n",
text->bv_val, 0, 0 );
e->e_id = NOID;
myPutTxn->close();
}
return e->e_id;
}
extern "C"
int ndb_tool_entry_reindex(
BackendDB *be,
ID id,
AttributeDescription **adv )
{
struct ndb_info *ni = (struct ndb_info *) be->be_private;
Debug( LDAP_DEBUG_ARGS,
"=> " LDAP_XSTRING(ndb_tool_entry_reindex) "( %ld )\n",
(long) id, 0, 0 );
return 0;
}
extern "C"
ID ndb_tool_entry_modify(
BackendDB *be,
Entry *e,
struct berval *text )
{
struct ndb_info *ni = (struct ndb_info *) be->be_private;
int rc;
Debug( LDAP_DEBUG_TRACE,
"=> " LDAP_XSTRING(ndb_tool_entry_modify) "( %ld, \"%s\" )\n",
(long) e->e_id, e->e_dn, 0 );
done:
return e->e_id;
}