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