1/* add.cpp - ldap NDB back-end add routine */ 2/* OpenLDAP: pkg/ldap/servers/slapd/back-ndb/add.cpp,v 1.3.2.5 2010/04/13 20:23:34 kurt Exp */ 3/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 4 * 5 * Copyright 2008-2010 The OpenLDAP Foundation. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted only as authorized by the OpenLDAP 10 * Public License. 11 * 12 * A copy of this license is available in the file LICENSE in the 13 * top-level directory of the distribution or, alternatively, at 14 * <http://www.OpenLDAP.org/license.html>. 15 */ 16/* ACKNOWLEDGEMENTS: 17 * This work was initially developed by Howard Chu for inclusion 18 * in OpenLDAP Software. This work was sponsored by MySQL. 19 */ 20 21#include "portable.h" 22 23#include <stdio.h> 24#include <ac/string.h> 25 26#include "back-ndb.h" 27 28extern "C" int 29ndb_back_add(Operation *op, SlapReply *rs ) 30{ 31 struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private; 32 Entry p = {0}; 33 Attribute poc; 34 char textbuf[SLAP_TEXT_BUFLEN]; 35 size_t textlen = sizeof textbuf; 36 AttributeDescription *children = slap_schema.si_ad_children; 37 AttributeDescription *entry = slap_schema.si_ad_entry; 38 NdbArgs NA; 39 NdbRdns rdns; 40 struct berval matched; 41 struct berval pdn, pndn; 42 43 int num_retries = 0; 44 int success; 45 46 LDAPControl **postread_ctrl = NULL; 47 LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS]; 48 int num_ctrls = 0; 49 50 Debug(LDAP_DEBUG_ARGS, "==> " LDAP_XSTRING(ndb_back_add) ": %s\n", 51 op->oq_add.rs_e->e_name.bv_val, 0, 0); 52 53 ctrls[num_ctrls] = 0; 54 55 /* check entry's schema */ 56 rs->sr_err = entry_schema_check( op, op->oq_add.rs_e, NULL, 57 get_relax(op), 1, NULL, &rs->sr_text, textbuf, textlen ); 58 if ( rs->sr_err != LDAP_SUCCESS ) { 59 Debug( LDAP_DEBUG_TRACE, 60 LDAP_XSTRING(ndb_back_add) ": entry failed schema check: " 61 "%s (%d)\n", rs->sr_text, rs->sr_err, 0 ); 62 goto return_results; 63 } 64 65 /* add opattrs to shadow as well, only missing attrs will actually 66 * be added; helps compatibility with older OL versions */ 67 rs->sr_err = slap_add_opattrs( op, &rs->sr_text, textbuf, textlen, 1 ); 68 if ( rs->sr_err != LDAP_SUCCESS ) { 69 Debug( LDAP_DEBUG_TRACE, 70 LDAP_XSTRING(ndb_back_add) ": entry failed op attrs add: " 71 "%s (%d)\n", rs->sr_text, rs->sr_err, 0 ); 72 goto return_results; 73 } 74 75 /* Get our NDB handle */ 76 rs->sr_err = ndb_thread_handle( op, &NA.ndb ); 77 78 /* 79 * Get the parent dn and see if the corresponding entry exists. 80 */ 81 if ( be_issuffix( op->o_bd, &op->oq_add.rs_e->e_nname ) ) { 82 pdn = slap_empty_bv; 83 pndn = slap_empty_bv; 84 } else { 85 dnParent( &op->ora_e->e_name, &pdn ); 86 dnParent( &op->ora_e->e_nname, &pndn ); 87 } 88 p.e_name = op->ora_e->e_name; 89 p.e_nname = op->ora_e->e_nname; 90 91 op->ora_e->e_id = NOID; 92 rdns.nr_num = 0; 93 NA.rdns = &rdns; 94 95 if( 0 ) { 96retry: /* transaction retry */ 97 NA.txn->close(); 98 NA.txn = NULL; 99 if ( op->o_abandon ) { 100 rs->sr_err = SLAPD_ABANDON; 101 goto return_results; 102 } 103 ndb_trans_backoff( ++num_retries ); 104 } 105 106 NA.txn = NA.ndb->startTransaction(); 107 rs->sr_text = NULL; 108 if( !NA.txn ) { 109 Debug( LDAP_DEBUG_TRACE, 110 LDAP_XSTRING(ndb_back_add) ": startTransaction failed: %s (%d)\n", 111 NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 ); 112 rs->sr_err = LDAP_OTHER; 113 rs->sr_text = "internal error"; 114 goto return_results; 115 } 116 117 /* get entry or parent */ 118 NA.e = &p; 119 NA.ocs = NULL; 120 rs->sr_err = ndb_entry_get_info( op, &NA, 0, &matched ); 121 switch( rs->sr_err ) { 122 case 0: 123 rs->sr_err = LDAP_ALREADY_EXISTS; 124 goto return_results; 125 case LDAP_NO_SUCH_OBJECT: 126 break; 127#if 0 128 case DB_LOCK_DEADLOCK: 129 case DB_LOCK_NOTGRANTED: 130 goto retry; 131#endif 132 case LDAP_BUSY: 133 rs->sr_text = "ldap server busy"; 134 goto return_results; 135 default: 136 rs->sr_err = LDAP_OTHER; 137 rs->sr_text = "internal error"; 138 goto return_results; 139 } 140 141 if ( NA.ocs ) { 142 int i; 143 for ( i=0; !BER_BVISNULL( &NA.ocs[i] ); i++ ); 144 poc.a_numvals = i; 145 poc.a_desc = slap_schema.si_ad_objectClass; 146 poc.a_vals = NA.ocs; 147 poc.a_nvals = poc.a_vals; 148 poc.a_next = NULL; 149 p.e_attrs = &poc; 150 } 151 152 if ( ber_bvstrcasecmp( &pndn, &matched ) ) { 153 rs->sr_matched = matched.bv_val; 154 Debug( LDAP_DEBUG_TRACE, 155 LDAP_XSTRING(ndb_back_add) ": parent " 156 "does not exist\n", 0, 0, 0 ); 157 158 rs->sr_text = "parent does not exist"; 159 rs->sr_err = LDAP_NO_SUCH_OBJECT; 160 if ( p.e_attrs && is_entry_referral( &p )) { 161is_ref: p.e_attrs = NULL; 162 ndb_entry_get_data( op, &NA, 0 ); 163 rs->sr_ref = get_entry_referrals( op, &p ); 164 rs->sr_err = LDAP_REFERRAL; 165 rs->sr_flags = REP_REF_MUSTBEFREED; 166 attrs_free( p.e_attrs ); 167 p.e_attrs = NULL; 168 } 169 goto return_results; 170 } 171 172 p.e_name = pdn; 173 p.e_nname = pndn; 174 rs->sr_err = access_allowed( op, &p, 175 children, NULL, ACL_WADD, NULL ); 176 177 if ( ! rs->sr_err ) { 178 Debug( LDAP_DEBUG_TRACE, 179 LDAP_XSTRING(ndb_back_add) ": no write access to parent\n", 180 0, 0, 0 ); 181 rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 182 rs->sr_text = "no write access to parent"; 183 goto return_results; 184 } 185 186 if ( NA.ocs ) { 187 if ( is_entry_subentry( &p )) { 188 /* parent is a subentry, don't allow add */ 189 Debug( LDAP_DEBUG_TRACE, 190 LDAP_XSTRING(ndb_back_add) ": parent is subentry\n", 191 0, 0, 0 ); 192 rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION; 193 rs->sr_text = "parent is a subentry"; 194 goto return_results; 195 } 196 197 if ( is_entry_alias( &p ) ) { 198 /* parent is an alias, don't allow add */ 199 Debug( LDAP_DEBUG_TRACE, 200 LDAP_XSTRING(ndb_back_add) ": parent is alias\n", 201 0, 0, 0 ); 202 rs->sr_err = LDAP_ALIAS_PROBLEM; 203 rs->sr_text = "parent is an alias"; 204 goto return_results; 205 } 206 207 if ( is_entry_referral( &p ) ) { 208 /* parent is a referral, don't allow add */ 209 rs->sr_matched = p.e_name.bv_val; 210 goto is_ref; 211 } 212 } 213 214 rs->sr_err = access_allowed( op, op->ora_e, 215 entry, NULL, ACL_WADD, NULL ); 216 217 if ( ! rs->sr_err ) { 218 Debug( LDAP_DEBUG_TRACE, 219 LDAP_XSTRING(ndb_back_add) ": no write access to entry\n", 220 0, 0, 0 ); 221 rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 222 rs->sr_text = "no write access to entry"; 223 goto return_results;; 224 } 225 226 /* 227 * Check ACL for attribute write access 228 */ 229 if (!acl_check_modlist(op, op->ora_e, op->ora_modlist)) { 230 Debug( LDAP_DEBUG_TRACE, 231 LDAP_XSTRING(bdb_add) ": no write access to attribute\n", 232 0, 0, 0 ); 233 rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 234 rs->sr_text = "no write access to attribute"; 235 goto return_results;; 236 } 237 238 239 /* acquire entry ID */ 240 if ( op->ora_e->e_id == NOID ) { 241 rs->sr_err = ndb_next_id( op->o_bd, NA.ndb, &op->ora_e->e_id ); 242 if( rs->sr_err != 0 ) { 243 Debug( LDAP_DEBUG_TRACE, 244 LDAP_XSTRING(ndb_back_add) ": next_id failed (%d)\n", 245 rs->sr_err, 0, 0 ); 246 rs->sr_err = LDAP_OTHER; 247 rs->sr_text = "internal error"; 248 goto return_results; 249 } 250 } 251 252 if ( matched.bv_val ) 253 rdns.nr_num++; 254 NA.e = op->ora_e; 255 /* dn2id index */ 256 rs->sr_err = ndb_entry_put_info( op->o_bd, &NA, 0 ); 257 if ( rs->sr_err ) { 258 Debug( LDAP_DEBUG_TRACE, 259 LDAP_XSTRING(ndb_back_add) ": ndb_entry_put_info failed (%d)\n", 260 rs->sr_err, 0, 0 ); 261 rs->sr_text = "internal error"; 262 goto return_results; 263 } 264 265 /* id2entry index */ 266 rs->sr_err = ndb_entry_put_data( op->o_bd, &NA ); 267 if ( rs->sr_err ) { 268 Debug( LDAP_DEBUG_TRACE, 269 LDAP_XSTRING(ndb_back_add) ": ndb_entry_put_data failed (%d) %s(%d)\n", 270 rs->sr_err, NA.txn->getNdbError().message, NA.txn->getNdbError().code ); 271 rs->sr_text = "internal error"; 272 goto return_results; 273 } 274 275 /* post-read */ 276 if( op->o_postread ) { 277 if( postread_ctrl == NULL ) { 278 postread_ctrl = &ctrls[num_ctrls++]; 279 ctrls[num_ctrls] = NULL; 280 } 281 if ( slap_read_controls( op, rs, op->oq_add.rs_e, 282 &slap_post_read_bv, postread_ctrl ) ) 283 { 284 Debug( LDAP_DEBUG_TRACE, 285 "<=- " LDAP_XSTRING(ndb_back_add) ": post-read " 286 "failed!\n", 0, 0, 0 ); 287 if ( op->o_postread & SLAP_CONTROL_CRITICAL ) { 288 /* FIXME: is it correct to abort 289 * operation if control fails? */ 290 goto return_results; 291 } 292 } 293 } 294 295 if ( op->o_noop ) { 296 if (( rs->sr_err=NA.txn->execute( NdbTransaction::Rollback, 297 NdbOperation::AbortOnError, 1 )) != 0 ) { 298 rs->sr_text = "txn (no-op) failed"; 299 } else { 300 rs->sr_err = LDAP_X_NO_OPERATION; 301 } 302 303 } else { 304 if(( rs->sr_err=NA.txn->execute( NdbTransaction::Commit, 305 NdbOperation::AbortOnError, 1 )) != 0 ) { 306 rs->sr_text = "txn_commit failed"; 307 } else { 308 rs->sr_err = LDAP_SUCCESS; 309 } 310 } 311 312 if ( rs->sr_err != LDAP_SUCCESS && rs->sr_err != LDAP_X_NO_OPERATION ) { 313 Debug( LDAP_DEBUG_TRACE, 314 LDAP_XSTRING(ndb_back_add) ": %s : %s (%d)\n", 315 rs->sr_text, NA.txn->getNdbError().message, NA.txn->getNdbError().code ); 316 rs->sr_err = LDAP_OTHER; 317 goto return_results; 318 } 319 NA.txn->close(); 320 NA.txn = NULL; 321 322 Debug(LDAP_DEBUG_TRACE, 323 LDAP_XSTRING(ndb_back_add) ": added%s id=%08lx dn=\"%s\"\n", 324 op->o_noop ? " (no-op)" : "", 325 op->oq_add.rs_e->e_id, op->oq_add.rs_e->e_dn ); 326 327 rs->sr_text = NULL; 328 if( num_ctrls ) rs->sr_ctrls = ctrls; 329 330return_results: 331 success = rs->sr_err; 332 send_ldap_result( op, rs ); 333 slap_graduate_commit_csn( op ); 334 335 if( NA.txn != NULL ) { 336 NA.txn->execute( Rollback ); 337 NA.txn->close(); 338 } 339 340 if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) { 341 slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx ); 342 slap_sl_free( *postread_ctrl, op->o_tmpmemctx ); 343 } 344 345 return rs->sr_err; 346} 347