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