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