1/* delete.cpp - ndb backend delete routine */ 2/* $OpenLDAP$ */ 3/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 4 * 5 * Copyright 2008-2021 The OpenLDAP Foundation. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted only as authorized by the OpenLDAP 10 * Public License. 11 * 12 * A copy of this license is available in the file LICENSE in the 13 * top-level directory of the distribution or, alternatively, at 14 * <http://www.OpenLDAP.org/license.html>. 15 */ 16/* ACKNOWLEDGEMENTS: 17 * This work was initially developed by Howard Chu for inclusion 18 * in OpenLDAP Software. This work was sponsored by MySQL. 19 */ 20 21#include "portable.h" 22 23#include <stdio.h> 24#include <ac/string.h> 25 26#include "lutil.h" 27#include "back-ndb.h" 28 29static struct berval glue_bv = BER_BVC("glue"); 30 31int 32ndb_back_delete( Operation *op, SlapReply *rs ) 33{ 34 struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private; 35 Entry e = {0}; 36 Entry p = {0}; 37 int manageDSAit = get_manageDSAit( op ); 38 AttributeDescription *children = slap_schema.si_ad_children; 39 AttributeDescription *entry = slap_schema.si_ad_entry; 40 41 NdbArgs NA; 42 NdbRdns rdns; 43 struct berval matched; 44 45 int num_retries = 0; 46 47 int rc; 48 49 LDAPControl **preread_ctrl = NULL; 50 LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS]; 51 int num_ctrls = 0; 52 53 Debug( LDAP_DEBUG_ARGS, "==> " LDAP_XSTRING(ndb_back_delete) ": %s\n", 54 op->o_req_dn.bv_val, 0, 0 ); 55 56 ctrls[num_ctrls] = 0; 57 58 /* allocate CSN */ 59 if ( BER_BVISNULL( &op->o_csn ) ) { 60 struct berval csn; 61 char csnbuf[LDAP_PVT_CSNSTR_BUFSIZE]; 62 63 csn.bv_val = csnbuf; 64 csn.bv_len = sizeof(csnbuf); 65 slap_get_csn( op, &csn, 1 ); 66 } 67 68 if ( !be_issuffix( op->o_bd, &op->o_req_ndn ) ) { 69 dnParent( &op->o_req_dn, &p.e_name ); 70 dnParent( &op->o_req_ndn, &p.e_nname ); 71 } 72 73 /* Get our NDB handle */ 74 rs->sr_err = ndb_thread_handle( op, &NA.ndb ); 75 rdns.nr_num = 0; 76 NA.rdns = &rdns; 77 NA.ocs = NULL; 78 NA.e = &e; 79 e.e_name = op->o_req_dn; 80 e.e_nname = op->o_req_ndn; 81 82 if( 0 ) { 83retry: /* transaction retry */ 84 NA.txn->close(); 85 NA.txn = NULL; 86 Debug( LDAP_DEBUG_TRACE, 87 "==> " LDAP_XSTRING(ndb_back_delete) ": retrying...\n", 88 0, 0, 0 ); 89 if ( op->o_abandon ) { 90 rs->sr_err = SLAPD_ABANDON; 91 goto return_results; 92 } 93 if ( NA.ocs ) { 94 ber_bvarray_free( NA.ocs ); 95 NA.ocs = NULL; 96 } 97 ndb_trans_backoff( ++num_retries ); 98 } 99 100 /* begin transaction */ 101 NA.txn = NA.ndb->startTransaction(); 102 rs->sr_text = NULL; 103 if( !NA.txn ) { 104 Debug( LDAP_DEBUG_TRACE, 105 LDAP_XSTRING(ndb_back_delete) ": startTransaction failed: %s (%d)\n", 106 NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 ); 107 rs->sr_err = LDAP_OTHER; 108 rs->sr_text = "internal error"; 109 goto return_results; 110 } 111 112 /* get entry */ 113 rs->sr_err = ndb_entry_get_info( op, &NA, 1, &matched ); 114 switch( rs->sr_err ) { 115 case 0: 116 case LDAP_NO_SUCH_OBJECT: 117 break; 118#if 0 119 case DB_LOCK_DEADLOCK: 120 case DB_LOCK_NOTGRANTED: 121 goto retry; 122#endif 123 case LDAP_BUSY: 124 rs->sr_text = "ldap server busy"; 125 goto return_results; 126 default: 127 rs->sr_err = LDAP_OTHER; 128 rs->sr_text = "internal error"; 129 goto return_results; 130 } 131 132 if ( rs->sr_err == LDAP_NO_SUCH_OBJECT || 133 ( !manageDSAit && bvmatch( NA.ocs, &glue_bv ))) { 134 Debug( LDAP_DEBUG_ARGS, 135 "<=- " LDAP_XSTRING(ndb_back_delete) ": no such object %s\n", 136 op->o_req_dn.bv_val, 0, 0); 137 138 if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) { 139 rs->sr_matched = matched.bv_val; 140 if ( NA.ocs ) 141 ndb_check_referral( op, rs, &NA ); 142 } else { 143 rs->sr_matched = p.e_name.bv_val; 144 rs->sr_err = LDAP_NO_SUCH_OBJECT; 145 } 146 goto return_results; 147 } 148 149 /* check parent for "children" acl */ 150 rs->sr_err = access_allowed( op, &p, 151 children, NULL, ACL_WDEL, NULL ); 152 153 if ( !rs->sr_err ) { 154 Debug( LDAP_DEBUG_TRACE, 155 "<=- " LDAP_XSTRING(ndb_back_delete) ": no write " 156 "access to parent\n", 0, 0, 0 ); 157 rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 158 rs->sr_text = "no write access to parent"; 159 goto return_results; 160 } 161 162 rs->sr_err = ndb_entry_get_data( op, &NA, 1 ); 163 164 rs->sr_err = access_allowed( op, &e, 165 entry, NULL, ACL_WDEL, NULL ); 166 167 if ( !rs->sr_err ) { 168 Debug( LDAP_DEBUG_TRACE, 169 "<=- " LDAP_XSTRING(ndb_back_delete) ": no write access " 170 "to entry\n", 0, 0, 0 ); 171 rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 172 rs->sr_text = "no write access to entry"; 173 goto return_results; 174 } 175 176 if ( !manageDSAit && is_entry_referral( &e ) ) { 177 /* entry is a referral, don't allow delete */ 178 rs->sr_ref = get_entry_referrals( op, &e ); 179 180 Debug( LDAP_DEBUG_TRACE, 181 LDAP_XSTRING(ndb_back_delete) ": entry is referral\n", 182 0, 0, 0 ); 183 184 rs->sr_err = LDAP_REFERRAL; 185 rs->sr_matched = e.e_name.bv_val; 186 rs->sr_flags = REP_REF_MUSTBEFREED; 187 goto return_results; 188 } 189 190 if ( get_assert( op ) && 191 ( test_filter( op, &e, (Filter *)get_assertion( op )) != LDAP_COMPARE_TRUE )) 192 { 193 rs->sr_err = LDAP_ASSERTION_FAILED; 194 goto return_results; 195 } 196 197 /* pre-read */ 198 if( op->o_preread ) { 199 if( preread_ctrl == NULL ) { 200 preread_ctrl = &ctrls[num_ctrls++]; 201 ctrls[num_ctrls] = NULL; 202 } 203 if( slap_read_controls( op, rs, &e, 204 &slap_pre_read_bv, preread_ctrl ) ) 205 { 206 Debug( LDAP_DEBUG_TRACE, 207 "<=- " LDAP_XSTRING(ndb_back_delete) ": pre-read " 208 "failed!\n", 0, 0, 0 ); 209 if ( op->o_preread & SLAP_CONTROL_CRITICAL ) { 210 /* FIXME: is it correct to abort 211 * operation if control fails? */ 212 goto return_results; 213 } 214 } 215 } 216 217 /* Can't do it if we have kids */ 218 rs->sr_err = ndb_has_children( &NA, &rc ); 219 if ( rs->sr_err ) { 220 Debug(LDAP_DEBUG_ARGS, 221 "<=- " LDAP_XSTRING(ndb_back_delete) 222 ": has_children failed: %s (%d)\n", 223 NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 ); 224 rs->sr_err = LDAP_OTHER; 225 rs->sr_text = "internal error"; 226 goto return_results; 227 } 228 if ( rc == LDAP_COMPARE_TRUE ) { 229 Debug(LDAP_DEBUG_ARGS, 230 "<=- " LDAP_XSTRING(ndb_back_delete) 231 ": non-leaf %s\n", 232 op->o_req_dn.bv_val, 0, 0); 233 rs->sr_err = LDAP_NOT_ALLOWED_ON_NONLEAF; 234 rs->sr_text = "subordinate objects must be deleted first"; 235 goto return_results; 236 } 237 238 /* delete info */ 239 rs->sr_err = ndb_entry_del_info( op->o_bd, &NA ); 240 if ( rs->sr_err != 0 ) { 241 Debug(LDAP_DEBUG_TRACE, 242 "<=- " LDAP_XSTRING(ndb_back_delete) ": del_info failed: %s (%d)\n", 243 NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 ); 244 rs->sr_text = "DN index delete failed"; 245 rs->sr_err = LDAP_OTHER; 246 goto return_results; 247 } 248 249 /* delete data */ 250 rs->sr_err = ndb_entry_del_data( op->o_bd, &NA ); 251 if ( rs->sr_err != 0 ) { 252 Debug( LDAP_DEBUG_TRACE, 253 "<=- " LDAP_XSTRING(ndb_back_delete) ": del_data failed: %s (%d)\n", 254 NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 ); 255 rs->sr_text = "entry delete failed"; 256 rs->sr_err = LDAP_OTHER; 257 goto return_results; 258 } 259 260 if( op->o_noop ) { 261 if (( rs->sr_err=NA.txn->execute( NdbTransaction::Rollback, 262 NdbOperation::AbortOnError, 1 )) != 0 ) { 263 rs->sr_text = "txn (no-op) failed"; 264 } else { 265 rs->sr_err = LDAP_X_NO_OPERATION; 266 } 267 } else { 268 if (( rs->sr_err=NA.txn->execute( NdbTransaction::Commit, 269 NdbOperation::AbortOnError, 1 )) != 0 ) { 270 rs->sr_text = "txn_commit failed"; 271 } else { 272 rs->sr_err = LDAP_SUCCESS; 273 } 274 } 275 276 if( rs->sr_err != LDAP_SUCCESS && rs->sr_err != LDAP_X_NO_OPERATION ) { 277 Debug( LDAP_DEBUG_TRACE, 278 LDAP_XSTRING(ndb_back_delete) ": txn_%s failed: %s (%d)\n", 279 op->o_noop ? "abort (no-op)" : "commit", 280 NA.txn->getNdbError().message, NA.txn->getNdbError().code ); 281 rs->sr_err = LDAP_OTHER; 282 rs->sr_text = "commit failed"; 283 284 goto return_results; 285 } 286 NA.txn->close(); 287 NA.txn = NULL; 288 289 Debug( LDAP_DEBUG_TRACE, 290 LDAP_XSTRING(ndb_back_delete) ": deleted%s id=%08lx dn=\"%s\"\n", 291 op->o_noop ? " (no-op)" : "", 292 e.e_id, op->o_req_dn.bv_val ); 293 rs->sr_err = LDAP_SUCCESS; 294 rs->sr_text = NULL; 295 if( num_ctrls ) rs->sr_ctrls = ctrls; 296 297return_results: 298 if ( NA.ocs ) { 299 ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx ); 300 NA.ocs = NULL; 301 } 302 303 /* free entry */ 304 if( e.e_attrs != NULL ) { 305 attrs_free( e.e_attrs ); 306 e.e_attrs = NULL; 307 } 308 309 if( NA.txn != NULL ) { 310 NA.txn->execute( Rollback ); 311 NA.txn->close(); 312 } 313 314 send_ldap_result( op, rs ); 315 slap_graduate_commit_csn( op ); 316 317 if( preread_ctrl != NULL && (*preread_ctrl) != NULL ) { 318 slap_sl_free( (*preread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx ); 319 slap_sl_free( *preread_ctrl, op->o_tmpmemctx ); 320 } 321 return rs->sr_err; 322} 323