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