1/* $NetBSD$ */ 2 3/* add.c - ldap BerkeleyDB back-end add routine */ 4/* OpenLDAP: pkg/ldap/servers/slapd/back-bdb/add.c,v 1.152.2.18 2010/04/13 20:23:23 kurt Exp */ 5/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 6 * 7 * Copyright 2000-2010 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-bdb.h" 25 26int 27bdb_add(Operation *op, SlapReply *rs ) 28{ 29 struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private; 30 struct berval pdn; 31 Entry *p = NULL, *oe = op->ora_e; 32 EntryInfo *ei; 33 char textbuf[SLAP_TEXT_BUFLEN]; 34 size_t textlen = sizeof textbuf; 35 AttributeDescription *children = slap_schema.si_ad_children; 36 AttributeDescription *entry = slap_schema.si_ad_entry; 37 DB_TXN *ltid = NULL, *lt2, *rtxn; 38 ID eid = NOID; 39 struct bdb_op_info opinfo = {{{ 0 }}}; 40 int subentry; 41 DB_LOCK lock; 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#ifdef LDAP_X_TXN 51 int settle = 0; 52#endif 53 54 Debug(LDAP_DEBUG_ARGS, "==> " LDAP_XSTRING(bdb_add) ": %s\n", 55 op->ora_e->e_name.bv_val, 0, 0); 56 57#ifdef LDAP_X_TXN 58 if( op->o_txnSpec ) { 59 /* acquire connection lock */ 60 ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex ); 61 if( op->o_conn->c_txn == CONN_TXN_INACTIVE ) { 62 rs->sr_text = "invalid transaction identifier"; 63 rs->sr_err = LDAP_X_TXN_ID_INVALID; 64 goto txnReturn; 65 } else if( op->o_conn->c_txn == CONN_TXN_SETTLE ) { 66 settle=1; 67 goto txnReturn; 68 } 69 70 if( op->o_conn->c_txn_backend == NULL ) { 71 op->o_conn->c_txn_backend = op->o_bd; 72 73 } else if( op->o_conn->c_txn_backend != op->o_bd ) { 74 rs->sr_text = "transaction cannot span multiple database contexts"; 75 rs->sr_err = LDAP_AFFECTS_MULTIPLE_DSAS; 76 goto txnReturn; 77 } 78 79 /* insert operation into transaction */ 80 81 rs->sr_text = "transaction specified"; 82 rs->sr_err = LDAP_X_TXN_SPECIFY_OKAY; 83 84txnReturn: 85 /* release connection lock */ 86 ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex ); 87 88 if( !settle ) { 89 send_ldap_result( op, rs ); 90 return rs->sr_err; 91 } 92 } 93#endif 94 95 ctrls[num_ctrls] = 0; 96 97 /* check entry's schema */ 98 rs->sr_err = entry_schema_check( op, op->ora_e, NULL, 99 get_relax(op), 1, NULL, &rs->sr_text, textbuf, textlen ); 100 if ( rs->sr_err != LDAP_SUCCESS ) { 101 Debug( LDAP_DEBUG_TRACE, 102 LDAP_XSTRING(bdb_add) ": entry failed schema check: " 103 "%s (%d)\n", rs->sr_text, rs->sr_err, 0 ); 104 goto return_results; 105 } 106 107 /* add opattrs to shadow as well, only missing attrs will actually 108 * be added; helps compatibility with older OL versions */ 109 rs->sr_err = slap_add_opattrs( op, &rs->sr_text, textbuf, textlen, 1 ); 110 if ( rs->sr_err != LDAP_SUCCESS ) { 111 Debug( LDAP_DEBUG_TRACE, 112 LDAP_XSTRING(bdb_add) ": entry failed op attrs add: " 113 "%s (%d)\n", rs->sr_text, rs->sr_err, 0 ); 114 goto return_results; 115 } 116 117 if ( get_assert( op ) && 118 ( test_filter( op, op->ora_e, get_assertion( op )) != LDAP_COMPARE_TRUE )) 119 { 120 rs->sr_err = LDAP_ASSERTION_FAILED; 121 goto return_results; 122 } 123 124 subentry = is_entry_subentry( op->ora_e ); 125 126 /* Get our reader TXN */ 127 rs->sr_err = bdb_reader_get( op, bdb->bi_dbenv, &rtxn ); 128 129 if( 0 ) { 130retry: /* transaction retry */ 131 if( p ) { 132 /* free parent and reader lock */ 133 if ( p != (Entry *)&slap_entry_root ) { 134 bdb_unlocked_cache_return_entry_r( bdb, p ); 135 } 136 p = NULL; 137 } 138 rs->sr_err = TXN_ABORT( ltid ); 139 ltid = NULL; 140 LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.boi_oe, OpExtra, oe_next ); 141 opinfo.boi_oe.oe_key = NULL; 142 op->o_do_not_cache = opinfo.boi_acl_cache; 143 if( rs->sr_err != 0 ) { 144 rs->sr_err = LDAP_OTHER; 145 rs->sr_text = "internal error"; 146 goto return_results; 147 } 148 if ( op->o_abandon ) { 149 rs->sr_err = SLAPD_ABANDON; 150 goto return_results; 151 } 152 bdb_trans_backoff( ++num_retries ); 153 } 154 155 /* begin transaction */ 156 rs->sr_err = TXN_BEGIN( bdb->bi_dbenv, NULL, <id, 157 bdb->bi_db_opflags ); 158 rs->sr_text = NULL; 159 if( rs->sr_err != 0 ) { 160 Debug( LDAP_DEBUG_TRACE, 161 LDAP_XSTRING(bdb_add) ": txn_begin failed: %s (%d)\n", 162 db_strerror(rs->sr_err), rs->sr_err, 0 ); 163 rs->sr_err = LDAP_OTHER; 164 rs->sr_text = "internal error"; 165 goto return_results; 166 } 167 168 opinfo.boi_oe.oe_key = bdb; 169 opinfo.boi_txn = ltid; 170 opinfo.boi_err = 0; 171 opinfo.boi_acl_cache = op->o_do_not_cache; 172 LDAP_SLIST_INSERT_HEAD( &op->o_extra, &opinfo.boi_oe, oe_next ); 173 174 /* 175 * Get the parent dn and see if the corresponding entry exists. 176 */ 177 if ( be_issuffix( op->o_bd, &op->ora_e->e_nname ) ) { 178 pdn = slap_empty_bv; 179 } else { 180 dnParent( &op->ora_e->e_nname, &pdn ); 181 } 182 183 /* get entry or parent */ 184 rs->sr_err = bdb_dn2entry( op, ltid, &op->ora_e->e_nname, &ei, 185 1, &lock ); 186 switch( rs->sr_err ) { 187 case 0: 188 rs->sr_err = LDAP_ALREADY_EXISTS; 189 goto return_results; 190 case DB_NOTFOUND: 191 break; 192 case DB_LOCK_DEADLOCK: 193 case DB_LOCK_NOTGRANTED: 194 goto retry; 195 case LDAP_BUSY: 196 rs->sr_text = "ldap server busy"; 197 goto return_results; 198 default: 199 rs->sr_err = LDAP_OTHER; 200 rs->sr_text = "internal error"; 201 goto return_results; 202 } 203 204 p = ei->bei_e; 205 if ( !p ) 206 p = (Entry *)&slap_entry_root; 207 208 if ( !bvmatch( &pdn, &p->e_nname ) ) { 209 rs->sr_matched = ber_strdup_x( p->e_name.bv_val, 210 op->o_tmpmemctx ); 211 rs->sr_ref = is_entry_referral( p ) 212 ? get_entry_referrals( op, p ) 213 : NULL; 214 if ( p != (Entry *)&slap_entry_root ) 215 bdb_unlocked_cache_return_entry_r( bdb, p ); 216 p = NULL; 217 Debug( LDAP_DEBUG_TRACE, 218 LDAP_XSTRING(bdb_add) ": parent " 219 "does not exist\n", 0, 0, 0 ); 220 221 rs->sr_err = LDAP_REFERRAL; 222 rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED; 223 goto return_results; 224 } 225 226 rs->sr_err = access_allowed( op, p, 227 children, NULL, ACL_WADD, NULL ); 228 229 if ( ! rs->sr_err ) { 230 switch( opinfo.boi_err ) { 231 case DB_LOCK_DEADLOCK: 232 case DB_LOCK_NOTGRANTED: 233 goto retry; 234 } 235 236 if ( p != (Entry *)&slap_entry_root ) 237 bdb_unlocked_cache_return_entry_r( bdb, p ); 238 p = NULL; 239 240 Debug( LDAP_DEBUG_TRACE, 241 LDAP_XSTRING(bdb_add) ": no write access to parent\n", 242 0, 0, 0 ); 243 rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 244 rs->sr_text = "no write access to parent"; 245 goto return_results;; 246 } 247 248 if ( p != (Entry *)&slap_entry_root ) { 249 if ( is_entry_subentry( p ) ) { 250 bdb_unlocked_cache_return_entry_r( bdb, p ); 251 p = NULL; 252 /* parent is a subentry, don't allow add */ 253 Debug( LDAP_DEBUG_TRACE, 254 LDAP_XSTRING(bdb_add) ": parent is subentry\n", 255 0, 0, 0 ); 256 rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION; 257 rs->sr_text = "parent is a subentry"; 258 goto return_results;; 259 } 260 261 if ( is_entry_alias( p ) ) { 262 bdb_unlocked_cache_return_entry_r( bdb, p ); 263 p = NULL; 264 /* parent is an alias, don't allow add */ 265 Debug( LDAP_DEBUG_TRACE, 266 LDAP_XSTRING(bdb_add) ": parent is alias\n", 267 0, 0, 0 ); 268 rs->sr_err = LDAP_ALIAS_PROBLEM; 269 rs->sr_text = "parent is an alias"; 270 goto return_results;; 271 } 272 273 if ( is_entry_referral( p ) ) { 274 /* parent is a referral, don't allow add */ 275 rs->sr_matched = ber_strdup_x( p->e_name.bv_val, 276 op->o_tmpmemctx ); 277 rs->sr_ref = get_entry_referrals( op, p ); 278 bdb_unlocked_cache_return_entry_r( bdb, p ); 279 p = NULL; 280 Debug( LDAP_DEBUG_TRACE, 281 LDAP_XSTRING(bdb_add) ": parent is referral\n", 282 0, 0, 0 ); 283 284 rs->sr_err = LDAP_REFERRAL; 285 rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED; 286 goto return_results; 287 } 288 289 } 290 291 if ( subentry ) { 292 /* FIXME: */ 293 /* parent must be an administrative point of the required kind */ 294 } 295 296 /* free parent and reader lock */ 297 if ( p != (Entry *)&slap_entry_root ) { 298 bdb_unlocked_cache_return_entry_r( bdb, p ); 299 } 300 p = NULL; 301 302 rs->sr_err = access_allowed( op, op->ora_e, 303 entry, NULL, ACL_WADD, NULL ); 304 305 if ( ! rs->sr_err ) { 306 switch( opinfo.boi_err ) { 307 case DB_LOCK_DEADLOCK: 308 case DB_LOCK_NOTGRANTED: 309 goto retry; 310 } 311 312 Debug( LDAP_DEBUG_TRACE, 313 LDAP_XSTRING(bdb_add) ": no write access to entry\n", 314 0, 0, 0 ); 315 rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 316 rs->sr_text = "no write access to entry"; 317 goto return_results;; 318 } 319 320 /* 321 * Check ACL for attribute write access 322 */ 323 if (!acl_check_modlist(op, oe, op->ora_modlist)) { 324 switch( opinfo.boi_err ) { 325 case DB_LOCK_DEADLOCK: 326 case DB_LOCK_NOTGRANTED: 327 goto retry; 328 } 329 330 Debug( LDAP_DEBUG_TRACE, 331 LDAP_XSTRING(bdb_add) ": no write access to attribute\n", 332 0, 0, 0 ); 333 rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 334 rs->sr_text = "no write access to attribute"; 335 goto return_results;; 336 } 337 338 if ( eid == NOID ) { 339 rs->sr_err = bdb_next_id( op->o_bd, &eid ); 340 if( rs->sr_err != 0 ) { 341 Debug( LDAP_DEBUG_TRACE, 342 LDAP_XSTRING(bdb_add) ": next_id failed (%d)\n", 343 rs->sr_err, 0, 0 ); 344 rs->sr_err = LDAP_OTHER; 345 rs->sr_text = "internal error"; 346 goto return_results; 347 } 348 op->ora_e->e_id = eid; 349 } 350 351 /* nested transaction */ 352 rs->sr_err = TXN_BEGIN( bdb->bi_dbenv, ltid, <2, 353 bdb->bi_db_opflags ); 354 rs->sr_text = NULL; 355 if( rs->sr_err != 0 ) { 356 Debug( LDAP_DEBUG_TRACE, 357 LDAP_XSTRING(bdb_add) ": txn_begin(2) failed: " 358 "%s (%d)\n", db_strerror(rs->sr_err), rs->sr_err, 0 ); 359 rs->sr_err = LDAP_OTHER; 360 rs->sr_text = "internal error"; 361 goto return_results; 362 } 363 364 /* dn2id index */ 365 rs->sr_err = bdb_dn2id_add( op, lt2, ei, op->ora_e ); 366 if ( rs->sr_err != 0 ) { 367 Debug( LDAP_DEBUG_TRACE, 368 LDAP_XSTRING(bdb_add) ": dn2id_add failed: %s (%d)\n", 369 db_strerror(rs->sr_err), rs->sr_err, 0 ); 370 371 switch( rs->sr_err ) { 372 case DB_LOCK_DEADLOCK: 373 case DB_LOCK_NOTGRANTED: 374 goto retry; 375 case DB_KEYEXIST: 376 rs->sr_err = LDAP_ALREADY_EXISTS; 377 break; 378 default: 379 rs->sr_err = LDAP_OTHER; 380 } 381 goto return_results; 382 } 383 384 /* attribute indexes */ 385 rs->sr_err = bdb_index_entry_add( op, lt2, op->ora_e ); 386 if ( rs->sr_err != LDAP_SUCCESS ) { 387 Debug( LDAP_DEBUG_TRACE, 388 LDAP_XSTRING(bdb_add) ": index_entry_add failed\n", 389 0, 0, 0 ); 390 switch( rs->sr_err ) { 391 case DB_LOCK_DEADLOCK: 392 case DB_LOCK_NOTGRANTED: 393 goto retry; 394 default: 395 rs->sr_err = LDAP_OTHER; 396 } 397 rs->sr_text = "index generation failed"; 398 goto return_results; 399 } 400 401 /* id2entry index */ 402 rs->sr_err = bdb_id2entry_add( op->o_bd, lt2, op->ora_e ); 403 if ( rs->sr_err != 0 ) { 404 Debug( LDAP_DEBUG_TRACE, 405 LDAP_XSTRING(bdb_add) ": id2entry_add failed\n", 406 0, 0, 0 ); 407 switch( rs->sr_err ) { 408 case DB_LOCK_DEADLOCK: 409 case DB_LOCK_NOTGRANTED: 410 goto retry; 411 default: 412 rs->sr_err = LDAP_OTHER; 413 } 414 rs->sr_text = "entry store failed"; 415 goto return_results; 416 } 417 418 if ( TXN_COMMIT( lt2, 0 ) != 0 ) { 419 rs->sr_err = LDAP_OTHER; 420 rs->sr_text = "txn_commit(2) failed"; 421 goto return_results; 422 } 423 424 /* post-read */ 425 if( op->o_postread ) { 426 if( postread_ctrl == NULL ) { 427 postread_ctrl = &ctrls[num_ctrls++]; 428 ctrls[num_ctrls] = NULL; 429 } 430 if ( slap_read_controls( op, rs, op->ora_e, 431 &slap_post_read_bv, postread_ctrl ) ) 432 { 433 Debug( LDAP_DEBUG_TRACE, 434 "<=- " LDAP_XSTRING(bdb_add) ": post-read " 435 "failed!\n", 0, 0, 0 ); 436 if ( op->o_postread & SLAP_CONTROL_CRITICAL ) { 437 /* FIXME: is it correct to abort 438 * operation if control fails? */ 439 goto return_results; 440 } 441 } 442 } 443 444 if ( op->o_noop ) { 445 if (( rs->sr_err=TXN_ABORT( ltid )) != 0 ) { 446 rs->sr_text = "txn_abort (no-op) failed"; 447 } else { 448 rs->sr_err = LDAP_X_NO_OPERATION; 449 ltid = NULL; 450 goto return_results; 451 } 452 453 } else { 454 struct berval nrdn; 455 456 /* pick the RDN if not suffix; otherwise pick the entire DN */ 457 if (pdn.bv_len) { 458 nrdn.bv_val = op->ora_e->e_nname.bv_val; 459 nrdn.bv_len = pdn.bv_val - op->ora_e->e_nname.bv_val - 1; 460 } else { 461 nrdn = op->ora_e->e_nname; 462 } 463 464 /* Use the reader txn here, outside the add txn */ 465 bdb_cache_add( bdb, ei, op->ora_e, &nrdn, rtxn, &lock ); 466 467 if(( rs->sr_err=TXN_COMMIT( ltid, 0 )) != 0 ) { 468 rs->sr_text = "txn_commit failed"; 469 } else { 470 rs->sr_err = LDAP_SUCCESS; 471 } 472 } 473 474 ltid = NULL; 475 LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.boi_oe, OpExtra, oe_next ); 476 opinfo.boi_oe.oe_key = NULL; 477 478 if ( rs->sr_err != LDAP_SUCCESS ) { 479 Debug( LDAP_DEBUG_TRACE, 480 LDAP_XSTRING(bdb_add) ": %s : %s (%d)\n", 481 rs->sr_text, db_strerror(rs->sr_err), rs->sr_err ); 482 rs->sr_err = LDAP_OTHER; 483 goto return_results; 484 } 485 486 Debug(LDAP_DEBUG_TRACE, 487 LDAP_XSTRING(bdb_add) ": added%s id=%08lx dn=\"%s\"\n", 488 op->o_noop ? " (no-op)" : "", 489 op->ora_e->e_id, op->ora_e->e_dn ); 490 491 rs->sr_text = NULL; 492 if( num_ctrls ) rs->sr_ctrls = ctrls; 493 494return_results: 495 success = rs->sr_err; 496 send_ldap_result( op, rs ); 497 slap_graduate_commit_csn( op ); 498 499 if( ltid != NULL ) { 500 TXN_ABORT( ltid ); 501 } 502 if ( opinfo.boi_oe.oe_key ) { 503 LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.boi_oe, OpExtra, oe_next ); 504 } 505 506 if( success == LDAP_SUCCESS ) { 507 /* We own the entry now, and it can be purged at will 508 * Check to make sure it's the same entry we entered with. 509 * Possibly a callback may have mucked with it, although 510 * in general callbacks should treat the entry as read-only. 511 */ 512 bdb_cache_return_entry_r( bdb, oe, &lock ); 513 if ( op->ora_e == oe ) 514 op->ora_e = NULL; 515 516 if ( bdb->bi_txn_cp_kbyte ) { 517 TXN_CHECKPOINT( bdb->bi_dbenv, 518 bdb->bi_txn_cp_kbyte, bdb->bi_txn_cp_min, 0 ); 519 } 520 } 521 522 if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) { 523 slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx ); 524 slap_sl_free( *postread_ctrl, op->o_tmpmemctx ); 525 } 526 527 return rs->sr_err; 528} 529