]> git.sur5r.net Git - openldap/blob - servers/slapd/back-mdb/add.c
Merge remote-tracking branch 'origin/mdb.RE/0.9'
[openldap] / servers / slapd / back-mdb / add.c
1 /* add.c - ldap mdb back-end add routine */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2000-2014 The OpenLDAP Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16
17 #include "portable.h"
18
19 #include <stdio.h>
20 #include <ac/string.h>
21
22 #include "back-mdb.h"
23
24 int
25 mdb_add(Operation *op, SlapReply *rs )
26 {
27         struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
28         struct berval   pdn;
29         Entry           *p = NULL, *oe = op->ora_e;
30         char textbuf[SLAP_TEXT_BUFLEN];
31         size_t textlen = sizeof textbuf;
32         AttributeDescription *children = slap_schema.si_ad_children;
33         AttributeDescription *entry = slap_schema.si_ad_entry;
34         MDB_txn         *txn = NULL;
35         MDB_cursor      *mc = NULL;
36         MDB_cursor      *mcd;
37         ID eid, pid = 0;
38         mdb_op_info opinfo = {{{ 0 }}}, *moi = &opinfo;
39         int subentry;
40
41         int             success;
42
43         LDAPControl **postread_ctrl = NULL;
44         LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
45         int num_ctrls = 0;
46
47         Debug(LDAP_DEBUG_ARGS, "==> " LDAP_XSTRING(mdb_add) ": %s\n",
48                 op->ora_e->e_name.bv_val, 0, 0);
49
50 #ifdef LDAP_X_TXN
51         if( op->o_txnSpec && txn_preop( op, rs ))
52                 return rs->sr_err;
53 #endif
54
55         ctrls[num_ctrls] = 0;
56
57         /* check entry's schema */
58         rs->sr_err = entry_schema_check( op, op->ora_e, NULL,
59                 get_relax(op), 1, NULL, &rs->sr_text, textbuf, textlen );
60         if ( rs->sr_err != LDAP_SUCCESS ) {
61                 Debug( LDAP_DEBUG_TRACE,
62                         LDAP_XSTRING(mdb_add) ": entry failed schema check: "
63                         "%s (%d)\n", rs->sr_text, rs->sr_err, 0 );
64                 goto return_results;
65         }
66
67         /* begin transaction */
68         rs->sr_err = mdb_opinfo_get( op, mdb, 0, &moi );
69         rs->sr_text = NULL;
70         if( rs->sr_err != 0 ) {
71                 Debug( LDAP_DEBUG_TRACE,
72                         LDAP_XSTRING(mdb_add) ": txn_begin failed: %s (%d)\n",
73                         mdb_strerror(rs->sr_err), rs->sr_err, 0 );
74                 rs->sr_err = LDAP_OTHER;
75                 rs->sr_text = "internal error";
76                 goto return_results;
77         }
78         txn = moi->moi_txn;
79
80         /* add opattrs to shadow as well, only missing attrs will actually
81          * be added; helps compatibility with older OL versions */
82         rs->sr_err = slap_add_opattrs( op, &rs->sr_text, textbuf, textlen, 1 );
83         if ( rs->sr_err != LDAP_SUCCESS ) {
84                 Debug( LDAP_DEBUG_TRACE,
85                         LDAP_XSTRING(mdb_add) ": entry failed op attrs add: "
86                         "%s (%d)\n", rs->sr_text, rs->sr_err, 0 );
87                 goto return_results;
88         }
89
90         if ( get_assert( op ) &&
91                 ( test_filter( op, op->ora_e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
92         {
93                 rs->sr_err = LDAP_ASSERTION_FAILED;
94                 goto return_results;
95         }
96
97         subentry = is_entry_subentry( op->ora_e );
98
99         /*
100          * Get the parent dn and see if the corresponding entry exists.
101          */
102         if ( be_issuffix( op->o_bd, &op->ora_e->e_nname ) ) {
103                 pdn = slap_empty_bv;
104         } else {
105                 dnParent( &op->ora_e->e_nname, &pdn );
106         }
107
108         rs->sr_err = mdb_cursor_open( txn, mdb->mi_dn2id, &mcd );
109         if( rs->sr_err != 0 ) {
110                 Debug( LDAP_DEBUG_TRACE,
111                         LDAP_XSTRING(mdb_add) ": mdb_cursor_open failed (%d)\n",
112                         rs->sr_err, 0, 0 );
113                 rs->sr_err = LDAP_OTHER;
114                 rs->sr_text = "internal error";
115                 goto return_results;
116         }
117
118         /* get entry or parent */
119         rs->sr_err = mdb_dn2entry( op, txn, mcd, &op->ora_e->e_nname, &p, NULL, 1 );
120         switch( rs->sr_err ) {
121         case 0:
122                 rs->sr_err = LDAP_ALREADY_EXISTS;
123                 mdb_entry_return( op, p );
124                 p = NULL;
125                 goto return_results;
126         case MDB_NOTFOUND:
127                 break;
128         case LDAP_BUSY:
129                 rs->sr_text = "ldap server busy";
130                 goto return_results;
131         default:
132                 rs->sr_err = LDAP_OTHER;
133                 rs->sr_text = "internal error";
134                 goto return_results;
135         }
136
137         if ( !p )
138                 p = (Entry *)&slap_entry_root;
139
140         if ( !bvmatch( &pdn, &p->e_nname ) ) {
141                 rs->sr_matched = ber_strdup_x( p->e_name.bv_val,
142                         op->o_tmpmemctx );
143                 if ( p != (Entry *)&slap_entry_root && is_entry_referral( p )) {
144                         BerVarray ref = get_entry_referrals( op, p );
145                         rs->sr_ref = referral_rewrite( ref, &p->e_name,
146                                 &op->o_req_dn, LDAP_SCOPE_DEFAULT );
147                         ber_bvarray_free( ref );
148                 } else {
149                         rs->sr_ref = NULL;
150                 }
151                 if ( p != (Entry *)&slap_entry_root )
152                         mdb_entry_return( op, p );
153                 p = NULL;
154                 Debug( LDAP_DEBUG_TRACE,
155                         LDAP_XSTRING(mdb_add) ": parent "
156                         "does not exist\n", 0, 0, 0 );
157
158                 rs->sr_err = LDAP_REFERRAL;
159                 rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
160                 goto return_results;
161         }
162
163         rs->sr_err = access_allowed( op, p,
164                 children, NULL, ACL_WADD, NULL );
165
166         if ( ! rs->sr_err ) {
167                 if ( p != (Entry *)&slap_entry_root )
168                         mdb_entry_return( op, p );
169                 p = NULL;
170
171                 Debug( LDAP_DEBUG_TRACE,
172                         LDAP_XSTRING(mdb_add) ": no write access to parent\n",
173                         0, 0, 0 );
174                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
175                 rs->sr_text = "no write access to parent";
176                 goto return_results;;
177         }
178
179         if ( p != (Entry *)&slap_entry_root ) {
180                 if ( is_entry_subentry( p ) ) {
181                         mdb_entry_return( op, p );
182                         p = NULL;
183                         /* parent is a subentry, don't allow add */
184                         Debug( LDAP_DEBUG_TRACE,
185                                 LDAP_XSTRING(mdb_add) ": parent is subentry\n",
186                                 0, 0, 0 );
187                         rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
188                         rs->sr_text = "parent is a subentry";
189                         goto return_results;;
190                 }
191
192                 if ( is_entry_alias( p ) ) {
193                         mdb_entry_return( op, p );
194                         p = NULL;
195                         /* parent is an alias, don't allow add */
196                         Debug( LDAP_DEBUG_TRACE,
197                                 LDAP_XSTRING(mdb_add) ": parent is alias\n",
198                                 0, 0, 0 );
199                         rs->sr_err = LDAP_ALIAS_PROBLEM;
200                         rs->sr_text = "parent is an alias";
201                         goto return_results;;
202                 }
203
204                 if ( is_entry_referral( p ) ) {
205                         BerVarray ref = get_entry_referrals( op, p );
206                         /* parent is a referral, don't allow add */
207                         rs->sr_matched = ber_strdup_x( p->e_name.bv_val,
208                                 op->o_tmpmemctx );
209                         rs->sr_ref = referral_rewrite( ref, &p->e_name,
210                                 &op->o_req_dn, LDAP_SCOPE_DEFAULT );
211                         ber_bvarray_free( ref );
212                         mdb_entry_return( op, p );
213                         p = NULL;
214                         Debug( LDAP_DEBUG_TRACE,
215                                 LDAP_XSTRING(mdb_add) ": parent is referral\n",
216                                 0, 0, 0 );
217
218                         rs->sr_err = LDAP_REFERRAL;
219                         rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
220                         goto return_results;
221                 }
222
223         }
224
225         if ( subentry ) {
226                 /* FIXME: */
227                 /* parent must be an administrative point of the required kind */
228         }
229
230         /* free parent */
231         if ( p != (Entry *)&slap_entry_root ) {
232                 pid = p->e_id;
233                 if ( p->e_nname.bv_len ) {
234                         struct berval ppdn;
235
236                         /* ITS#5326: use parent's DN if differs from provided one */
237                         dnParent( &op->ora_e->e_name, &ppdn );
238                         if ( !dn_match( &p->e_name, &ppdn ) ) {
239                                 struct berval rdn;
240                                 struct berval newdn;
241
242                                 dnRdn( &op->ora_e->e_name, &rdn );
243
244                                 build_new_dn( &newdn, &p->e_name, &rdn, NULL ); 
245                                 if ( op->ora_e->e_name.bv_val != op->o_req_dn.bv_val )
246                                         ber_memfree( op->ora_e->e_name.bv_val );
247                                 op->ora_e->e_name = newdn;
248
249                                 /* FIXME: should check whether
250                                  * dnNormalize(newdn) == e->e_nname ... */
251                         }
252                 }
253
254                 mdb_entry_return( op, p );
255         }
256         p = NULL;
257
258         rs->sr_err = access_allowed( op, op->ora_e,
259                 entry, NULL, ACL_WADD, NULL );
260
261         if ( ! rs->sr_err ) {
262                 Debug( LDAP_DEBUG_TRACE,
263                         LDAP_XSTRING(mdb_add) ": no write access to entry\n",
264                         0, 0, 0 );
265                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
266                 rs->sr_text = "no write access to entry";
267                 goto return_results;;
268         }
269
270         /* 
271          * Check ACL for attribute write access
272          */
273         if (!acl_check_modlist(op, oe, op->ora_modlist)) {
274                 Debug( LDAP_DEBUG_TRACE,
275                         LDAP_XSTRING(mdb_add) ": no write access to attribute\n",
276                         0, 0, 0 );
277                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
278                 rs->sr_text = "no write access to attribute";
279                 goto return_results;;
280         }
281
282         rs->sr_err = mdb_cursor_open( txn, mdb->mi_id2entry, &mc );
283         if( rs->sr_err != 0 ) {
284                 Debug( LDAP_DEBUG_TRACE,
285                         LDAP_XSTRING(mdb_add) ": mdb_cursor_open failed (%d)\n",
286                         rs->sr_err, 0, 0 );
287                 rs->sr_err = LDAP_OTHER;
288                 rs->sr_text = "internal error";
289                 goto return_results;
290         }
291
292         rs->sr_err = mdb_next_id( op->o_bd, mc, &eid );
293         if( rs->sr_err != 0 ) {
294                 Debug( LDAP_DEBUG_TRACE,
295                         LDAP_XSTRING(mdb_add) ": next_id failed (%d)\n",
296                         rs->sr_err, 0, 0 );
297                 rs->sr_err = LDAP_OTHER;
298                 rs->sr_text = "internal error";
299                 goto return_results;
300         }
301         op->ora_e->e_id = eid;
302
303         /* dn2id index */
304         rs->sr_err = mdb_dn2id_add( op, mcd, mcd, pid, 1, 1, op->ora_e );
305         mdb_cursor_close( mcd );
306         if ( rs->sr_err != 0 ) {
307                 Debug( LDAP_DEBUG_TRACE,
308                         LDAP_XSTRING(mdb_add) ": dn2id_add failed: %s (%d)\n",
309                         mdb_strerror(rs->sr_err), rs->sr_err, 0 );
310
311                 switch( rs->sr_err ) {
312                 case MDB_KEYEXIST:
313                         rs->sr_err = LDAP_ALREADY_EXISTS;
314                         break;
315                 default:
316                         rs->sr_err = LDAP_OTHER;
317                 }
318                 goto return_results;
319         }
320
321         /* attribute indexes */
322         rs->sr_err = mdb_index_entry_add( op, txn, op->ora_e );
323         if ( rs->sr_err != LDAP_SUCCESS ) {
324                 Debug( LDAP_DEBUG_TRACE,
325                         LDAP_XSTRING(mdb_add) ": index_entry_add failed\n",
326                         0, 0, 0 );
327                 rs->sr_err = LDAP_OTHER;
328                 rs->sr_text = "index generation failed";
329                 goto return_results;
330         }
331
332         /* id2entry index */
333         rs->sr_err = mdb_id2entry_add( op, txn, mc, op->ora_e );
334         if ( rs->sr_err != 0 ) {
335                 Debug( LDAP_DEBUG_TRACE,
336                         LDAP_XSTRING(mdb_add) ": id2entry_add failed\n",
337                         0, 0, 0 );
338                 if ( rs->sr_err == LDAP_ADMINLIMIT_EXCEEDED ) {
339                         rs->sr_text = "entry is too big";
340                 } else {
341                         rs->sr_err = LDAP_OTHER;
342                         rs->sr_text = "entry store failed";
343                 }
344                 goto return_results;
345         }
346
347         /* post-read */
348         if( op->o_postread ) {
349                 if( postread_ctrl == NULL ) {
350                         postread_ctrl = &ctrls[num_ctrls++];
351                         ctrls[num_ctrls] = NULL;
352                 }
353                 if ( slap_read_controls( op, rs, op->ora_e,
354                         &slap_post_read_bv, postread_ctrl ) )
355                 {
356                         Debug( LDAP_DEBUG_TRACE,
357                                 "<=- " LDAP_XSTRING(mdb_add) ": post-read "
358                                 "failed!\n", 0, 0, 0 );
359                         if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
360                                 /* FIXME: is it correct to abort
361                                  * operation if control fails? */
362                                 goto return_results;
363                         }
364                 }
365         }
366
367         if ( moi == &opinfo ) {
368                 LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.moi_oe, OpExtra, oe_next );
369                 opinfo.moi_oe.oe_key = NULL;
370                 if ( op->o_noop ) {
371                         mdb_txn_abort( txn );
372                         rs->sr_err = LDAP_X_NO_OPERATION;
373                         txn = NULL;
374                         goto return_results;
375                 }
376
377                 rs->sr_err = mdb_txn_commit( txn );
378                 txn = NULL;
379                 if ( rs->sr_err != 0 ) {
380                         rs->sr_text = "txn_commit failed";
381                         Debug( LDAP_DEBUG_ANY,
382                                 LDAP_XSTRING(mdb_add) ": %s : %s (%d)\n",
383                                 rs->sr_text, mdb_strerror(rs->sr_err), rs->sr_err );
384                         rs->sr_err = LDAP_OTHER;
385                         goto return_results;
386                 }
387         }
388
389         Debug(LDAP_DEBUG_TRACE,
390                 LDAP_XSTRING(mdb_add) ": added%s id=%08lx dn=\"%s\"\n",
391                 op->o_noop ? " (no-op)" : "",
392                 op->ora_e->e_id, op->ora_e->e_dn );
393
394         rs->sr_text = NULL;
395         if( num_ctrls ) rs->sr_ctrls = ctrls;
396
397 return_results:
398         success = rs->sr_err;
399         send_ldap_result( op, rs );
400
401         if( moi == &opinfo ) {
402                 if( txn != NULL ) {
403                         mdb_txn_abort( txn );
404                 }
405                 if ( opinfo.moi_oe.oe_key ) {
406                         LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.moi_oe, OpExtra, oe_next );
407                 }
408         } else {
409                 moi->moi_ref--;
410         }
411
412         if( success == LDAP_SUCCESS ) {
413 #if 0
414                 if ( mdb->bi_txn_cp_kbyte ) {
415                         TXN_CHECKPOINT( mdb->bi_dbenv,
416                                 mdb->bi_txn_cp_kbyte, mdb->bi_txn_cp_min, 0 );
417                 }
418 #endif
419         }
420
421         slap_graduate_commit_csn( op );
422
423         if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) {
424                 slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
425                 slap_sl_free( *postread_ctrl, op->o_tmpmemctx );
426         }
427         return rs->sr_err;
428 }