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