1 /* $NetBSD: delete.c,v 1.1.1.1 2014/05/28 09:58:49 tron Exp $ */ 2 3 /* delete.c - mdb backend delete 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 "lutil.h" 25 #include "back-mdb.h" 26 27 int 28 mdb_delete( Operation *op, SlapReply *rs ) 29 { 30 struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private; 31 struct berval pdn = {0, NULL}; 32 Entry *e = NULL; 33 Entry *p = NULL; 34 int manageDSAit = get_manageDSAit( op ); 35 AttributeDescription *children = slap_schema.si_ad_children; 36 AttributeDescription *entry = slap_schema.si_ad_entry; 37 MDB_txn *txn = NULL; 38 MDB_cursor *mc; 39 mdb_op_info opinfo = {{{ 0 }}}, *moi = &opinfo; 40 41 LDAPControl **preread_ctrl = NULL; 42 LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS]; 43 int num_ctrls = 0; 44 45 int parent_is_glue = 0; 46 int parent_is_leaf = 0; 47 48 #ifdef LDAP_X_TXN 49 int settle = 0; 50 #endif 51 52 Debug( LDAP_DEBUG_ARGS, "==> " LDAP_XSTRING(mdb_delete) ": %s\n", 53 op->o_req_dn.bv_val, 0, 0 ); 54 55 #ifdef LDAP_X_TXN 56 if( op->o_txnSpec ) { 57 /* acquire connection lock */ 58 ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex ); 59 if( op->o_conn->c_txn == CONN_TXN_INACTIVE ) { 60 rs->sr_text = "invalid transaction identifier"; 61 rs->sr_err = LDAP_X_TXN_ID_INVALID; 62 goto txnReturn; 63 } else if( op->o_conn->c_txn == CONN_TXN_SETTLE ) { 64 settle=1; 65 goto txnReturn; 66 } 67 68 if( op->o_conn->c_txn_backend == NULL ) { 69 op->o_conn->c_txn_backend = op->o_bd; 70 71 } else if( op->o_conn->c_txn_backend != op->o_bd ) { 72 rs->sr_text = "transaction cannot span multiple database contexts"; 73 rs->sr_err = LDAP_AFFECTS_MULTIPLE_DSAS; 74 goto txnReturn; 75 } 76 77 /* insert operation into transaction */ 78 79 rs->sr_text = "transaction specified"; 80 rs->sr_err = LDAP_X_TXN_SPECIFY_OKAY; 81 82 txnReturn: 83 /* release connection lock */ 84 ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex ); 85 86 if( !settle ) { 87 send_ldap_result( op, rs ); 88 return rs->sr_err; 89 } 90 } 91 #endif 92 93 ctrls[num_ctrls] = 0; 94 95 /* begin transaction */ 96 rs->sr_err = mdb_opinfo_get( op, mdb, 0, &moi ); 97 rs->sr_text = NULL; 98 if( rs->sr_err != 0 ) { 99 Debug( LDAP_DEBUG_TRACE, 100 LDAP_XSTRING(mdb_delete) ": txn_begin failed: " 101 "%s (%d)\n", mdb_strerror(rs->sr_err), rs->sr_err, 0 ); 102 rs->sr_err = LDAP_OTHER; 103 rs->sr_text = "internal error"; 104 goto return_results; 105 } 106 txn = moi->moi_txn; 107 108 /* allocate CSN */ 109 if ( BER_BVISNULL( &op->o_csn ) ) { 110 struct berval csn; 111 char csnbuf[LDAP_PVT_CSNSTR_BUFSIZE]; 112 113 csn.bv_val = csnbuf; 114 csn.bv_len = sizeof(csnbuf); 115 slap_get_csn( op, &csn, 1 ); 116 } 117 118 if ( !be_issuffix( op->o_bd, &op->o_req_ndn ) ) { 119 dnParent( &op->o_req_ndn, &pdn ); 120 } 121 122 rs->sr_err = mdb_cursor_open( txn, mdb->mi_dn2id, &mc ); 123 if ( rs->sr_err ) { 124 rs->sr_err = LDAP_OTHER; 125 rs->sr_text = "internal error"; 126 goto return_results; 127 } 128 /* get parent */ 129 rs->sr_err = mdb_dn2entry( op, txn, mc, &pdn, &p, NULL, 1 ); 130 switch( rs->sr_err ) { 131 case 0: 132 case MDB_NOTFOUND: 133 break; 134 case LDAP_BUSY: 135 rs->sr_text = "ldap server busy"; 136 goto return_results; 137 default: 138 rs->sr_err = LDAP_OTHER; 139 rs->sr_text = "internal error"; 140 goto return_results; 141 } 142 if ( rs->sr_err == MDB_NOTFOUND ) { 143 Debug( LDAP_DEBUG_ARGS, 144 "<=- " LDAP_XSTRING(mdb_delete) ": no such object %s\n", 145 op->o_req_dn.bv_val, 0, 0); 146 147 if ( p && !BER_BVISEMPTY( &p->e_name )) { 148 rs->sr_matched = ch_strdup( p->e_name.bv_val ); 149 if ( is_entry_referral( p )) { 150 BerVarray ref = get_entry_referrals( op, p ); 151 rs->sr_ref = referral_rewrite( ref, &p->e_name, 152 &op->o_req_dn, LDAP_SCOPE_DEFAULT ); 153 ber_bvarray_free( ref ); 154 } else { 155 rs->sr_ref = NULL; 156 } 157 } else { 158 rs->sr_ref = referral_rewrite( default_referral, NULL, 159 &op->o_req_dn, LDAP_SCOPE_DEFAULT ); 160 } 161 if ( p ) { 162 mdb_entry_return( op, p ); 163 p = NULL; 164 } 165 166 rs->sr_err = LDAP_REFERRAL; 167 rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED; 168 goto return_results; 169 } 170 171 /* get entry */ 172 rs->sr_err = mdb_dn2entry( op, txn, mc, &op->o_req_ndn, &e, NULL, 0 ); 173 switch( rs->sr_err ) { 174 case MDB_NOTFOUND: 175 e = p; 176 p = NULL; 177 case 0: 178 break; 179 case LDAP_BUSY: 180 rs->sr_text = "ldap server busy"; 181 goto return_results; 182 default: 183 rs->sr_err = LDAP_OTHER; 184 rs->sr_text = "internal error"; 185 goto return_results; 186 } 187 188 /* FIXME : dn2entry() should return non-glue entry */ 189 if ( rs->sr_err == MDB_NOTFOUND || ( !manageDSAit && is_entry_glue( e ))) { 190 Debug( LDAP_DEBUG_ARGS, 191 "<=- " LDAP_XSTRING(mdb_delete) ": no such object %s\n", 192 op->o_req_dn.bv_val, 0, 0); 193 194 rs->sr_matched = ch_strdup( e->e_dn ); 195 if ( is_entry_referral( e )) { 196 BerVarray ref = get_entry_referrals( op, e ); 197 rs->sr_ref = referral_rewrite( ref, &e->e_name, 198 &op->o_req_dn, LDAP_SCOPE_DEFAULT ); 199 ber_bvarray_free( ref ); 200 } else { 201 rs->sr_ref = NULL; 202 } 203 mdb_entry_return( op, e ); 204 e = NULL; 205 206 rs->sr_err = LDAP_REFERRAL; 207 rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED; 208 goto return_results; 209 } 210 211 if ( pdn.bv_len != 0 ) { 212 /* check parent for "children" acl */ 213 rs->sr_err = access_allowed( op, p, 214 children, NULL, ACL_WDEL, NULL ); 215 216 if ( !rs->sr_err ) { 217 Debug( LDAP_DEBUG_TRACE, 218 "<=- " LDAP_XSTRING(mdb_delete) ": no write " 219 "access to parent\n", 0, 0, 0 ); 220 rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 221 rs->sr_text = "no write access to parent"; 222 goto return_results; 223 } 224 225 } else { 226 /* no parent, must be root to delete */ 227 if( ! be_isroot( op ) ) { 228 if ( be_issuffix( op->o_bd, (struct berval *)&slap_empty_bv ) 229 || be_shadow_update( op ) ) { 230 p = (Entry *)&slap_entry_root; 231 232 /* check parent for "children" acl */ 233 rs->sr_err = access_allowed( op, p, 234 children, NULL, ACL_WDEL, NULL ); 235 236 p = NULL; 237 238 if ( !rs->sr_err ) { 239 Debug( LDAP_DEBUG_TRACE, 240 "<=- " LDAP_XSTRING(mdb_delete) 241 ": no 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 } else { 249 Debug( LDAP_DEBUG_TRACE, 250 "<=- " LDAP_XSTRING(mdb_delete) 251 ": no parent and not root\n", 0, 0, 0 ); 252 rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 253 goto return_results; 254 } 255 } 256 } 257 258 if ( get_assert( op ) && 259 ( test_filter( op, e, get_assertion( op )) != LDAP_COMPARE_TRUE )) 260 { 261 rs->sr_err = LDAP_ASSERTION_FAILED; 262 goto return_results; 263 } 264 265 rs->sr_err = access_allowed( op, e, 266 entry, NULL, ACL_WDEL, NULL ); 267 268 if ( !rs->sr_err ) { 269 Debug( LDAP_DEBUG_TRACE, 270 "<=- " LDAP_XSTRING(mdb_delete) ": no write access " 271 "to entry\n", 0, 0, 0 ); 272 rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 273 rs->sr_text = "no write access to entry"; 274 goto return_results; 275 } 276 277 if ( !manageDSAit && is_entry_referral( e ) ) { 278 /* entry is a referral, don't allow delete */ 279 rs->sr_ref = get_entry_referrals( op, e ); 280 281 Debug( LDAP_DEBUG_TRACE, 282 LDAP_XSTRING(mdb_delete) ": entry is referral\n", 283 0, 0, 0 ); 284 285 rs->sr_err = LDAP_REFERRAL; 286 rs->sr_matched = ch_strdup( e->e_name.bv_val ); 287 rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED; 288 goto return_results; 289 } 290 291 /* pre-read */ 292 if( op->o_preread ) { 293 if( preread_ctrl == NULL ) { 294 preread_ctrl = &ctrls[num_ctrls++]; 295 ctrls[num_ctrls] = NULL; 296 } 297 if( slap_read_controls( op, rs, e, 298 &slap_pre_read_bv, preread_ctrl ) ) 299 { 300 Debug( LDAP_DEBUG_TRACE, 301 "<=- " LDAP_XSTRING(mdb_delete) ": pre-read " 302 "failed!\n", 0, 0, 0 ); 303 if ( op->o_preread & SLAP_CONTROL_CRITICAL ) { 304 /* FIXME: is it correct to abort 305 * operation if control fails? */ 306 goto return_results; 307 } 308 } 309 } 310 311 rs->sr_text = NULL; 312 313 /* Can't do it if we have kids */ 314 rs->sr_err = mdb_dn2id_children( op, txn, e ); 315 if( rs->sr_err != MDB_NOTFOUND ) { 316 switch( rs->sr_err ) { 317 case 0: 318 Debug(LDAP_DEBUG_ARGS, 319 "<=- " LDAP_XSTRING(mdb_delete) 320 ": non-leaf %s\n", 321 op->o_req_dn.bv_val, 0, 0); 322 rs->sr_err = LDAP_NOT_ALLOWED_ON_NONLEAF; 323 rs->sr_text = "subordinate objects must be deleted first"; 324 break; 325 default: 326 Debug(LDAP_DEBUG_ARGS, 327 "<=- " LDAP_XSTRING(mdb_delete) 328 ": has_children failed: %s (%d)\n", 329 mdb_strerror(rs->sr_err), rs->sr_err, 0 ); 330 rs->sr_err = LDAP_OTHER; 331 rs->sr_text = "internal error"; 332 } 333 goto return_results; 334 } 335 336 /* delete from dn2id */ 337 rs->sr_err = mdb_dn2id_delete( op, mc, e->e_id, 1 ); 338 mdb_cursor_close( mc ); 339 if ( rs->sr_err != 0 ) { 340 Debug(LDAP_DEBUG_TRACE, 341 "<=- " LDAP_XSTRING(mdb_delete) ": dn2id failed: " 342 "%s (%d)\n", mdb_strerror(rs->sr_err), rs->sr_err, 0 ); 343 rs->sr_text = "DN index delete failed"; 344 rs->sr_err = LDAP_OTHER; 345 goto return_results; 346 } 347 348 /* delete indices for old attributes */ 349 rs->sr_err = mdb_index_entry_del( op, txn, e ); 350 if ( rs->sr_err != LDAP_SUCCESS ) { 351 Debug(LDAP_DEBUG_TRACE, 352 "<=- " LDAP_XSTRING(mdb_delete) ": index failed: " 353 "%s (%d)\n", mdb_strerror(rs->sr_err), rs->sr_err, 0 ); 354 rs->sr_text = "entry index delete failed"; 355 rs->sr_err = LDAP_OTHER; 356 goto return_results; 357 } 358 359 /* fixup delete CSN */ 360 if ( !SLAP_SHADOW( op->o_bd )) { 361 struct berval vals[2]; 362 363 assert( !BER_BVISNULL( &op->o_csn ) ); 364 vals[0] = op->o_csn; 365 BER_BVZERO( &vals[1] ); 366 rs->sr_err = mdb_index_values( op, txn, slap_schema.si_ad_entryCSN, 367 vals, 0, SLAP_INDEX_ADD_OP ); 368 if ( rs->sr_err != LDAP_SUCCESS ) { 369 rs->sr_text = "entryCSN index update failed"; 370 rs->sr_err = LDAP_OTHER; 371 goto return_results; 372 } 373 } 374 375 /* delete from id2entry */ 376 rs->sr_err = mdb_id2entry_delete( op->o_bd, txn, e ); 377 if ( rs->sr_err != 0 ) { 378 Debug( LDAP_DEBUG_TRACE, 379 "<=- " LDAP_XSTRING(mdb_delete) ": id2entry failed: " 380 "%s (%d)\n", mdb_strerror(rs->sr_err), rs->sr_err, 0 ); 381 rs->sr_text = "entry delete failed"; 382 rs->sr_err = LDAP_OTHER; 383 goto return_results; 384 } 385 386 if ( pdn.bv_len != 0 ) { 387 parent_is_glue = is_entry_glue(p); 388 rs->sr_err = mdb_dn2id_children( op, txn, p ); 389 if ( rs->sr_err != MDB_NOTFOUND ) { 390 switch( rs->sr_err ) { 391 case 0: 392 break; 393 default: 394 Debug(LDAP_DEBUG_ARGS, 395 "<=- " LDAP_XSTRING(mdb_delete) 396 ": has_children failed: %s (%d)\n", 397 mdb_strerror(rs->sr_err), rs->sr_err, 0 ); 398 rs->sr_err = LDAP_OTHER; 399 rs->sr_text = "internal error"; 400 goto return_results; 401 } 402 parent_is_leaf = 1; 403 } 404 mdb_entry_return( op, p ); 405 p = NULL; 406 } 407 408 if( moi == &opinfo ) { 409 LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.moi_oe, OpExtra, oe_next ); 410 opinfo.moi_oe.oe_key = NULL; 411 if( op->o_noop ) { 412 mdb_txn_abort( txn ); 413 rs->sr_err = LDAP_X_NO_OPERATION; 414 txn = NULL; 415 goto return_results; 416 } else { 417 rs->sr_err = mdb_txn_commit( txn ); 418 } 419 txn = NULL; 420 } 421 422 if( rs->sr_err != 0 ) { 423 Debug( LDAP_DEBUG_ANY, 424 LDAP_XSTRING(mdb_delete) ": txn_%s failed: %s (%d)\n", 425 op->o_noop ? "abort (no-op)" : "commit", 426 mdb_strerror(rs->sr_err), rs->sr_err ); 427 rs->sr_err = LDAP_OTHER; 428 rs->sr_text = "commit failed"; 429 430 goto return_results; 431 } 432 433 Debug( LDAP_DEBUG_TRACE, 434 LDAP_XSTRING(mdb_delete) ": deleted%s id=%08lx dn=\"%s\"\n", 435 op->o_noop ? " (no-op)" : "", 436 e->e_id, op->o_req_dn.bv_val ); 437 rs->sr_err = LDAP_SUCCESS; 438 rs->sr_text = NULL; 439 if( num_ctrls ) rs->sr_ctrls = ctrls; 440 441 return_results: 442 if ( rs->sr_err == LDAP_SUCCESS && parent_is_glue && parent_is_leaf ) { 443 op->o_delete_glue_parent = 1; 444 } 445 446 if ( p != NULL ) { 447 mdb_entry_return( op, p ); 448 } 449 450 /* free entry */ 451 if( e != NULL ) { 452 mdb_entry_return( op, e ); 453 } 454 455 if( moi == &opinfo ) { 456 if( txn != NULL ) { 457 mdb_txn_abort( txn ); 458 } 459 if ( opinfo.moi_oe.oe_key ) { 460 LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.moi_oe, OpExtra, oe_next ); 461 } 462 } else { 463 moi->moi_ref--; 464 } 465 466 send_ldap_result( op, rs ); 467 slap_graduate_commit_csn( op ); 468 469 if( preread_ctrl != NULL && (*preread_ctrl) != NULL ) { 470 slap_sl_free( (*preread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx ); 471 slap_sl_free( *preread_ctrl, op->o_tmpmemctx ); 472 } 473 474 #if 0 475 if( rs->sr_err == LDAP_SUCCESS && mdb->bi_txn_cp_kbyte ) { 476 TXN_CHECKPOINT( mdb->bi_dbenv, 477 mdb->bi_txn_cp_kbyte, mdb->bi_txn_cp_min, 0 ); 478 } 479 #endif 480 return rs->sr_err; 481 } 482