]> git.sur5r.net Git - openldap/blob - servers/slapd/back-bdb/delete.c
234c5feb3fca92c54e079d8667a46b4934e19dd1
[openldap] / servers / slapd / back-bdb / delete.c
1 /* delete.c - bdb backend delete routine */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2000-2004 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
17 #include "portable.h"
18
19 #include <stdio.h>
20 #include <ac/string.h>
21
22 #include "back-bdb.h"
23 #include "external.h"
24
25 int
26 bdb_delete( Operation *op, SlapReply *rs )
27 {
28         struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
29         Entry   *matched = NULL;
30         struct berval   pdn = {0, NULL};
31         Entry   *e = NULL;
32         Entry   *p = NULL;
33         EntryInfo       *ei = NULL, *eip = NULL;
34         int             manageDSAit = get_manageDSAit( op );
35         AttributeDescription *children = slap_schema.si_ad_children;
36         AttributeDescription *entry = slap_schema.si_ad_entry;
37         DB_TXN          *ltid = NULL, *lt2;
38         struct bdb_op_info opinfo;
39         ID      eid;
40
41         u_int32_t       locker = 0;
42         DB_LOCK         lock, plock;
43
44         int             num_retries = 0;
45
46         Operation* ps_list;
47         int     rc;
48         EntryInfo   *suffix_ei;
49         Entry       *ctxcsn_e;
50         int         ctxcsn_added = 0;
51
52         LDAPControl **preread_ctrl = NULL;
53         LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
54         int num_ctrls = 0;
55
56         int     parent_is_glue = 0;
57         int parent_is_leaf = 0;
58
59         ctrls[num_ctrls] = 0;
60
61         Debug( LDAP_DEBUG_ARGS, "==> bdb_delete: %s\n",
62                 op->o_req_dn.bv_val, 0, 0 );
63
64         if( 0 ) {
65 retry:  /* transaction retry */
66                 if( e != NULL ) {
67                         bdb_unlocked_cache_return_entry_w(&bdb->bi_cache, e);
68                         e = NULL;
69                 }
70                 if( p != NULL ) {
71                         bdb_unlocked_cache_return_entry_r(&bdb->bi_cache, p);
72                         p = NULL;
73                 }
74                 Debug( LDAP_DEBUG_TRACE, "==> bdb_delete: retrying...\n",
75                         0, 0, 0 );
76                 rs->sr_err = TXN_ABORT( ltid );
77                 ltid = NULL;
78                 op->o_private = NULL;
79                 op->o_do_not_cache = opinfo.boi_acl_cache;
80                 if( rs->sr_err != 0 ) {
81                         rs->sr_err = LDAP_OTHER;
82                         rs->sr_text = "internal error";
83                         goto return_results;
84                 }
85                 parent_is_glue = 0;
86                 parent_is_leaf = 0;
87                 ldap_pvt_thread_yield();
88                 bdb_trans_backoff( ++num_retries );
89         }
90
91         /* begin transaction */
92         rs->sr_err = TXN_BEGIN( bdb->bi_dbenv, NULL, &ltid, 
93                 bdb->bi_db_opflags );
94         rs->sr_text = NULL;
95         if( rs->sr_err != 0 ) {
96                 Debug( LDAP_DEBUG_TRACE,
97                         "bdb_delete: txn_begin failed: %s (%d)\n",
98                         db_strerror(rs->sr_err), rs->sr_err, 0 );
99                 rs->sr_err = LDAP_OTHER;
100                 rs->sr_text = "internal error";
101                 goto return_results;
102         }
103
104         locker = TXN_ID ( ltid );
105
106         opinfo.boi_bdb = op->o_bd;
107         opinfo.boi_txn = ltid;
108         opinfo.boi_locker = locker;
109         opinfo.boi_err = 0;
110         opinfo.boi_acl_cache = op->o_do_not_cache;
111         op->o_private = &opinfo;
112
113         if ( !be_issuffix( op->o_bd, &op->o_req_ndn ) ) {
114                 dnParent( &op->o_req_ndn, &pdn );
115         }
116
117         /* get entry */
118         rs->sr_err = bdb_dn2entry( op, ltid, &op->o_req_ndn, &ei, 1,
119                 locker, &lock );
120
121         switch( rs->sr_err ) {
122         case 0:
123         case DB_NOTFOUND:
124                 break;
125         case DB_LOCK_DEADLOCK:
126         case DB_LOCK_NOTGRANTED:
127                 goto retry;
128         case LDAP_BUSY:
129                 rs->sr_text = "ldap server busy";
130                 goto return_results;
131         default:
132                 rs->sr_err = LDAP_OTHER;
133                 rs->sr_text = "internal error";
134                 goto return_results;
135         }
136
137         if ( rs->sr_err == 0 ) {
138                 e = ei->bei_e;
139                 eip = ei->bei_parent;
140         } else {
141                 matched = ei->bei_e;
142         }
143
144         /* FIXME : dn2entry() should return non-glue entry */
145         if ( e == NULL || ( !manageDSAit && is_entry_glue( e ))) {
146                 BerVarray deref = NULL;
147
148                 Debug( LDAP_DEBUG_ARGS,
149                         "<=- bdb_delete: no such object %s\n",
150                         op->o_req_dn.bv_val, 0, 0);
151
152                 if ( matched != NULL ) {
153                         rs->sr_matched = ch_strdup( matched->e_dn );
154                         rs->sr_ref = is_entry_referral( matched )
155                                 ? get_entry_referrals( op, matched )
156                                 : NULL;
157                         bdb_unlocked_cache_return_entry_r(&bdb->bi_cache, matched);
158                         matched = NULL;
159
160                 } else {
161                         if ( !LDAP_STAILQ_EMPTY( &op->o_bd->be_syncinfo )) {
162                                 syncinfo_t *si;
163                                 LDAP_STAILQ_FOREACH( si, &op->o_bd->be_syncinfo, si_next ) {
164                                         struct berval tmpbv;
165                                         ber_dupbv( &tmpbv, &si->si_provideruri_bv[0] );
166                                         ber_bvarray_add( &deref, &tmpbv );
167                                 }
168                         } else {
169                                 deref = default_referral;
170                         }
171                         rs->sr_ref = referral_rewrite( deref, NULL, &op->o_req_dn,
172                                         LDAP_SCOPE_DEFAULT );
173                 }
174
175                 rs->sr_err = LDAP_REFERRAL;
176                 send_ldap_result( op, rs );
177
178                 if ( rs->sr_ref != default_referral ) {
179                         ber_bvarray_free( rs->sr_ref );
180                 }
181                 if ( deref != default_referral ) {
182                         ber_bvarray_free( deref );
183                 }
184                 free( (char *)rs->sr_matched );
185                 rs->sr_ref = NULL;
186                 rs->sr_matched = NULL;
187
188                 rs->sr_err = -1;
189                 goto done;
190         }
191
192         rc = bdb_cache_find_id( op, ltid, eip->bei_id, &eip, 0, locker, &plock );
193         switch( rc ) {
194         case DB_LOCK_DEADLOCK:
195         case DB_LOCK_NOTGRANTED:
196                 goto retry;
197         case 0:
198         case DB_NOTFOUND:
199                 break;
200         default:
201                 rs->sr_err = LDAP_OTHER;
202                 rs->sr_text = "internal error";
203                 goto return_results;
204         }
205         if ( eip ) p = eip->bei_e;
206
207         if ( pdn.bv_len != 0 ) {
208                 if( p == NULL || !bvmatch( &pdn, &p->e_nname )) {
209                         Debug( LDAP_DEBUG_TRACE,
210                                 "<=- bdb_delete: parent does not exist\n",
211                                 0, 0, 0);
212                         rs->sr_err = LDAP_OTHER;
213                         rs->sr_text = "could not locate parent of entry";
214                         goto return_results;
215                 }
216
217                 /* check parent for "children" acl */
218                 rs->sr_err = access_allowed( op, p,
219                         children, NULL, ACL_WRITE, NULL );
220
221                 if ( !rs->sr_err  ) {
222                         switch( opinfo.boi_err ) {
223                         case DB_LOCK_DEADLOCK:
224                         case DB_LOCK_NOTGRANTED:
225                                 goto retry;
226                         }
227
228                         Debug( LDAP_DEBUG_TRACE,
229                                 "<=- bdb_delete: no write access to parent\n",
230                                 0, 0, 0 );
231                         rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
232                         rs->sr_text = "no write access to parent";
233                         goto return_results;
234                 }
235
236         } else {
237                 /* no parent, must be root to delete */
238                 if( ! be_isroot( op ) ) {
239                         if ( be_issuffix( op->o_bd, (struct berval *)&slap_empty_bv )
240                                 || be_shadow_update( op ) ) {
241                                 p = (Entry *)&slap_entry_root;
242
243                                 /* check parent for "children" acl */
244                                 rs->sr_err = access_allowed( op, p,
245                                         children, NULL, ACL_WRITE, NULL );
246
247                                 p = NULL;
248
249                                 if ( !rs->sr_err  ) {
250                                         switch( opinfo.boi_err ) {
251                                         case DB_LOCK_DEADLOCK:
252                                         case DB_LOCK_NOTGRANTED:
253                                                 goto retry;
254                                         }
255
256                                         Debug( LDAP_DEBUG_TRACE,
257                                                 "<=- bdb_delete: no access "
258                                                 "to parent\n", 0, 0, 0 );
259                                         rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
260                                         rs->sr_text = "no write access to parent";
261                                         goto return_results;
262                                 }
263
264                         } else {
265                                 Debug( LDAP_DEBUG_TRACE,
266                                         "<=- bdb_delete: no parent "
267                                         "and not root\n", 0, 0, 0);
268                                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
269                                 goto return_results;
270                         }
271                 }
272         }
273
274         if ( get_assert( op ) &&
275                 ( test_filter( op, e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
276         {
277                 rs->sr_err = LDAP_ASSERTION_FAILED;
278                 goto return_results;
279         }
280
281         rs->sr_err = access_allowed( op, e,
282                 entry, NULL, ACL_WRITE, NULL );
283
284         if ( !rs->sr_err  ) {
285                 switch( opinfo.boi_err ) {
286                 case DB_LOCK_DEADLOCK:
287                 case DB_LOCK_NOTGRANTED:
288                         goto retry;
289                 }
290
291                 Debug( LDAP_DEBUG_TRACE,
292                         "<=- bdb_delete: no write access to entry\n",
293                         0, 0, 0 );
294                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
295                 rs->sr_text = "no write access to entry";
296                 goto return_results;
297         }
298
299         if ( !manageDSAit && is_entry_referral( e ) ) {
300                 /* entry is a referral, don't allow delete */
301                 rs->sr_ref = get_entry_referrals( op, e );
302
303                 Debug( LDAP_DEBUG_TRACE,
304                         "bdb_delete: entry is referral\n", 0, 0, 0 );
305
306                 rs->sr_err = LDAP_REFERRAL;
307                 rs->sr_matched = e->e_name.bv_val;
308                 send_ldap_result( op, rs );
309
310                 ber_bvarray_free( rs->sr_ref );
311                 rs->sr_ref = NULL;
312                 rs->sr_matched = NULL;
313
314                 rs->sr_err = 1;
315                 goto done;
316         }
317
318         /* pre-read */
319         if( op->o_preread ) {
320                 if( preread_ctrl == NULL ) {
321                         preread_ctrl = &ctrls[num_ctrls++];
322                         ctrls[num_ctrls] = NULL;
323                 }
324                 if( slap_read_controls( op, rs, e,
325                         &slap_pre_read_bv, preread_ctrl ) )
326                 {
327                         Debug( LDAP_DEBUG_TRACE,
328                                 "<=- bdb_delete: pre-read failed!\n", 0, 0, 0 );
329                         goto return_results;
330                 }
331         }
332
333         /* nested transaction */
334         rs->sr_err = TXN_BEGIN( bdb->bi_dbenv, ltid, &lt2, 
335                 bdb->bi_db_opflags );
336         rs->sr_text = NULL;
337         if( rs->sr_err != 0 ) {
338                 Debug( LDAP_DEBUG_TRACE,
339                         "bdb_delete: txn_begin(2) failed: %s (%d)\n",
340                         db_strerror(rs->sr_err), rs->sr_err, 0 );
341                 rs->sr_err = LDAP_OTHER;
342                 rs->sr_text = "internal error";
343                 goto return_results;
344         }
345
346         /* Can't do it if we have kids */
347         rs->sr_err = bdb_cache_children( op, lt2, e );
348         if( rs->sr_err != DB_NOTFOUND ) {
349                 switch( rs->sr_err ) {
350                 case DB_LOCK_DEADLOCK:
351                 case DB_LOCK_NOTGRANTED:
352                         goto retry;
353                 case 0:
354                         Debug(LDAP_DEBUG_ARGS,
355                                 "<=- bdb_delete: non-leaf %s\n",
356                                 op->o_req_dn.bv_val, 0, 0);
357                         rs->sr_err = LDAP_NOT_ALLOWED_ON_NONLEAF;
358                         rs->sr_text = "subtree delete not supported";
359                         break;
360                 default:
361                         Debug(LDAP_DEBUG_ARGS,
362                                 "<=- bdb_delete: has_children failed: %s (%d)\n",
363                                 db_strerror(rs->sr_err), rs->sr_err, 0 );
364                         rs->sr_err = LDAP_OTHER;
365                         rs->sr_text = "internal error";
366                 }
367                 goto return_results;
368         }
369
370         ldap_pvt_thread_rdwr_wlock( &bdb->bi_pslist_rwlock );
371         LDAP_LIST_FOREACH( ps_list, &bdb->bi_psearch_list, o_ps_link ) {
372                 rc = bdb_psearch( op, rs, ps_list, e, LDAP_PSEARCH_BY_PREDELETE );
373                 if ( rc == LDAP_BUSY && op->o_ps_send_wait ) {
374                         ldap_pvt_thread_rdwr_wunlock( &bdb->bi_pslist_rwlock );
375                         goto retry;
376                 } else if ( rc ) {
377                         Debug( LDAP_DEBUG_TRACE,
378                                 "bdb_delete: persistent search failed (%d,%d)\n",
379                                 rc, rs->sr_err, 0 );
380                 }
381         }
382         ldap_pvt_thread_rdwr_wunlock( &bdb->bi_pslist_rwlock );
383
384         /* delete from dn2id */
385         rs->sr_err = bdb_dn2id_delete( op, lt2, eip, e );
386         if ( rs->sr_err != 0 ) {
387                 Debug(LDAP_DEBUG_TRACE,
388                         "<=- bdb_delete: dn2id failed: %s (%d)\n",
389                         db_strerror(rs->sr_err), rs->sr_err, 0 );
390                 switch( rs->sr_err ) {
391                 case DB_LOCK_DEADLOCK:
392                 case DB_LOCK_NOTGRANTED:
393                         goto retry;
394                 }
395                 rs->sr_text = "DN index delete failed";
396                 rs->sr_err = LDAP_OTHER;
397                 goto return_results;
398         }
399
400         /* delete from id2entry */
401         rs->sr_err = bdb_id2entry_delete( op->o_bd, lt2, e );
402         if ( rs->sr_err != 0 ) {
403                 Debug(LDAP_DEBUG_TRACE,
404                         "<=- bdb_delete: id2entry failed: %s (%d)\n",
405                         db_strerror(rs->sr_err), rs->sr_err, 0 );
406                 switch( rs->sr_err ) {
407                 case DB_LOCK_DEADLOCK:
408                 case DB_LOCK_NOTGRANTED:
409                         goto retry;
410                 }
411                 rs->sr_text = "entry delete failed";
412                 rs->sr_err = LDAP_OTHER;
413                 goto return_results;
414         }
415
416         /* delete indices for old attributes */
417         rs->sr_err = bdb_index_entry_del( op, lt2, e );
418         if ( rs->sr_err != LDAP_SUCCESS ) {
419                 Debug( LDAP_DEBUG_TRACE,
420                         "<=- bdb_delete: index failed: %s (%d)\n", 
421                         db_strerror(rs->sr_err), rs->sr_err, 0 );
422                 switch( rs->sr_err ) {
423                 case DB_LOCK_DEADLOCK:
424                 case DB_LOCK_NOTGRANTED:
425                         goto retry;
426                 }
427                 rs->sr_text = "entry index delete failed";
428                 rs->sr_err = LDAP_OTHER;
429                 goto return_results;
430         }
431
432         if ( pdn.bv_len != 0 ) {
433                 parent_is_glue = is_entry_glue(p);
434                 rs->sr_err = bdb_cache_children( op, lt2, p );
435                 if ( rs->sr_err != DB_NOTFOUND ) {
436                         switch( rs->sr_err ) {
437                         case DB_LOCK_DEADLOCK:
438                         case DB_LOCK_NOTGRANTED:
439                                 goto retry;
440                         case 0:
441                                 break;
442                         default:
443                                 Debug(LDAP_DEBUG_ARGS,
444                                         "<=- bdb_delete: has_children failed: %s (%d)\n",
445                                         db_strerror(rs->sr_err), rs->sr_err, 0 );
446                                 rs->sr_err = LDAP_OTHER;
447                                 rs->sr_text = "internal error";
448                                 goto return_results;
449                         }
450                         parent_is_leaf = 1;
451                 }
452                 bdb_unlocked_cache_return_entry_r(&bdb->bi_cache, p);
453                 p = NULL;
454         }
455
456         if ( TXN_COMMIT( lt2, 0 ) != 0 ) {
457                 rs->sr_err = LDAP_OTHER;
458                 rs->sr_text = "txn_commit(2) failed";
459                 goto return_results;
460         }
461
462         eid = e->e_id;
463
464 #if 0   /* Do we want to reclaim deleted IDs? */
465         ldap_pvt_thread_mutex_lock( &bdb->bi_lastid_mutex );
466         if ( e->e_id == bdb->bi_lastid ) {
467                 bdb_last_id( op->o_bd, ltid );
468         }
469         ldap_pvt_thread_mutex_unlock( &bdb->bi_lastid_mutex );
470 #endif
471
472         if ( LDAP_STAILQ_EMPTY( &op->o_bd->be_syncinfo )) {
473                 rc = bdb_csn_commit( op, rs, ltid, ei, &suffix_ei,
474                         &ctxcsn_e, &ctxcsn_added, locker );
475                 switch ( rc ) {
476                 case BDB_CSN_ABORT :
477                         goto return_results;
478                 case BDB_CSN_RETRY :
479                         goto retry;
480                 }
481         }
482
483         if( op->o_noop ) {
484                 if ( ( rs->sr_err = TXN_ABORT( ltid ) ) != 0 ) {
485                         rs->sr_text = "txn_abort (no-op) failed";
486                 } else {
487                         rs->sr_err = LDAP_NO_OPERATION;
488                         goto return_results;
489                 }
490         } else {
491                 rc = bdb_cache_delete( &bdb->bi_cache, e, bdb->bi_dbenv,
492                         locker, &lock );
493                 switch( rc ) {
494                 case DB_LOCK_DEADLOCK:
495                 case DB_LOCK_NOTGRANTED:
496                         goto retry;
497                 }
498
499                 if ( LDAP_STAILQ_EMPTY( &op->o_bd->be_syncinfo )) {
500                         if ( ctxcsn_added ) {
501                                 bdb_cache_add( bdb, suffix_ei,
502                                         ctxcsn_e, (struct berval *)&slap_ldapsync_cn_bv, locker );
503                         }
504                 }
505
506                 if ( rs->sr_err == LDAP_SUCCESS && !op->o_no_psearch ) {
507                         Attribute *a;
508                         a = attr_find( e->e_attrs, slap_schema.si_ad_entryCSN );
509                         if ( a ) {
510                                 if( (void *) e->e_attrs != (void *) (e+1)) {
511                                         attr_delete( &e->e_attrs, slap_schema.si_ad_entryCSN );
512                                         attr_merge_normalize_one( e, slap_schema.si_ad_entryCSN,
513                                         &op->o_sync_csn, NULL );
514                                 } else {
515                                         a->a_vals[0] = op->o_sync_csn;
516                                 }
517                         } else {
518                                 /* Hm, the entryCSN ought to exist. ??? */
519                         }
520                         ldap_pvt_thread_rdwr_wlock( &bdb->bi_pslist_rwlock );
521                         LDAP_LIST_FOREACH( ps_list, &bdb->bi_psearch_list, o_ps_link ) {
522                                 rc = bdb_psearch( op, rs, ps_list, e, LDAP_PSEARCH_BY_DELETE );
523                                 if ( rc ) {
524                                         Debug( LDAP_DEBUG_TRACE,
525                                                 "bdb_delete: persistent search failed (%d,%d)\n",
526                                                 rc, rs->sr_err, 0 );
527                                 }
528                         }
529                         ldap_pvt_thread_rdwr_wunlock( &bdb->bi_pslist_rwlock );
530                 }
531
532                 rs->sr_err = TXN_COMMIT( ltid, 0 );
533         }
534         ltid = NULL;
535         op->o_private = NULL;
536
537         if( rs->sr_err != 0 ) {
538                 Debug( LDAP_DEBUG_TRACE,
539                         "bdb_delete: txn_%s failed: %s (%d)\n",
540                         op->o_noop ? "abort (no-op)" : "commit",
541                         db_strerror(rs->sr_err), rs->sr_err );
542                 rs->sr_err = LDAP_OTHER;
543                 rs->sr_text = "commit failed";
544
545                 goto return_results;
546         }
547
548         Debug( LDAP_DEBUG_TRACE,
549                 "bdb_delete: deleted%s id=%08lx dn=\"%s\"\n",
550                 op->o_noop ? " (no-op)" : "",
551                 eid, op->o_req_dn.bv_val );
552         rs->sr_err = LDAP_SUCCESS;
553         rs->sr_text = NULL;
554         if( num_ctrls ) rs->sr_ctrls = ctrls;
555
556 return_results:
557         send_ldap_result( op, rs );
558
559         if( rs->sr_err == LDAP_SUCCESS && bdb->bi_txn_cp ) {
560                 ldap_pvt_thread_yield();
561                 TXN_CHECKPOINT( bdb->bi_dbenv,
562                         bdb->bi_txn_cp_kbyte, bdb->bi_txn_cp_min, 0 );
563         }
564
565         if ( rs->sr_err == LDAP_SUCCESS && parent_is_glue && parent_is_leaf ) {
566                 op->o_delete_glue_parent = 1;
567         }
568
569 done:
570         if ( p )
571                 bdb_unlocked_cache_return_entry_r(&bdb->bi_cache, p);
572
573         /* free entry */
574         if( e != NULL ) {
575                 if ( rs->sr_err == LDAP_SUCCESS ) {
576                         /* Free the EntryInfo and the Entry */
577                         bdb_cache_delete_cleanup( &bdb->bi_cache, BEI(e) );
578                 } else {
579                         bdb_unlocked_cache_return_entry_w(&bdb->bi_cache, e);
580                 }
581         }
582
583         if( ltid != NULL ) {
584                 TXN_ABORT( ltid );
585                 op->o_private = NULL;
586         }
587
588         if( preread_ctrl != NULL ) {
589                 slap_sl_free( (*preread_ctrl)->ldctl_value.bv_val, &op->o_tmpmemctx );
590                 slap_sl_free( *preread_ctrl, &op->o_tmpmemctx );
591         }
592         return rs->sr_err;
593 }