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