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