1 /* $NetBSD: add.c,v 1.1.1.1 2014/05/28 09:58:49 tron Exp $ */ 2 3 /* add.c - ldap mdb back-end add routine */ 4 /* $OpenLDAP$ */ 5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 6 * 7 * Copyright 2000-2014 The OpenLDAP Foundation. 8 * All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted only as authorized by the OpenLDAP 12 * Public License. 13 * 14 * A copy of this license is available in the file LICENSE in the 15 * top-level directory of the distribution or, alternatively, at 16 * <http://www.OpenLDAP.org/license.html>. 17 */ 18 19 #include "portable.h" 20 21 #include <stdio.h> 22 #include <ac/string.h> 23 24 #include "back-mdb.h" 25 26 int 27 mdb_add(Operation *op, SlapReply *rs ) 28 { 29 struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private; 30 struct berval pdn; 31 Entry *p = NULL, *oe = op->ora_e; 32 char textbuf[SLAP_TEXT_BUFLEN]; 33 size_t textlen = sizeof textbuf; 34 AttributeDescription *children = slap_schema.si_ad_children; 35 AttributeDescription *entry = slap_schema.si_ad_entry; 36 MDB_txn *txn = NULL; 37 MDB_cursor *mc = NULL; 38 MDB_cursor *mcd; 39 ID eid, pid = 0; 40 mdb_op_info opinfo = {{{ 0 }}}, *moi = &opinfo; 41 int subentry; 42 43 int success; 44 45 LDAPControl **postread_ctrl = NULL; 46 LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS]; 47 int num_ctrls = 0; 48 49 #ifdef LDAP_X_TXN 50 int settle = 0; 51 #endif 52 53 Debug(LDAP_DEBUG_ARGS, "==> " LDAP_XSTRING(mdb_add) ": %s\n", 54 op->ora_e->e_name.bv_val, 0, 0); 55 56 #ifdef LDAP_X_TXN 57 if( op->o_txnSpec ) { 58 /* acquire connection lock */ 59 ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex ); 60 if( op->o_conn->c_txn == CONN_TXN_INACTIVE ) { 61 rs->sr_text = "invalid transaction identifier"; 62 rs->sr_err = LDAP_X_TXN_ID_INVALID; 63 goto txnReturn; 64 } else if( op->o_conn->c_txn == CONN_TXN_SETTLE ) { 65 settle=1; 66 goto txnReturn; 67 } 68 69 if( op->o_conn->c_txn_backend == NULL ) { 70 op->o_conn->c_txn_backend = op->o_bd; 71 72 } else if( op->o_conn->c_txn_backend != op->o_bd ) { 73 rs->sr_text = "transaction cannot span multiple database contexts"; 74 rs->sr_err = LDAP_AFFECTS_MULTIPLE_DSAS; 75 goto txnReturn; 76 } 77 78 /* insert operation into transaction */ 79 80 rs->sr_text = "transaction specified"; 81 rs->sr_err = LDAP_X_TXN_SPECIFY_OKAY; 82 83 txnReturn: 84 /* release connection lock */ 85 ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex ); 86 87 if( !settle ) { 88 send_ldap_result( op, rs ); 89 return rs->sr_err; 90 } 91 } 92 #endif 93 94 ctrls[num_ctrls] = 0; 95 96 /* check entry's schema */ 97 rs->sr_err = entry_schema_check( op, op->ora_e, NULL, 98 get_relax(op), 1, NULL, &rs->sr_text, textbuf, textlen ); 99 if ( rs->sr_err != LDAP_SUCCESS ) { 100 Debug( LDAP_DEBUG_TRACE, 101 LDAP_XSTRING(mdb_add) ": entry failed schema check: " 102 "%s (%d)\n", rs->sr_text, rs->sr_err, 0 ); 103 goto return_results; 104 } 105 106 /* begin transaction */ 107 rs->sr_err = mdb_opinfo_get( op, mdb, 0, &moi ); 108 rs->sr_text = NULL; 109 if( rs->sr_err != 0 ) { 110 Debug( LDAP_DEBUG_TRACE, 111 LDAP_XSTRING(mdb_add) ": txn_begin failed: %s (%d)\n", 112 mdb_strerror(rs->sr_err), rs->sr_err, 0 ); 113 rs->sr_err = LDAP_OTHER; 114 rs->sr_text = "internal error"; 115 goto return_results; 116 } 117 txn = moi->moi_txn; 118 119 /* add opattrs to shadow as well, only missing attrs will actually 120 * be added; helps compatibility with older OL versions */ 121 rs->sr_err = slap_add_opattrs( op, &rs->sr_text, textbuf, textlen, 1 ); 122 if ( rs->sr_err != LDAP_SUCCESS ) { 123 Debug( LDAP_DEBUG_TRACE, 124 LDAP_XSTRING(mdb_add) ": entry failed op attrs add: " 125 "%s (%d)\n", rs->sr_text, rs->sr_err, 0 ); 126 goto return_results; 127 } 128 129 if ( get_assert( op ) && 130 ( test_filter( op, op->ora_e, get_assertion( op )) != LDAP_COMPARE_TRUE )) 131 { 132 rs->sr_err = LDAP_ASSERTION_FAILED; 133 goto return_results; 134 } 135 136 subentry = is_entry_subentry( op->ora_e ); 137 138 /* 139 * Get the parent dn and see if the corresponding entry exists. 140 */ 141 if ( be_issuffix( op->o_bd, &op->ora_e->e_nname ) ) { 142 pdn = slap_empty_bv; 143 } else { 144 dnParent( &op->ora_e->e_nname, &pdn ); 145 } 146 147 rs->sr_err = mdb_cursor_open( txn, mdb->mi_dn2id, &mcd ); 148 if( rs->sr_err != 0 ) { 149 Debug( LDAP_DEBUG_TRACE, 150 LDAP_XSTRING(mdb_add) ": mdb_cursor_open failed (%d)\n", 151 rs->sr_err, 0, 0 ); 152 rs->sr_err = LDAP_OTHER; 153 rs->sr_text = "internal error"; 154 goto return_results; 155 } 156 157 /* get entry or parent */ 158 rs->sr_err = mdb_dn2entry( op, txn, mcd, &op->ora_e->e_nname, &p, NULL, 1 ); 159 switch( rs->sr_err ) { 160 case 0: 161 rs->sr_err = LDAP_ALREADY_EXISTS; 162 mdb_entry_return( op, p ); 163 p = NULL; 164 goto return_results; 165 case MDB_NOTFOUND: 166 break; 167 case LDAP_BUSY: 168 rs->sr_text = "ldap server busy"; 169 goto return_results; 170 default: 171 rs->sr_err = LDAP_OTHER; 172 rs->sr_text = "internal error"; 173 goto return_results; 174 } 175 176 if ( !p ) 177 p = (Entry *)&slap_entry_root; 178 179 if ( !bvmatch( &pdn, &p->e_nname ) ) { 180 rs->sr_matched = ber_strdup_x( p->e_name.bv_val, 181 op->o_tmpmemctx ); 182 if ( p != (Entry *)&slap_entry_root && is_entry_referral( p )) { 183 BerVarray ref = get_entry_referrals( op, p ); 184 rs->sr_ref = referral_rewrite( ref, &p->e_name, 185 &op->o_req_dn, LDAP_SCOPE_DEFAULT ); 186 ber_bvarray_free( ref ); 187 } else { 188 rs->sr_ref = NULL; 189 } 190 if ( p != (Entry *)&slap_entry_root ) 191 mdb_entry_return( op, p ); 192 p = NULL; 193 Debug( LDAP_DEBUG_TRACE, 194 LDAP_XSTRING(mdb_add) ": parent " 195 "does not exist\n", 0, 0, 0 ); 196 197 rs->sr_err = LDAP_REFERRAL; 198 rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED; 199 goto return_results; 200 } 201 202 rs->sr_err = access_allowed( op, p, 203 children, NULL, ACL_WADD, NULL ); 204 205 if ( ! rs->sr_err ) { 206 if ( p != (Entry *)&slap_entry_root ) 207 mdb_entry_return( op, p ); 208 p = NULL; 209 210 Debug( LDAP_DEBUG_TRACE, 211 LDAP_XSTRING(mdb_add) ": no write access to parent\n", 212 0, 0, 0 ); 213 rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 214 rs->sr_text = "no write access to parent"; 215 goto return_results;; 216 } 217 218 if ( p != (Entry *)&slap_entry_root ) { 219 if ( is_entry_subentry( p ) ) { 220 mdb_entry_return( op, p ); 221 p = NULL; 222 /* parent is a subentry, don't allow add */ 223 Debug( LDAP_DEBUG_TRACE, 224 LDAP_XSTRING(mdb_add) ": parent is subentry\n", 225 0, 0, 0 ); 226 rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION; 227 rs->sr_text = "parent is a subentry"; 228 goto return_results;; 229 } 230 231 if ( is_entry_alias( p ) ) { 232 mdb_entry_return( op, p ); 233 p = NULL; 234 /* parent is an alias, don't allow add */ 235 Debug( LDAP_DEBUG_TRACE, 236 LDAP_XSTRING(mdb_add) ": parent is alias\n", 237 0, 0, 0 ); 238 rs->sr_err = LDAP_ALIAS_PROBLEM; 239 rs->sr_text = "parent is an alias"; 240 goto return_results;; 241 } 242 243 if ( is_entry_referral( p ) ) { 244 BerVarray ref = get_entry_referrals( op, p ); 245 /* parent is a referral, don't allow add */ 246 rs->sr_matched = ber_strdup_x( p->e_name.bv_val, 247 op->o_tmpmemctx ); 248 rs->sr_ref = referral_rewrite( ref, &p->e_name, 249 &op->o_req_dn, LDAP_SCOPE_DEFAULT ); 250 ber_bvarray_free( ref ); 251 mdb_entry_return( op, p ); 252 p = NULL; 253 Debug( LDAP_DEBUG_TRACE, 254 LDAP_XSTRING(mdb_add) ": parent is referral\n", 255 0, 0, 0 ); 256 257 rs->sr_err = LDAP_REFERRAL; 258 rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED; 259 goto return_results; 260 } 261 262 } 263 264 if ( subentry ) { 265 /* FIXME: */ 266 /* parent must be an administrative point of the required kind */ 267 } 268 269 /* free parent */ 270 if ( p != (Entry *)&slap_entry_root ) { 271 pid = p->e_id; 272 if ( p->e_nname.bv_len ) { 273 struct berval ppdn; 274 275 /* ITS#5326: use parent's DN if differs from provided one */ 276 dnParent( &op->ora_e->e_name, &ppdn ); 277 if ( !dn_match( &p->e_name, &ppdn ) ) { 278 struct berval rdn; 279 struct berval newdn; 280 281 dnRdn( &op->ora_e->e_name, &rdn ); 282 283 build_new_dn( &newdn, &p->e_name, &rdn, NULL ); 284 if ( op->ora_e->e_name.bv_val != op->o_req_dn.bv_val ) 285 ber_memfree( op->ora_e->e_name.bv_val ); 286 op->ora_e->e_name = newdn; 287 288 /* FIXME: should check whether 289 * dnNormalize(newdn) == e->e_nname ... */ 290 } 291 } 292 293 mdb_entry_return( op, p ); 294 } 295 p = NULL; 296 297 rs->sr_err = access_allowed( op, op->ora_e, 298 entry, NULL, ACL_WADD, NULL ); 299 300 if ( ! rs->sr_err ) { 301 Debug( LDAP_DEBUG_TRACE, 302 LDAP_XSTRING(mdb_add) ": no write access to entry\n", 303 0, 0, 0 ); 304 rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 305 rs->sr_text = "no write access to entry"; 306 goto return_results;; 307 } 308 309 /* 310 * Check ACL for attribute write access 311 */ 312 if (!acl_check_modlist(op, oe, op->ora_modlist)) { 313 Debug( LDAP_DEBUG_TRACE, 314 LDAP_XSTRING(mdb_add) ": no write access to attribute\n", 315 0, 0, 0 ); 316 rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 317 rs->sr_text = "no write access to attribute"; 318 goto return_results;; 319 } 320 321 rs->sr_err = mdb_cursor_open( txn, mdb->mi_id2entry, &mc ); 322 if( rs->sr_err != 0 ) { 323 Debug( LDAP_DEBUG_TRACE, 324 LDAP_XSTRING(mdb_add) ": mdb_cursor_open failed (%d)\n", 325 rs->sr_err, 0, 0 ); 326 rs->sr_err = LDAP_OTHER; 327 rs->sr_text = "internal error"; 328 goto return_results; 329 } 330 331 rs->sr_err = mdb_next_id( op->o_bd, mc, &eid ); 332 if( rs->sr_err != 0 ) { 333 Debug( LDAP_DEBUG_TRACE, 334 LDAP_XSTRING(mdb_add) ": next_id failed (%d)\n", 335 rs->sr_err, 0, 0 ); 336 rs->sr_err = LDAP_OTHER; 337 rs->sr_text = "internal error"; 338 goto return_results; 339 } 340 op->ora_e->e_id = eid; 341 342 /* dn2id index */ 343 rs->sr_err = mdb_dn2id_add( op, mcd, mcd, pid, 1, 1, op->ora_e ); 344 mdb_cursor_close( mcd ); 345 if ( rs->sr_err != 0 ) { 346 Debug( LDAP_DEBUG_TRACE, 347 LDAP_XSTRING(mdb_add) ": dn2id_add failed: %s (%d)\n", 348 mdb_strerror(rs->sr_err), rs->sr_err, 0 ); 349 350 switch( rs->sr_err ) { 351 case MDB_KEYEXIST: 352 rs->sr_err = LDAP_ALREADY_EXISTS; 353 break; 354 default: 355 rs->sr_err = LDAP_OTHER; 356 } 357 goto return_results; 358 } 359 360 /* attribute indexes */ 361 rs->sr_err = mdb_index_entry_add( op, txn, op->ora_e ); 362 if ( rs->sr_err != LDAP_SUCCESS ) { 363 Debug( LDAP_DEBUG_TRACE, 364 LDAP_XSTRING(mdb_add) ": index_entry_add failed\n", 365 0, 0, 0 ); 366 rs->sr_err = LDAP_OTHER; 367 rs->sr_text = "index generation failed"; 368 goto return_results; 369 } 370 371 /* id2entry index */ 372 rs->sr_err = mdb_id2entry_add( op, txn, mc, op->ora_e ); 373 if ( rs->sr_err != 0 ) { 374 Debug( LDAP_DEBUG_TRACE, 375 LDAP_XSTRING(mdb_add) ": id2entry_add failed\n", 376 0, 0, 0 ); 377 rs->sr_err = LDAP_OTHER; 378 rs->sr_text = "entry store failed"; 379 goto return_results; 380 } 381 382 /* post-read */ 383 if( op->o_postread ) { 384 if( postread_ctrl == NULL ) { 385 postread_ctrl = &ctrls[num_ctrls++]; 386 ctrls[num_ctrls] = NULL; 387 } 388 if ( slap_read_controls( op, rs, op->ora_e, 389 &slap_post_read_bv, postread_ctrl ) ) 390 { 391 Debug( LDAP_DEBUG_TRACE, 392 "<=- " LDAP_XSTRING(mdb_add) ": post-read " 393 "failed!\n", 0, 0, 0 ); 394 if ( op->o_postread & SLAP_CONTROL_CRITICAL ) { 395 /* FIXME: is it correct to abort 396 * operation if control fails? */ 397 goto return_results; 398 } 399 } 400 } 401 402 if ( moi == &opinfo ) { 403 LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.moi_oe, OpExtra, oe_next ); 404 opinfo.moi_oe.oe_key = NULL; 405 if ( op->o_noop ) { 406 mdb_txn_abort( txn ); 407 rs->sr_err = LDAP_X_NO_OPERATION; 408 txn = NULL; 409 goto return_results; 410 } 411 412 rs->sr_err = mdb_txn_commit( txn ); 413 txn = NULL; 414 if ( rs->sr_err != 0 ) { 415 rs->sr_text = "txn_commit failed"; 416 Debug( LDAP_DEBUG_ANY, 417 LDAP_XSTRING(mdb_add) ": %s : %s (%d)\n", 418 rs->sr_text, mdb_strerror(rs->sr_err), rs->sr_err ); 419 rs->sr_err = LDAP_OTHER; 420 goto return_results; 421 } 422 } 423 424 Debug(LDAP_DEBUG_TRACE, 425 LDAP_XSTRING(mdb_add) ": added%s id=%08lx dn=\"%s\"\n", 426 op->o_noop ? " (no-op)" : "", 427 op->ora_e->e_id, op->ora_e->e_dn ); 428 429 rs->sr_text = NULL; 430 if( num_ctrls ) rs->sr_ctrls = ctrls; 431 432 return_results: 433 success = rs->sr_err; 434 send_ldap_result( op, rs ); 435 436 if( moi == &opinfo ) { 437 if( txn != NULL ) { 438 mdb_txn_abort( txn ); 439 } 440 if ( opinfo.moi_oe.oe_key ) { 441 LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.moi_oe, OpExtra, oe_next ); 442 } 443 } else { 444 moi->moi_ref--; 445 } 446 447 if( success == LDAP_SUCCESS ) { 448 #if 0 449 if ( mdb->bi_txn_cp_kbyte ) { 450 TXN_CHECKPOINT( mdb->bi_dbenv, 451 mdb->bi_txn_cp_kbyte, mdb->bi_txn_cp_min, 0 ); 452 } 453 #endif 454 } 455 456 slap_graduate_commit_csn( op ); 457 458 if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) { 459 slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx ); 460 slap_sl_free( *postread_ctrl, op->o_tmpmemctx ); 461 } 462 return rs->sr_err; 463 } 464