]> git.sur5r.net Git - openldap/blob - servers/slapd/back-bdb/add.c
ITS#2368 - fix deleting key from range IDL
[openldap] / servers / slapd / back-bdb / add.c
1 /* add.c - ldap BerkeleyDB back-end add routine */
2 /* $OpenLDAP$ */
3 /*
4  * Copyright 1998-2003 The OpenLDAP Foundation, All Rights Reserved.
5  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
6  */
7
8 #include "portable.h"
9
10 #include <stdio.h>
11 #include <ac/string.h>
12
13 #include "back-bdb.h"
14 #include "external.h"
15
16 int
17 bdb_add(
18         BackendDB       *be,
19         Connection      *conn,
20         Operation       *op,
21         Entry   *e )
22 {
23         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
24         struct berval   pdn;
25         Entry           *p = NULL;
26         int             rc; 
27         const char      *text;
28         char textbuf[SLAP_TEXT_BUFLEN];
29         size_t textlen = sizeof textbuf;
30         AttributeDescription *children = slap_schema.si_ad_children;
31         AttributeDescription *entry = slap_schema.si_ad_entry;
32         DB_TXN          *ltid = NULL;
33         struct bdb_op_info opinfo;
34 #ifdef BDB_SUBENTRIES
35         int subentry;
36 #endif
37         u_int32_t       locker = 0;
38         DB_LOCK         lock;
39 #if 0
40         u_int32_t       lockid;
41         DB_LOCK         lock;
42 #endif
43         int             noop = 0;
44
45 #if defined(LDAP_CLIENT_UPDATE) || defined(LDAP_SYNC)
46         Operation* ps_list;
47 #endif
48
49 #ifdef NEW_LOGGING
50         LDAP_LOG ( OPERATION, ARGS, "==> bdb_add: %s\n", e->e_dn, 0, 0 );
51 #else
52         Debug(LDAP_DEBUG_ARGS, "==> bdb_add: %s\n", e->e_dn, 0, 0);
53 #endif
54
55         /* check entry's schema */
56         rc = entry_schema_check( be, e, NULL, &text, textbuf, textlen );
57         if ( rc != LDAP_SUCCESS ) {
58 #ifdef NEW_LOGGING
59         LDAP_LOG ( OPERATION, ERR, 
60                 "bdb_add: entry failed schema check: %s (%d)\n", text, rc, 0 );
61 #else
62                 Debug( LDAP_DEBUG_TRACE,
63                         "bdb_add: entry failed schema check: %s (%d)\n",
64                         text, rc, 0 );
65 #endif
66                 goto return_results;
67         }
68
69 #ifdef BDB_SUBENTRIES
70         subentry = is_entry_subentry( e );
71 #endif
72
73         /*
74          * acquire an ID outside of the operation transaction
75          * to avoid serializing adds.
76          */
77         rc = bdb_next_id( be, NULL, &e->e_id );
78         if( rc != 0 ) {
79 #ifdef NEW_LOGGING
80                 LDAP_LOG ( OPERATION, ERR, 
81                         "bdb_add: next_id failed (%d)\n", rc, 0, 0 );
82 #else
83                 Debug( LDAP_DEBUG_TRACE,
84                         "bdb_add: next_id failed (%d)\n",
85                         rc, 0, 0 );
86 #endif
87                 rc = LDAP_OTHER;
88                 text = "internal error";
89                 goto return_results;
90         }
91
92         if( 0 ) {
93 retry:  /* transaction retry */
94                 if( p ) {
95                         /* free parent and reader lock */
96                         bdb_unlocked_cache_return_entry_r( &bdb->bi_cache, p );
97                         p = NULL;
98                 }
99                 rc = TXN_ABORT( ltid );
100                 ltid = NULL;
101                 op->o_private = NULL;
102                 op->o_do_not_cache = opinfo.boi_acl_cache;
103                 if( rc != 0 ) {
104                         rc = LDAP_OTHER;
105                         text = "internal error";
106                         goto return_results;
107                 }
108                 ldap_pvt_thread_yield();
109         }
110
111         /* begin transaction */
112         rc = TXN_BEGIN( bdb->bi_dbenv, NULL, &ltid, 
113                 bdb->bi_db_opflags );
114         text = NULL;
115         if( rc != 0 ) {
116 #ifdef NEW_LOGGING
117                 LDAP_LOG ( OPERATION, ERR, 
118                         "bdb_add: txn_begin failed: %s (%d)\n", db_strerror(rc), rc, 0 );
119 #else
120                 Debug( LDAP_DEBUG_TRACE,
121                         "bdb_add: txn_begin failed: %s (%d)\n",
122                         db_strerror(rc), rc, 0 );
123 #endif
124                 rc = LDAP_OTHER;
125                 text = "internal error";
126                 goto return_results;
127         }
128
129         locker = TXN_ID ( ltid );
130 #if 0
131         lockid = TXN_ID( ltid );
132 #endif
133
134         opinfo.boi_bdb = be;
135         opinfo.boi_txn = ltid;
136         opinfo.boi_locker = locker;
137         opinfo.boi_err = 0;
138         opinfo.boi_acl_cache = op->o_do_not_cache;
139         op->o_private = &opinfo;
140         
141         /*
142          * Get the parent dn and see if the corresponding entry exists.
143          * If the parent does not exist, only allow the "root" user to
144          * add the entry.
145          */
146         if ( be_issuffix( be, &e->e_nname ) ) {
147                 pdn = slap_empty_bv;
148         } else {
149                 dnParent( &e->e_nname, &pdn );
150         }
151
152         if( pdn.bv_len != 0 ) {
153                 Entry *matched = NULL;
154
155 #if 0
156                 if ( ltid ) {
157                         DBT obj;
158                         obj.data = pdn.bv_val-1;
159                         obj.size = pdn.bv_len+1;
160                         rc = LOCK_GET( bdb->bi_dbenv, lockid, 0, &obj,
161                                 DB_LOCK_WRITE, &lock);
162                 }
163 #endif
164
165                 /* get parent */
166                 rc = bdb_dn2entry_r( be, ltid, &pdn, &p, &matched, 0, locker, &lock );
167
168                 switch( rc ) {
169                 case 0:
170                 case DB_NOTFOUND:
171                         break;
172                 case DB_LOCK_DEADLOCK:
173                 case DB_LOCK_NOTGRANTED:
174                         goto retry;
175                 case LDAP_BUSY:
176                         text = "ldap server busy";
177                         goto return_results;
178                 default:
179                         rc = LDAP_OTHER;
180                         text = "internal error";
181                         goto return_results;
182                 }
183
184                 if ( p == NULL ) {
185                         char *matched_dn = NULL;
186                         BerVarray refs;
187
188                         if ( matched != NULL ) {
189                                 matched_dn = ch_strdup( matched->e_dn );
190                                 refs = is_entry_referral( matched )
191                                         ? get_entry_referrals( be, conn, op, matched )
192                                         : NULL;
193                                 bdb_unlocked_cache_return_entry_r( &bdb->bi_cache, matched );
194                                 matched = NULL;
195
196                         } else {
197                                 refs = referral_rewrite( default_referral,
198                                         NULL, &e->e_name, LDAP_SCOPE_DEFAULT );
199                         }
200
201 #ifdef NEW_LOGGING
202                         LDAP_LOG ( OPERATION, DETAIL1, 
203                                 "bdb_add: parent does not exist\n", 0, 0, 0 );
204 #else
205                         Debug( LDAP_DEBUG_TRACE, "bdb_add: parent does not exist\n",
206                                 0, 0, 0 );
207 #endif
208
209                         send_ldap_result( conn, op, rc = LDAP_REFERRAL,
210                                 matched_dn, NULL, refs, NULL );
211
212                         ber_bvarray_free( refs );
213                         ch_free( matched_dn );
214
215                         goto done;
216                 }
217
218                 rc = access_allowed( be, conn, op, p,
219                         children, NULL, ACL_WRITE, NULL );
220
221                 if ( ! rc ) {
222                         switch( opinfo.boi_err ) {
223                         case DB_LOCK_DEADLOCK:
224                         case DB_LOCK_NOTGRANTED:
225                                 goto retry;
226                         }
227
228 #ifdef NEW_LOGGING
229                         LDAP_LOG ( OPERATION, DETAIL1, 
230                                 "bdb_add: no write access to parent\n", 0, 0, 0 );
231 #else
232                         Debug( LDAP_DEBUG_TRACE, "bdb_add: no write access to parent\n",
233                                 0, 0, 0 );
234 #endif
235                         rc = LDAP_INSUFFICIENT_ACCESS;
236                         text = "no write access to parent";
237                         goto return_results;;
238                 }
239
240 #ifdef BDB_SUBENTRIES
241                 if ( is_entry_subentry( p ) ) {
242                         /* parent is a subentry, don't allow add */
243 #ifdef NEW_LOGGING
244                         LDAP_LOG ( OPERATION, DETAIL1, 
245                                 "bdb_add: parent is subentry\n", 0, 0, 0 );
246 #else
247                         Debug( LDAP_DEBUG_TRACE, "bdb_add: parent is subentry\n",
248                                 0, 0, 0 );
249 #endif
250                         rc = LDAP_OBJECT_CLASS_VIOLATION;
251                         text = "parent is a subentry";
252                         goto return_results;;
253                 }
254 #endif
255 #ifdef BDB_ALIASES
256                 if ( is_entry_alias( p ) ) {
257                         /* parent is an alias, don't allow add */
258 #ifdef NEW_LOGGING
259                         LDAP_LOG ( OPERATION, DETAIL1, 
260                                 "bdb_add: parent is alias\n", 0, 0, 0 );
261 #else
262                         Debug( LDAP_DEBUG_TRACE, "bdb_add: parent is alias\n",
263                                 0, 0, 0 );
264 #endif
265                         rc = LDAP_ALIAS_PROBLEM;
266                         text = "parent is an alias";
267                         goto return_results;;
268                 }
269 #endif
270
271                 if ( is_entry_referral( p ) ) {
272                         /* parent is a referral, don't allow add */
273                         char *matched_dn = p->e_dn;
274                         BerVarray refs = get_entry_referrals( be, conn, op, p );
275
276 #ifdef NEW_LOGGING
277                         LDAP_LOG ( OPERATION, DETAIL1, 
278                                 "bdb_add: parent is referral\n", 0, 0, 0 );
279 #else
280                         Debug( LDAP_DEBUG_TRACE, "bdb_add: parent is referral\n",
281                                 0, 0, 0 );
282 #endif
283
284                         send_ldap_result( conn, op, rc = LDAP_REFERRAL,
285                                 matched_dn, NULL, refs, NULL );
286
287                         ber_bvarray_free( refs );
288                         bdb_unlocked_cache_return_entry_r( &bdb->bi_cache, p );
289                         p = NULL;
290                         goto done;
291                 }
292
293 #ifdef BDB_SUBENTRIES
294                 if ( subentry ) {
295                         /* FIXME: */
296                         /* parent must be an administrative point of the required kind */
297                 }
298 #endif
299
300                 /* free parent and reader lock */
301                 bdb_unlocked_cache_return_entry_r( &bdb->bi_cache, p );
302                 p = NULL;
303
304         } else {
305                 /*
306                  * no parent!
307                  *      must be adding entry at suffix or with parent ""
308                  */
309                 if ( !be_isroot( be, &op->o_ndn )) {
310                         if ( be_issuffix( be, (struct berval *)&slap_empty_bv )
311                                 || be_isupdate( be, &op->o_ndn ) )
312                         {
313                                 p = (Entry *)&slap_entry_root;
314
315                                 /* check parent for "children" acl */
316                                 rc = access_allowed( be, conn, op, p,
317                                         children, NULL, ACL_WRITE, NULL );
318
319                                 p = NULL;
320
321                                 if ( ! rc ) {
322                                         switch( opinfo.boi_err ) {
323                                         case DB_LOCK_DEADLOCK:
324                                         case DB_LOCK_NOTGRANTED:
325                                                 goto retry;
326                                         }
327
328 #ifdef NEW_LOGGING
329                                         LDAP_LOG ( OPERATION, DETAIL1, 
330                                                 "bdb_add: no write access to parent\n", 0, 0, 0 );
331 #else
332                                         Debug( LDAP_DEBUG_TRACE,
333                                                 "bdb_add: no write access to parent\n",
334                                                 0, 0, 0 );
335 #endif
336                                         rc = LDAP_INSUFFICIENT_ACCESS;
337                                         text = "no write access to parent";
338                                         goto return_results;;
339                                 }
340
341                         } else {
342 #ifdef NEW_LOGGING
343                                 LDAP_LOG ( OPERATION, DETAIL1, "bdb_add: %s denied\n", 
344                                         pdn.bv_len == 0 ? "suffix" : "entry at root", 0, 0 );
345 #else
346                                 Debug( LDAP_DEBUG_TRACE, "bdb_add: %s denied\n",
347                                         pdn.bv_len == 0 ? "suffix" : "entry at root",
348                                         0, 0 );
349 #endif
350                                 rc = LDAP_INSUFFICIENT_ACCESS;
351                                 goto return_results;
352                         }
353                 }
354
355 #ifdef BDB_SUBENTRIES
356                 if( subentry ) {
357 #ifdef NEW_LOGGING
358                         LDAP_LOG ( OPERATION, DETAIL1, 
359                                 "bdb_add: no parent, cannot add subentry\n", 0, 0, 0 );
360 #else
361                         Debug( LDAP_DEBUG_TRACE,
362                                 "bdb_add: no parent, cannot add subentry\n",
363                                 0, 0, 0 );
364 #endif
365                         rc = LDAP_INSUFFICIENT_ACCESS;
366                         text = "no parent, cannot add subentry";
367                         goto return_results;;
368                 }
369 #endif
370 #if 0
371                 if ( ltid ) {
372                         DBT obj;
373                         obj.data = ",";
374                         obj.size = 1;
375                         rc = LOCK_GET( bdb->bi_dbenv, lockid, 0, &obj,
376                                 DB_LOCK_WRITE, &lock);
377                 }
378 #endif
379         }
380
381         rc = access_allowed( be, conn, op, e,
382                 entry, NULL, ACL_WRITE, NULL );
383
384         if ( ! rc ) {
385                 switch( opinfo.boi_err ) {
386                 case DB_LOCK_DEADLOCK:
387                 case DB_LOCK_NOTGRANTED:
388                         goto retry;
389                 }
390
391 #ifdef NEW_LOGGING
392                 LDAP_LOG ( OPERATION, DETAIL1, 
393                         "bdb_add: no write access to entry\n", 0, 0, 0 );
394 #else
395                 Debug( LDAP_DEBUG_TRACE, "bdb_add: no write access to entry\n",
396                         0, 0, 0 );
397 #endif
398                 rc = LDAP_INSUFFICIENT_ACCESS;
399                 text = "no write access to entry";
400                 goto return_results;;
401         }
402
403         /* dn2id index */
404         rc = bdb_dn2id_add( be, ltid, &pdn, e );
405         if ( rc != 0 ) {
406 #ifdef NEW_LOGGING
407                 LDAP_LOG ( OPERATION, ERR, 
408                         "bdb_add: dn2id_add failed: %s (%d)\n", db_strerror(rc), rc, 0 );
409 #else
410                 Debug( LDAP_DEBUG_TRACE, "bdb_add: dn2id_add failed: %s (%d)\n",
411                         db_strerror(rc), rc, 0 );
412 #endif
413
414                 switch( rc ) {
415                 case DB_LOCK_DEADLOCK:
416                 case DB_LOCK_NOTGRANTED:
417                         goto retry;
418                 case DB_KEYEXIST:
419                         rc = LDAP_ALREADY_EXISTS;
420                         break;
421                 default:
422                         rc = LDAP_OTHER;
423                 }
424                 goto return_results;
425         }
426
427         /* id2entry index */
428         rc = bdb_id2entry_add( be, ltid, e );
429         if ( rc != 0 ) {
430 #ifdef NEW_LOGGING
431                 LDAP_LOG ( OPERATION, ERR, "bdb_add: id2entry_add failed\n", 0, 0, 0 );
432 #else
433                 Debug( LDAP_DEBUG_TRACE, "bdb_add: id2entry_add failed\n",
434                         0, 0, 0 );
435 #endif
436                 switch( rc ) {
437                 case DB_LOCK_DEADLOCK:
438                 case DB_LOCK_NOTGRANTED:
439                         goto retry;
440                 default:
441                         rc = LDAP_OTHER;
442                 }
443                 text = "entry store failed";
444                 goto return_results;
445         }
446
447         /* attribute indexes */
448         rc = bdb_index_entry_add( be, ltid, e, e->e_attrs );
449         if ( rc != LDAP_SUCCESS ) {
450 #ifdef NEW_LOGGING
451                 LDAP_LOG ( OPERATION, ERR, 
452                         "bdb_add: index_entry_add failed\n", 0, 0, 0 );
453 #else
454                 Debug( LDAP_DEBUG_TRACE, "bdb_add: index_entry_add failed\n",
455                         0, 0, 0 );
456 #endif
457                 switch( rc ) {
458                 case DB_LOCK_DEADLOCK:
459                 case DB_LOCK_NOTGRANTED:
460                         goto retry;
461                 default:
462                         rc = LDAP_OTHER;
463                 }
464                 text = "index generation failed";
465                 goto return_results;
466         }
467
468
469         if( op->o_noop ) {
470                 if (( rc=TXN_ABORT( ltid )) != 0 ) {
471                         text = "txn_abort (no-op) failed";
472                 } else {
473                         noop = 1;
474                         rc = LDAP_SUCCESS;
475                 }
476
477         } else {
478                 char gid[DB_XIDDATASIZE];
479
480                 snprintf( gid, sizeof( gid ), "%s-%08lx-%08lx",
481                         bdb_uuid.bv_val, (long) op->o_connid, (long) op->o_opid );
482
483                 if (( rc=TXN_PREPARE( ltid, gid )) != 0 ) {
484                         text = "txn_prepare failed";
485
486                 } else {
487                         int ret = bdb_cache_add_entry_rw(bdb->bi_dbenv,
488                                         &bdb->bi_cache, e, CACHE_WRITE_LOCK,
489                                         locker, &lock);
490 #if 0
491                         if ( bdb_cache_add_entry_rw(&bdb->bi_cache,
492                                 e, CACHE_WRITE_LOCK) != 0 )
493 #endif
494                         switch ( ret ) {
495                         case 0:
496                                 break;
497                         case DB_LOCK_DEADLOCK:
498                         case DB_LOCK_NOTGRANTED:
499                                 goto retry;
500                         default:
501                                 ret = LDAP_OTHER;
502                         }
503
504                         if ( ret ) {
505                                 if(( rc=TXN_ABORT( ltid )) != 0 ) {
506                                         text = "cache add & txn_abort failed";
507                                 } else {
508                                         rc = LDAP_OTHER;
509                                         text = "cache add failed";
510                                 }
511                         } else {
512                                 if(( rc=TXN_COMMIT( ltid, 0 )) != 0 ) {
513                                         text = "txn_commit failed";
514                                 } else {
515                                         rc = LDAP_SUCCESS;
516                                 }
517                         }
518                 }
519         }
520
521         ltid = NULL;
522         op->o_private = NULL;
523
524         if (rc == LDAP_SUCCESS) {
525 #ifdef NEW_LOGGING
526                 LDAP_LOG ( OPERATION, RESULTS, 
527                         "bdb_add: added%s id=%08lx dn=\"%s\"\n", 
528                         op->o_noop ? " (no-op)" : "", e->e_id, e->e_dn );
529 #else
530                 Debug(LDAP_DEBUG_TRACE, "bdb_add: added%s id=%08lx dn=\"%s\"\n",
531                         op->o_noop ? " (no-op)" : "", e->e_id, e->e_dn );
532 #endif
533                 text = NULL;
534                 if ( !noop ) {
535                         bdb_cache_entry_commit( e );
536                 }
537         }
538         else {
539 #ifdef NEW_LOGGING
540                 LDAP_LOG ( OPERATION, ERR, 
541                         "bdb_add: %s : %s (%d)\n",  text, db_strerror(rc), rc );
542 #else
543                 Debug( LDAP_DEBUG_TRACE, "bdb_add: %s : %s (%d)\n",
544                         text, db_strerror(rc), rc );
545 #endif
546                 rc = LDAP_OTHER;
547         }
548
549 return_results:
550         send_ldap_result( conn, op, rc,
551                 NULL, text, NULL, NULL );
552
553 #if defined(LDAP_CLIENT_UPDATE) || defined(LDAP_SYNC)
554         if ( rc == LDAP_SUCCESS && !noop ) {
555                 LDAP_LIST_FOREACH ( ps_list, &bdb->psearch_list, link ) {
556                         bdb_psearch( be, conn, op, ps_list, e, LDAP_PSEARCH_BY_ADD );
557                 }
558         }
559 #endif /* LDAP_CLIENT_UPDATE */
560
561         if( rc == LDAP_SUCCESS && bdb->bi_txn_cp ) {
562                 ldap_pvt_thread_yield();
563                 TXN_CHECKPOINT( bdb->bi_dbenv,
564                         bdb->bi_txn_cp_kbyte, bdb->bi_txn_cp_min, 0 );
565         }
566
567 done:
568
569         if( ltid != NULL ) {
570                 TXN_ABORT( ltid );
571                 op->o_private = NULL;
572         }
573
574         return ( ( rc == LDAP_SUCCESS ) ? noop : rc );
575 }
576