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