]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldbm/add.c
Do not return pointers into BerElement we do not own
[openldap] / servers / slapd / back-ldbm / add.c
1 /* add.c - ldap ldbm back-end add routine */
2 /* $OpenLDAP$ */
3 /*
4  * Copyright 1998-2003 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
12 #include <ac/socket.h>
13 #include <ac/string.h>
14
15 #include "slap.h"
16 #include "back-ldbm.h"
17 #include "proto-back-ldbm.h"
18
19 int
20 ldbm_back_add(
21     Operation   *op,
22     SlapReply   *rs )
23 {
24         struct ldbminfo *li = (struct ldbminfo *) op->o_bd->be_private;
25         struct berval   pdn;
26         Entry           *p = NULL;
27         ID               id = NOID;
28         AttributeDescription *children = slap_schema.si_ad_children;
29         AttributeDescription *entry = slap_schema.si_ad_entry;
30         char textbuf[SLAP_TEXT_BUFLEN];
31         size_t textlen = sizeof textbuf;
32
33 #ifdef NEW_LOGGING
34         LDAP_LOG( BACK_LDBM, ENTRY, "ldbm_back_add: %s\n", op->o_req_dn.bv_val, 0, 0 );
35 #else
36         Debug(LDAP_DEBUG_ARGS, "==> ldbm_back_add: %s\n", op->o_req_dn.bv_val, 0, 0);
37 #endif
38         
39 #ifndef LDAP_CACHING
40         rs->sr_err = entry_schema_check( op->o_bd, op->oq_add.rs_e, NULL, &rs->sr_text, textbuf, textlen );
41 #else /* LDAP_CACHING */
42         if ( !op->o_caching_on ) {
43                 rs->sr_err = entry_schema_check( op->o_bd, op->oq_add.rs_e, NULL, &rs->sr_text, textbuf, textlen );
44         } else {
45                 rs->sr_err = LDAP_SUCCESS;
46         }
47 #endif /* LDAP_CACHING */
48
49         if ( rs->sr_err != LDAP_SUCCESS ) {
50 #ifdef NEW_LOGGING
51                 LDAP_LOG( BACK_LDBM, ERR, 
52                         "ldbm_back_add: entry (%s) failed schema check.\n", op->o_req_dn.bv_val, 0, 0 );
53 #else
54                 Debug( LDAP_DEBUG_TRACE, "entry failed schema check: %s\n",
55                         rs->sr_text, 0, 0 );
56 #endif
57
58                 send_ldap_result( op, rs );
59                 return( -1 );
60         }
61
62 #ifdef LDAP_CACHING
63         if ( !op->o_caching_on ) {
64 #endif /* LDAP_CACHING */
65         if ( !access_allowed( op, op->oq_add.rs_e,
66                                 entry, NULL, ACL_WRITE, NULL ) )
67         {
68 #ifdef NEW_LOGGING
69                 LDAP_LOG( BACK_LDBM, ERR, 
70                         "ldbm_back_add: No write access to entry (%s).\n", 
71                         op->o_req_dn.bv_val, 0, 0 );
72 #else
73                 Debug( LDAP_DEBUG_TRACE, "no write access to entry\n", 0,
74                     0, 0 );
75 #endif
76
77                 send_ldap_error( op, rs, LDAP_INSUFFICIENT_ACCESS,
78                     "no write access to entry" );
79
80                 return -1;
81         }
82 #ifdef LDAP_CACHING
83         }
84 #endif /* LDAP_CACHING */
85
86         /* grab giant lock for writing */
87         ldap_pvt_thread_rdwr_wlock(&li->li_giant_rwlock);
88
89         if ( ( rs->sr_err = dn2id( op->o_bd, &op->o_req_ndn, &id ) ) || id != NOID ) {
90                 /* if (rs->sr_err) something bad happened to ldbm cache */
91                 ldap_pvt_thread_rdwr_wunlock(&li->li_giant_rwlock);
92                 rs->sr_err = rs->sr_err ? LDAP_OTHER : LDAP_ALREADY_EXISTS;
93                 send_ldap_result( op, rs );
94                 return( -1 );
95         }
96
97         /*
98          * Get the parent dn and see if the corresponding entry exists.
99          * If the parent does not exist, only allow the "root" user to
100          * add the entry.
101          */
102
103         if ( be_issuffix( op->o_bd, &op->o_req_ndn ) ) {
104                 pdn = slap_empty_bv;
105         } else {
106                 dnParent( &op->o_req_ndn, &pdn );
107         }
108
109 #ifndef LDAP_CACHING
110         if( pdn.bv_len )
111 #else /* LDAP_CACHING */
112         if( pdn.bv_len && !op->o_caching_on )
113 #endif /* LDAP_CACHING */
114         {
115                 Entry *matched = NULL;
116
117                 /* get parent with writer lock */
118                 if ( (p = dn2entry_w( op->o_bd, &pdn, &matched )) == NULL ) {
119                         if ( matched != NULL ) {
120                                 rs->sr_matched = ch_strdup( matched->e_dn );
121                                 rs->sr_ref = is_entry_referral( matched )
122                                         ? get_entry_referrals( op, matched )
123                                         : NULL;
124                                 cache_return_entry_r( &li->li_cache, matched );
125
126                         } else {
127                                 rs->sr_ref = referral_rewrite( default_referral,
128                                         NULL, &op->o_req_dn, LDAP_SCOPE_DEFAULT );
129                         }
130
131                         ldap_pvt_thread_rdwr_wunlock(&li->li_giant_rwlock);
132
133 #ifdef NEW_LOGGING
134                         LDAP_LOG( BACK_LDBM, ERR, 
135                                 "ldbm_back_add: Parent of (%s) does not exist.\n", 
136                                 op->o_req_dn.bv_val, 0, 0 );
137 #else
138                         Debug( LDAP_DEBUG_TRACE, "parent does not exist\n",
139                                 0, 0, 0 );
140 #endif
141
142                         rs->sr_text = rs->sr_ref ? "parent is referral" : "parent does not exist";
143                         rs->sr_err = LDAP_REFERRAL;
144                         send_ldap_result( op, rs );
145
146                         ber_bvarray_free( rs->sr_ref );
147                         free( (char *)rs->sr_matched );
148
149                         return -1;
150                 }
151
152                 if ( ! access_allowed( op, p,
153                         children, NULL, ACL_WRITE, NULL ) )
154                 {
155                         /* free parent and writer lock */
156                         cache_return_entry_w( &li->li_cache, p ); 
157                         ldap_pvt_thread_rdwr_wunlock(&li->li_giant_rwlock);
158
159 #ifdef NEW_LOGGING
160                         LDAP_LOG( BACK_LDBM, ERR, 
161                                 "ldbm_back_add: No write access to parent (%s).\n", 
162                                 op->o_req_dn.bv_val, 0, 0 );
163 #else
164                         Debug( LDAP_DEBUG_TRACE, "no write access to parent\n", 0,
165                             0, 0 );
166 #endif
167
168                         send_ldap_error( op, rs, LDAP_INSUFFICIENT_ACCESS,
169                             "no write access to parent" );
170
171                         return -1;
172                 }
173
174                 if ( is_entry_alias( p ) ) {
175                         /* parent is an alias, don't allow add */
176
177                         /* free parent and writer lock */
178                         cache_return_entry_w( &li->li_cache, p );
179                         ldap_pvt_thread_rdwr_wunlock(&li->li_giant_rwlock);
180
181 #ifdef NEW_LOGGING
182                         LDAP_LOG(BACK_LDBM, ERR, 
183                                 "ldbm_back_add:  Parent is an alias.\n", 0, 0, 0 );
184 #else
185                         Debug( LDAP_DEBUG_TRACE, "parent is alias\n", 0,
186                             0, 0 );
187 #endif
188
189
190                         send_ldap_error( op, rs, LDAP_ALIAS_PROBLEM,
191                             "parent is an alias" );
192
193                         return -1;
194                 }
195
196                 if ( is_entry_referral( p ) ) {
197                         /* parent is a referral, don't allow add */
198                         rs->sr_matched = ch_strdup( p->e_dn );
199                         rs->sr_ref = is_entry_referral( p )
200                                 ? get_entry_referrals( op, p )
201                                 : NULL;
202
203                         /* free parent and writer lock */
204                         cache_return_entry_w( &li->li_cache, p );
205                         ldap_pvt_thread_rdwr_wunlock(&li->li_giant_rwlock);
206
207 #ifdef NEW_LOGGING
208                         LDAP_LOG( BACK_LDBM, ERR,
209                                    "ldbm_back_add: Parent is referral.\n", 0, 0, 0 );
210 #else
211                         Debug( LDAP_DEBUG_TRACE, "parent is referral\n", 0,
212                             0, 0 );
213 #endif
214                         rs->sr_err = LDAP_REFERRAL;
215                         send_ldap_result( op, rs );
216
217                         ber_bvarray_free( rs->sr_ref );
218                         free( (char *)rs->sr_matched );
219                         return -1;
220                 }
221
222         } else {
223 #ifndef LDAP_CACHING
224                 if( pdn.bv_val != NULL )
225 #else /* LDAP_CACHING */
226                 if( pdn.bv_val != NULL && !op->o_caching_on )
227 #endif /* LDAP_CACHING */
228                 {
229                         assert( *pdn.bv_val == '\0' );
230                 }
231
232                 /* no parent, must be adding entry to root */
233 #ifndef LDAP_CACHING
234                 if ( !be_isroot( op->o_bd, &op->o_ndn ) )
235 #else /* LDAP_CACHING */
236                 if ( !be_isroot( op->o_bd, &op->o_ndn ) && !op->o_caching_on )
237 #endif /* LDAP_CACHING */
238                 {
239                         if ( be_issuffix( op->o_bd, (struct berval *)&slap_empty_bv ) || be_isupdate( op->o_bd, &op->o_ndn ) ) {
240                                 p = (Entry *)&slap_entry_root;
241                                 
242                                 rs->sr_err = access_allowed( op, p,
243                                         children, NULL, ACL_WRITE, NULL );
244                                 p = NULL;
245                                 
246                                 if ( ! rs->sr_err ) {
247                                         ldap_pvt_thread_rdwr_wunlock(&li->li_giant_rwlock);
248
249 #ifdef NEW_LOGGING
250                                         LDAP_LOG( BACK_LDBM, ERR,
251                                                 "ldbm_back_add: No write "
252                                                 "access to parent (\"\").\n", 0, 0, 0 );
253 #else
254                                         Debug( LDAP_DEBUG_TRACE, 
255                                                 "no write access to parent\n", 
256                                                 0, 0, 0 );
257 #endif
258
259                                         send_ldap_error( op, rs,
260                                                 LDAP_INSUFFICIENT_ACCESS,
261                                                 "no write access to parent" );
262
263                                         return -1;
264                                 }
265
266                         } else {
267                                 ldap_pvt_thread_rdwr_wunlock(&li->li_giant_rwlock);
268
269 #ifdef NEW_LOGGING
270                                 LDAP_LOG( BACK_LDBM, ERR,
271                                            "ldbm_back_add: %s add denied.\n",
272                                            pdn.bv_val == NULL ? "suffix" 
273                                            : "entry at root", 0, 0 );
274 #else
275                                 Debug( LDAP_DEBUG_TRACE, "%s add denied\n",
276                                                 pdn.bv_val == NULL ? "suffix" 
277                                                 : "entry at root", 0, 0 );
278 #endif
279
280                                 send_ldap_error( op, rs,
281                                                 LDAP_INSUFFICIENT_ACCESS, NULL );
282
283                                 return -1;
284                         }
285                 }
286         }
287
288         if ( next_id( op->o_bd, &op->oq_add.rs_e->e_id ) ) {
289                 if( p != NULL) {
290                         /* free parent and writer lock */
291                         cache_return_entry_w( &li->li_cache, p ); 
292                 }
293
294                 ldap_pvt_thread_rdwr_wunlock(&li->li_giant_rwlock);
295
296 #ifdef NEW_LOGGING
297                 LDAP_LOG( BACK_LDBM, ERR,
298                         "ldbm_back_add: next_id failed.\n", 0, 0, 0 );
299 #else
300                 Debug( LDAP_DEBUG_ANY, "ldbm_add: next_id failed\n",
301                         0, 0, 0 );
302 #endif
303
304                 send_ldap_error( op, rs, LDAP_OTHER,
305                         "next_id add failed" );
306
307                 return( -1 );
308         }
309
310         /*
311          * Try to add the entry to the cache, assign it a new dnid.
312          */
313         rs->sr_err = cache_add_entry_rw(&li->li_cache, op->oq_add.rs_e, CACHE_WRITE_LOCK);
314
315         if ( rs->sr_err != 0 ) {
316                 if( p != NULL) {
317                         /* free parent and writer lock */
318                         cache_return_entry_w( &li->li_cache, p ); 
319                 }
320
321                 ldap_pvt_thread_rdwr_wunlock(&li->li_giant_rwlock);
322
323 #ifdef NEW_LOGGING
324                 LDAP_LOG( BACK_LDBM, ERR,
325                         "ldbm_back_add: cache_add_entry_lock failed.\n", 0, 0, 0 );
326 #else
327                 Debug( LDAP_DEBUG_ANY, "cache_add_entry_lock failed\n", 0, 0,
328                     0 );
329 #endif
330
331                 rs->sr_text = rs->sr_err > 0 ? NULL : "cache add failed";
332                 rs->sr_err = rs->sr_err > 0 ? LDAP_ALREADY_EXISTS : LDAP_OTHER;
333                 send_ldap_result( op, rs );
334
335                 return( -1 );
336         }
337
338         rs->sr_err = -1;
339
340         /* attribute indexes */
341         if ( index_entry_add( op->o_bd, op->oq_add.rs_e ) != LDAP_SUCCESS ) {
342 #ifdef NEW_LOGGING
343                 LDAP_LOG( BACK_LDBM, ERR,
344                         "ldbm_back_add: index_entry_add failed.\n", 0, 0, 0 );
345 #else
346                 Debug( LDAP_DEBUG_TRACE, "index_entry_add failed\n", 0,
347                     0, 0 );
348 #endif
349                 
350                 send_ldap_error( op, rs, LDAP_OTHER,
351                         "index generation failed" );
352
353                 goto return_results;
354         }
355
356         /* dn2id index */
357         if ( dn2id_add( op->o_bd, &op->oq_add.rs_e->e_nname, op->oq_add.rs_e->e_id ) != 0 ) {
358 #ifdef NEW_LOGGING
359                 LDAP_LOG( BACK_LDBM, ERR,
360                         "ldbm_back_add: dn2id_add failed.\n", 0, 0, 0 );
361 #else
362                 Debug( LDAP_DEBUG_TRACE, "dn2id_add failed\n", 0,
363                     0, 0 );
364 #endif
365                 /* FIXME: delete attr indices? */
366
367                 send_ldap_error( op, rs, LDAP_OTHER,
368                         "DN index generation failed" );
369
370                 goto return_results;
371         }
372
373         /* id2entry index */
374         if ( id2entry_add( op->o_bd, op->oq_add.rs_e ) != 0 ) {
375 #ifdef NEW_LOGGING
376                 LDAP_LOG( BACK_LDBM, ERR,
377                            "ldbm_back_add: id2entry_add failed.\n", 0, 0, 0 );
378 #else
379                 Debug( LDAP_DEBUG_TRACE, "id2entry_add failed\n", 0,
380                     0, 0 );
381 #endif
382
383                 /* FIXME: delete attr indices? */
384                 (void) dn2id_delete( op->o_bd, &op->oq_add.rs_e->e_nname, op->oq_add.rs_e->e_id );
385                 
386                 send_ldap_error( op, rs, LDAP_OTHER,
387                         "entry store failed" );
388
389                 goto return_results;
390         }
391
392         rs->sr_err = LDAP_SUCCESS;
393         send_ldap_result( op, rs );
394
395         /* marks the entry as committed, so it is added to the cache;
396          * otherwise it is removed from the cache, but not destroyed;
397          * it will be destroyed by the caller */
398         cache_entry_commit( op->oq_add.rs_e );
399
400 return_results:;
401         if (p != NULL) {
402                 /* free parent and writer lock */
403                 cache_return_entry_w( &li->li_cache, p ); 
404         }
405
406         if ( rs->sr_err ) {
407                 /*
408                  * in case of error, writer lock is freed 
409                  * and entry's private data is destroyed.
410                  * otherwise, this is done when entry is released
411                  */
412                 cache_return_entry_w( &li->li_cache, op->oq_add.rs_e );
413                 ldap_pvt_thread_rdwr_wunlock(&li->li_giant_rwlock);
414         }
415
416         return( rs->sr_err );
417 }