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