14e6df137Slukem /* add.cpp - ldap NDB back-end add routine */
2d11b170bStron /* $OpenLDAP$ */
34e6df137Slukem /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
44e6df137Slukem *
5*e670fd5cSchristos * Copyright 2008-2021 The OpenLDAP Foundation.
64e6df137Slukem * All rights reserved.
74e6df137Slukem *
84e6df137Slukem * Redistribution and use in source and binary forms, with or without
94e6df137Slukem * modification, are permitted only as authorized by the OpenLDAP
104e6df137Slukem * Public License.
114e6df137Slukem *
124e6df137Slukem * A copy of this license is available in the file LICENSE in the
134e6df137Slukem * top-level directory of the distribution or, alternatively, at
144e6df137Slukem * <http://www.OpenLDAP.org/license.html>.
154e6df137Slukem */
164e6df137Slukem /* ACKNOWLEDGEMENTS:
174e6df137Slukem * This work was initially developed by Howard Chu for inclusion
184e6df137Slukem * in OpenLDAP Software. This work was sponsored by MySQL.
194e6df137Slukem */
204e6df137Slukem
214e6df137Slukem #include "portable.h"
224e6df137Slukem
234e6df137Slukem #include <stdio.h>
244e6df137Slukem #include <ac/string.h>
254e6df137Slukem
264e6df137Slukem #include "back-ndb.h"
274e6df137Slukem
284e6df137Slukem extern "C" int
ndb_back_add(Operation * op,SlapReply * rs)294e6df137Slukem ndb_back_add(Operation *op, SlapReply *rs )
304e6df137Slukem {
314e6df137Slukem struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
324e6df137Slukem Entry p = {0};
334e6df137Slukem Attribute poc;
344e6df137Slukem char textbuf[SLAP_TEXT_BUFLEN];
354e6df137Slukem size_t textlen = sizeof textbuf;
364e6df137Slukem AttributeDescription *children = slap_schema.si_ad_children;
374e6df137Slukem AttributeDescription *entry = slap_schema.si_ad_entry;
384e6df137Slukem NdbArgs NA;
394e6df137Slukem NdbRdns rdns;
404e6df137Slukem struct berval matched;
414e6df137Slukem struct berval pdn, pndn;
424e6df137Slukem
434e6df137Slukem int num_retries = 0;
444e6df137Slukem int success;
454e6df137Slukem
464e6df137Slukem LDAPControl **postread_ctrl = NULL;
474e6df137Slukem LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
484e6df137Slukem int num_ctrls = 0;
494e6df137Slukem
504e6df137Slukem Debug(LDAP_DEBUG_ARGS, "==> " LDAP_XSTRING(ndb_back_add) ": %s\n",
514e6df137Slukem op->oq_add.rs_e->e_name.bv_val, 0, 0);
524e6df137Slukem
534e6df137Slukem ctrls[num_ctrls] = 0;
54d11b170bStron NA.txn = NULL;
554e6df137Slukem
564e6df137Slukem /* check entry's schema */
574e6df137Slukem rs->sr_err = entry_schema_check( op, op->oq_add.rs_e, NULL,
584e6df137Slukem get_relax(op), 1, NULL, &rs->sr_text, textbuf, textlen );
594e6df137Slukem if ( rs->sr_err != LDAP_SUCCESS ) {
604e6df137Slukem Debug( LDAP_DEBUG_TRACE,
614e6df137Slukem LDAP_XSTRING(ndb_back_add) ": entry failed schema check: "
624e6df137Slukem "%s (%d)\n", rs->sr_text, rs->sr_err, 0 );
634e6df137Slukem goto return_results;
644e6df137Slukem }
654e6df137Slukem
664e6df137Slukem /* add opattrs to shadow as well, only missing attrs will actually
674e6df137Slukem * be added; helps compatibility with older OL versions */
684e6df137Slukem rs->sr_err = slap_add_opattrs( op, &rs->sr_text, textbuf, textlen, 1 );
694e6df137Slukem if ( rs->sr_err != LDAP_SUCCESS ) {
704e6df137Slukem Debug( LDAP_DEBUG_TRACE,
714e6df137Slukem LDAP_XSTRING(ndb_back_add) ": entry failed op attrs add: "
724e6df137Slukem "%s (%d)\n", rs->sr_text, rs->sr_err, 0 );
734e6df137Slukem goto return_results;
744e6df137Slukem }
754e6df137Slukem
764e6df137Slukem /* Get our NDB handle */
774e6df137Slukem rs->sr_err = ndb_thread_handle( op, &NA.ndb );
784e6df137Slukem
794e6df137Slukem /*
804e6df137Slukem * Get the parent dn and see if the corresponding entry exists.
814e6df137Slukem */
824e6df137Slukem if ( be_issuffix( op->o_bd, &op->oq_add.rs_e->e_nname ) ) {
834e6df137Slukem pdn = slap_empty_bv;
844e6df137Slukem pndn = slap_empty_bv;
854e6df137Slukem } else {
864e6df137Slukem dnParent( &op->ora_e->e_name, &pdn );
874e6df137Slukem dnParent( &op->ora_e->e_nname, &pndn );
884e6df137Slukem }
894e6df137Slukem p.e_name = op->ora_e->e_name;
904e6df137Slukem p.e_nname = op->ora_e->e_nname;
914e6df137Slukem
924e6df137Slukem op->ora_e->e_id = NOID;
934e6df137Slukem rdns.nr_num = 0;
944e6df137Slukem NA.rdns = &rdns;
954e6df137Slukem
964e6df137Slukem if( 0 ) {
974e6df137Slukem retry: /* transaction retry */
984e6df137Slukem NA.txn->close();
994e6df137Slukem NA.txn = NULL;
1004e6df137Slukem if ( op->o_abandon ) {
1014e6df137Slukem rs->sr_err = SLAPD_ABANDON;
1024e6df137Slukem goto return_results;
1034e6df137Slukem }
1044e6df137Slukem ndb_trans_backoff( ++num_retries );
1054e6df137Slukem }
1064e6df137Slukem
1074e6df137Slukem NA.txn = NA.ndb->startTransaction();
1084e6df137Slukem rs->sr_text = NULL;
1094e6df137Slukem if( !NA.txn ) {
1104e6df137Slukem Debug( LDAP_DEBUG_TRACE,
1114e6df137Slukem LDAP_XSTRING(ndb_back_add) ": startTransaction failed: %s (%d)\n",
1124e6df137Slukem NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 );
1134e6df137Slukem rs->sr_err = LDAP_OTHER;
1144e6df137Slukem rs->sr_text = "internal error";
1154e6df137Slukem goto return_results;
1164e6df137Slukem }
1174e6df137Slukem
1184e6df137Slukem /* get entry or parent */
1194e6df137Slukem NA.e = &p;
1204e6df137Slukem NA.ocs = NULL;
1214e6df137Slukem rs->sr_err = ndb_entry_get_info( op, &NA, 0, &matched );
1224e6df137Slukem switch( rs->sr_err ) {
1234e6df137Slukem case 0:
1244e6df137Slukem rs->sr_err = LDAP_ALREADY_EXISTS;
1254e6df137Slukem goto return_results;
1264e6df137Slukem case LDAP_NO_SUCH_OBJECT:
1274e6df137Slukem break;
1284e6df137Slukem #if 0
1294e6df137Slukem case DB_LOCK_DEADLOCK:
1304e6df137Slukem case DB_LOCK_NOTGRANTED:
1314e6df137Slukem goto retry;
1324e6df137Slukem #endif
1334e6df137Slukem case LDAP_BUSY:
1344e6df137Slukem rs->sr_text = "ldap server busy";
1354e6df137Slukem goto return_results;
1364e6df137Slukem default:
1374e6df137Slukem rs->sr_err = LDAP_OTHER;
1384e6df137Slukem rs->sr_text = "internal error";
1394e6df137Slukem goto return_results;
1404e6df137Slukem }
1414e6df137Slukem
1424e6df137Slukem if ( NA.ocs ) {
1434e6df137Slukem int i;
1444e6df137Slukem for ( i=0; !BER_BVISNULL( &NA.ocs[i] ); i++ );
1454e6df137Slukem poc.a_numvals = i;
1464e6df137Slukem poc.a_desc = slap_schema.si_ad_objectClass;
1474e6df137Slukem poc.a_vals = NA.ocs;
1484e6df137Slukem poc.a_nvals = poc.a_vals;
1494e6df137Slukem poc.a_next = NULL;
1504e6df137Slukem p.e_attrs = &poc;
1514e6df137Slukem }
1524e6df137Slukem
1534e6df137Slukem if ( ber_bvstrcasecmp( &pndn, &matched ) ) {
1544e6df137Slukem rs->sr_matched = matched.bv_val;
1554e6df137Slukem Debug( LDAP_DEBUG_TRACE,
1564e6df137Slukem LDAP_XSTRING(ndb_back_add) ": parent "
1574e6df137Slukem "does not exist\n", 0, 0, 0 );
1584e6df137Slukem
1594e6df137Slukem rs->sr_text = "parent does not exist";
1604e6df137Slukem rs->sr_err = LDAP_NO_SUCH_OBJECT;
1614e6df137Slukem if ( p.e_attrs && is_entry_referral( &p )) {
1624e6df137Slukem is_ref: p.e_attrs = NULL;
1634e6df137Slukem ndb_entry_get_data( op, &NA, 0 );
1644e6df137Slukem rs->sr_ref = get_entry_referrals( op, &p );
1654e6df137Slukem rs->sr_err = LDAP_REFERRAL;
1664e6df137Slukem rs->sr_flags = REP_REF_MUSTBEFREED;
1674e6df137Slukem attrs_free( p.e_attrs );
1684e6df137Slukem p.e_attrs = NULL;
1694e6df137Slukem }
1704e6df137Slukem goto return_results;
1714e6df137Slukem }
1724e6df137Slukem
1734e6df137Slukem p.e_name = pdn;
1744e6df137Slukem p.e_nname = pndn;
1754e6df137Slukem rs->sr_err = access_allowed( op, &p,
1764e6df137Slukem children, NULL, ACL_WADD, NULL );
1774e6df137Slukem
1784e6df137Slukem if ( ! rs->sr_err ) {
1794e6df137Slukem Debug( LDAP_DEBUG_TRACE,
1804e6df137Slukem LDAP_XSTRING(ndb_back_add) ": no write access to parent\n",
1814e6df137Slukem 0, 0, 0 );
1824e6df137Slukem rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
1834e6df137Slukem rs->sr_text = "no write access to parent";
1844e6df137Slukem goto return_results;
1854e6df137Slukem }
1864e6df137Slukem
1874e6df137Slukem if ( NA.ocs ) {
1884e6df137Slukem if ( is_entry_subentry( &p )) {
1894e6df137Slukem /* parent is a subentry, don't allow add */
1904e6df137Slukem Debug( LDAP_DEBUG_TRACE,
1914e6df137Slukem LDAP_XSTRING(ndb_back_add) ": parent is subentry\n",
1924e6df137Slukem 0, 0, 0 );
1934e6df137Slukem rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
1944e6df137Slukem rs->sr_text = "parent is a subentry";
1954e6df137Slukem goto return_results;
1964e6df137Slukem }
1974e6df137Slukem
1984e6df137Slukem if ( is_entry_alias( &p ) ) {
1994e6df137Slukem /* parent is an alias, don't allow add */
2004e6df137Slukem Debug( LDAP_DEBUG_TRACE,
2014e6df137Slukem LDAP_XSTRING(ndb_back_add) ": parent is alias\n",
2024e6df137Slukem 0, 0, 0 );
2034e6df137Slukem rs->sr_err = LDAP_ALIAS_PROBLEM;
2044e6df137Slukem rs->sr_text = "parent is an alias";
2054e6df137Slukem goto return_results;
2064e6df137Slukem }
2074e6df137Slukem
2084e6df137Slukem if ( is_entry_referral( &p ) ) {
2094e6df137Slukem /* parent is a referral, don't allow add */
2104e6df137Slukem rs->sr_matched = p.e_name.bv_val;
2114e6df137Slukem goto is_ref;
2124e6df137Slukem }
2134e6df137Slukem }
2144e6df137Slukem
2154e6df137Slukem rs->sr_err = access_allowed( op, op->ora_e,
2164e6df137Slukem entry, NULL, ACL_WADD, NULL );
2174e6df137Slukem
2184e6df137Slukem if ( ! rs->sr_err ) {
2194e6df137Slukem Debug( LDAP_DEBUG_TRACE,
2204e6df137Slukem LDAP_XSTRING(ndb_back_add) ": no write access to entry\n",
2214e6df137Slukem 0, 0, 0 );
2224e6df137Slukem rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
2234e6df137Slukem rs->sr_text = "no write access to entry";
2244e6df137Slukem goto return_results;;
2254e6df137Slukem }
2264e6df137Slukem
2274e6df137Slukem /*
2284e6df137Slukem * Check ACL for attribute write access
2294e6df137Slukem */
2304e6df137Slukem if (!acl_check_modlist(op, op->ora_e, op->ora_modlist)) {
2314e6df137Slukem Debug( LDAP_DEBUG_TRACE,
2324e6df137Slukem LDAP_XSTRING(bdb_add) ": no write access to attribute\n",
2334e6df137Slukem 0, 0, 0 );
2344e6df137Slukem rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
2354e6df137Slukem rs->sr_text = "no write access to attribute";
2364e6df137Slukem goto return_results;;
2374e6df137Slukem }
2384e6df137Slukem
2394e6df137Slukem
2404e6df137Slukem /* acquire entry ID */
2414e6df137Slukem if ( op->ora_e->e_id == NOID ) {
2424e6df137Slukem rs->sr_err = ndb_next_id( op->o_bd, NA.ndb, &op->ora_e->e_id );
2434e6df137Slukem if( rs->sr_err != 0 ) {
2444e6df137Slukem Debug( LDAP_DEBUG_TRACE,
2454e6df137Slukem LDAP_XSTRING(ndb_back_add) ": next_id failed (%d)\n",
2464e6df137Slukem rs->sr_err, 0, 0 );
2474e6df137Slukem rs->sr_err = LDAP_OTHER;
2484e6df137Slukem rs->sr_text = "internal error";
2494e6df137Slukem goto return_results;
2504e6df137Slukem }
2514e6df137Slukem }
2524e6df137Slukem
2534e6df137Slukem if ( matched.bv_val )
2544e6df137Slukem rdns.nr_num++;
2554e6df137Slukem NA.e = op->ora_e;
2564e6df137Slukem /* dn2id index */
2574e6df137Slukem rs->sr_err = ndb_entry_put_info( op->o_bd, &NA, 0 );
2584e6df137Slukem if ( rs->sr_err ) {
2594e6df137Slukem Debug( LDAP_DEBUG_TRACE,
2604e6df137Slukem LDAP_XSTRING(ndb_back_add) ": ndb_entry_put_info failed (%d)\n",
2614e6df137Slukem rs->sr_err, 0, 0 );
2624e6df137Slukem rs->sr_text = "internal error";
2634e6df137Slukem goto return_results;
2644e6df137Slukem }
2654e6df137Slukem
2664e6df137Slukem /* id2entry index */
2674e6df137Slukem rs->sr_err = ndb_entry_put_data( op->o_bd, &NA );
2684e6df137Slukem if ( rs->sr_err ) {
2694e6df137Slukem Debug( LDAP_DEBUG_TRACE,
2704e6df137Slukem LDAP_XSTRING(ndb_back_add) ": ndb_entry_put_data failed (%d) %s(%d)\n",
2714e6df137Slukem rs->sr_err, NA.txn->getNdbError().message, NA.txn->getNdbError().code );
2724e6df137Slukem rs->sr_text = "internal error";
2734e6df137Slukem goto return_results;
2744e6df137Slukem }
2754e6df137Slukem
2764e6df137Slukem /* post-read */
2774e6df137Slukem if( op->o_postread ) {
2784e6df137Slukem if( postread_ctrl == NULL ) {
2794e6df137Slukem postread_ctrl = &ctrls[num_ctrls++];
2804e6df137Slukem ctrls[num_ctrls] = NULL;
2814e6df137Slukem }
2824e6df137Slukem if ( slap_read_controls( op, rs, op->oq_add.rs_e,
2834e6df137Slukem &slap_post_read_bv, postread_ctrl ) )
2844e6df137Slukem {
2854e6df137Slukem Debug( LDAP_DEBUG_TRACE,
2864e6df137Slukem "<=- " LDAP_XSTRING(ndb_back_add) ": post-read "
2874e6df137Slukem "failed!\n", 0, 0, 0 );
2884e6df137Slukem if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
2894e6df137Slukem /* FIXME: is it correct to abort
2904e6df137Slukem * operation if control fails? */
2914e6df137Slukem goto return_results;
2924e6df137Slukem }
2934e6df137Slukem }
2944e6df137Slukem }
2954e6df137Slukem
2964e6df137Slukem if ( op->o_noop ) {
2974e6df137Slukem if (( rs->sr_err=NA.txn->execute( NdbTransaction::Rollback,
2984e6df137Slukem NdbOperation::AbortOnError, 1 )) != 0 ) {
2994e6df137Slukem rs->sr_text = "txn (no-op) failed";
3004e6df137Slukem } else {
3014e6df137Slukem rs->sr_err = LDAP_X_NO_OPERATION;
3024e6df137Slukem }
3034e6df137Slukem
3044e6df137Slukem } else {
3054e6df137Slukem if(( rs->sr_err=NA.txn->execute( NdbTransaction::Commit,
3064e6df137Slukem NdbOperation::AbortOnError, 1 )) != 0 ) {
3074e6df137Slukem rs->sr_text = "txn_commit failed";
3084e6df137Slukem } else {
3094e6df137Slukem rs->sr_err = LDAP_SUCCESS;
3104e6df137Slukem }
3114e6df137Slukem }
3124e6df137Slukem
3134e6df137Slukem if ( rs->sr_err != LDAP_SUCCESS && rs->sr_err != LDAP_X_NO_OPERATION ) {
3144e6df137Slukem Debug( LDAP_DEBUG_TRACE,
3154e6df137Slukem LDAP_XSTRING(ndb_back_add) ": %s : %s (%d)\n",
3164e6df137Slukem rs->sr_text, NA.txn->getNdbError().message, NA.txn->getNdbError().code );
3174e6df137Slukem rs->sr_err = LDAP_OTHER;
3184e6df137Slukem goto return_results;
3194e6df137Slukem }
3204e6df137Slukem NA.txn->close();
3214e6df137Slukem NA.txn = NULL;
3224e6df137Slukem
3234e6df137Slukem Debug(LDAP_DEBUG_TRACE,
3244e6df137Slukem LDAP_XSTRING(ndb_back_add) ": added%s id=%08lx dn=\"%s\"\n",
3254e6df137Slukem op->o_noop ? " (no-op)" : "",
3264e6df137Slukem op->oq_add.rs_e->e_id, op->oq_add.rs_e->e_dn );
3274e6df137Slukem
3284e6df137Slukem rs->sr_text = NULL;
3294e6df137Slukem if( num_ctrls ) rs->sr_ctrls = ctrls;
3304e6df137Slukem
3314e6df137Slukem return_results:
3324e6df137Slukem success = rs->sr_err;
3334e6df137Slukem send_ldap_result( op, rs );
3344e6df137Slukem slap_graduate_commit_csn( op );
3354e6df137Slukem
3364e6df137Slukem if( NA.txn != NULL ) {
3374e6df137Slukem NA.txn->execute( Rollback );
3384e6df137Slukem NA.txn->close();
3394e6df137Slukem }
3404e6df137Slukem
3414e6df137Slukem if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) {
3424e6df137Slukem slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
3434e6df137Slukem slap_sl_free( *postread_ctrl, op->o_tmpmemctx );
3444e6df137Slukem }
3454e6df137Slukem
3464e6df137Slukem return rs->sr_err;
3474e6df137Slukem }
348