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