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