]> git.sur5r.net Git - openldap/blob - servers/slapd/back-mdb/add.c
Merge remote-tracking branch 'origin/mdb.master'
[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 #ifdef LDAP_X_TXN
48         int settle = 0;
49 #endif
50
51         Debug(LDAP_DEBUG_ARGS, "==> " LDAP_XSTRING(mdb_add) ": %s\n",
52                 op->ora_e->e_name.bv_val, 0, 0);
53
54 #ifdef LDAP_X_TXN
55         if( op->o_txnSpec ) {
56                 /* acquire connection lock */
57                 ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
58                 if( op->o_conn->c_txn == CONN_TXN_INACTIVE ) {
59                         rs->sr_text = "invalid transaction identifier";
60                         rs->sr_err = LDAP_X_TXN_ID_INVALID;
61                         goto txnReturn;
62                 } else if( op->o_conn->c_txn == CONN_TXN_SETTLE ) {
63                         settle=1;
64                         goto txnReturn;
65                 }
66
67                 if( op->o_conn->c_txn_backend == NULL ) {
68                         op->o_conn->c_txn_backend = op->o_bd;
69
70                 } else if( op->o_conn->c_txn_backend != op->o_bd ) {
71                         rs->sr_text = "transaction cannot span multiple database contexts";
72                         rs->sr_err = LDAP_AFFECTS_MULTIPLE_DSAS;
73                         goto txnReturn;
74                 }
75
76                 /* insert operation into transaction */
77
78                 rs->sr_text = "transaction specified";
79                 rs->sr_err = LDAP_X_TXN_SPECIFY_OKAY;
80
81 txnReturn:
82                 /* release connection lock */
83                 ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
84
85                 if( !settle ) {
86                         send_ldap_result( op, rs );
87                         return rs->sr_err;
88                 }
89         }
90 #endif
91
92         ctrls[num_ctrls] = 0;
93
94         /* check entry's schema */
95         rs->sr_err = entry_schema_check( op, op->ora_e, NULL,
96                 get_relax(op), 1, NULL, &rs->sr_text, textbuf, textlen );
97         if ( rs->sr_err != LDAP_SUCCESS ) {
98                 Debug( LDAP_DEBUG_TRACE,
99                         LDAP_XSTRING(mdb_add) ": entry failed schema check: "
100                         "%s (%d)\n", rs->sr_text, rs->sr_err, 0 );
101                 goto return_results;
102         }
103
104         /* begin transaction */
105         rs->sr_err = mdb_opinfo_get( op, mdb, 0, &moi );
106         rs->sr_text = NULL;
107         if( rs->sr_err != 0 ) {
108                 Debug( LDAP_DEBUG_TRACE,
109                         LDAP_XSTRING(mdb_add) ": txn_begin failed: %s (%d)\n",
110                         mdb_strerror(rs->sr_err), rs->sr_err, 0 );
111                 rs->sr_err = LDAP_OTHER;
112                 rs->sr_text = "internal error";
113                 goto return_results;
114         }
115         txn = moi->moi_txn;
116
117         /* add opattrs to shadow as well, only missing attrs will actually
118          * be added; helps compatibility with older OL versions */
119         rs->sr_err = slap_add_opattrs( op, &rs->sr_text, textbuf, textlen, 1 );
120         if ( rs->sr_err != LDAP_SUCCESS ) {
121                 Debug( LDAP_DEBUG_TRACE,
122                         LDAP_XSTRING(mdb_add) ": entry failed op attrs add: "
123                         "%s (%d)\n", rs->sr_text, rs->sr_err, 0 );
124                 goto return_results;
125         }
126
127         if ( get_assert( op ) &&
128                 ( test_filter( op, op->ora_e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
129         {
130                 rs->sr_err = LDAP_ASSERTION_FAILED;
131                 goto return_results;
132         }
133
134         subentry = is_entry_subentry( op->ora_e );
135
136         /*
137          * Get the parent dn and see if the corresponding entry exists.
138          */
139         if ( be_issuffix( op->o_bd, &op->ora_e->e_nname ) ) {
140                 pdn = slap_empty_bv;
141         } else {
142                 dnParent( &op->ora_e->e_nname, &pdn );
143         }
144
145         rs->sr_err = mdb_cursor_open( txn, mdb->mi_dn2id, &mcd );
146         if( rs->sr_err != 0 ) {
147                 Debug( LDAP_DEBUG_TRACE,
148                         LDAP_XSTRING(mdb_add) ": mdb_cursor_open failed (%d)\n",
149                         rs->sr_err, 0, 0 );
150                 rs->sr_err = LDAP_OTHER;
151                 rs->sr_text = "internal error";
152                 goto return_results;
153         }
154
155         /* get entry or parent */
156         rs->sr_err = mdb_dn2entry( op, txn, mcd, &op->ora_e->e_nname, &p, NULL, 1 );
157         switch( rs->sr_err ) {
158         case 0:
159                 rs->sr_err = LDAP_ALREADY_EXISTS;
160                 mdb_entry_return( op, p );
161                 p = NULL;
162                 goto return_results;
163         case MDB_NOTFOUND:
164                 break;
165         case LDAP_BUSY:
166                 rs->sr_text = "ldap server busy";
167                 goto return_results;
168         default:
169                 rs->sr_err = LDAP_OTHER;
170                 rs->sr_text = "internal error";
171                 goto return_results;
172         }
173
174         if ( !p )
175                 p = (Entry *)&slap_entry_root;
176
177         if ( !bvmatch( &pdn, &p->e_nname ) ) {
178                 rs->sr_matched = ber_strdup_x( p->e_name.bv_val,
179                         op->o_tmpmemctx );
180                 if ( p != (Entry *)&slap_entry_root && is_entry_referral( p )) {
181                         BerVarray ref = get_entry_referrals( op, p );
182                         rs->sr_ref = referral_rewrite( ref, &p->e_name,
183                                 &op->o_req_dn, LDAP_SCOPE_DEFAULT );
184                         ber_bvarray_free( ref );
185                 } else {
186                         rs->sr_ref = NULL;
187                 }
188                 if ( p != (Entry *)&slap_entry_root )
189                         mdb_entry_return( op, p );
190                 p = NULL;
191                 Debug( LDAP_DEBUG_TRACE,
192                         LDAP_XSTRING(mdb_add) ": parent "
193                         "does not exist\n", 0, 0, 0 );
194
195                 rs->sr_err = LDAP_REFERRAL;
196                 rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
197                 goto return_results;
198         }
199
200         rs->sr_err = access_allowed( op, p,
201                 children, NULL, ACL_WADD, NULL );
202
203         if ( ! rs->sr_err ) {
204                 if ( p != (Entry *)&slap_entry_root )
205                         mdb_entry_return( op, p );
206                 p = NULL;
207
208                 Debug( LDAP_DEBUG_TRACE,
209                         LDAP_XSTRING(mdb_add) ": no write access to parent\n",
210                         0, 0, 0 );
211                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
212                 rs->sr_text = "no write access to parent";
213                 goto return_results;;
214         }
215
216         if ( p != (Entry *)&slap_entry_root ) {
217                 if ( is_entry_subentry( p ) ) {
218                         mdb_entry_return( op, p );
219                         p = NULL;
220                         /* parent is a subentry, don't allow add */
221                         Debug( LDAP_DEBUG_TRACE,
222                                 LDAP_XSTRING(mdb_add) ": parent is subentry\n",
223                                 0, 0, 0 );
224                         rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
225                         rs->sr_text = "parent is a subentry";
226                         goto return_results;;
227                 }
228
229                 if ( is_entry_alias( p ) ) {
230                         mdb_entry_return( op, p );
231                         p = NULL;
232                         /* parent is an alias, don't allow add */
233                         Debug( LDAP_DEBUG_TRACE,
234                                 LDAP_XSTRING(mdb_add) ": parent is alias\n",
235                                 0, 0, 0 );
236                         rs->sr_err = LDAP_ALIAS_PROBLEM;
237                         rs->sr_text = "parent is an alias";
238                         goto return_results;;
239                 }
240
241                 if ( is_entry_referral( p ) ) {
242                         BerVarray ref = get_entry_referrals( op, p );
243                         /* parent is a referral, don't allow add */
244                         rs->sr_matched = ber_strdup_x( p->e_name.bv_val,
245                                 op->o_tmpmemctx );
246                         rs->sr_ref = referral_rewrite( ref, &p->e_name,
247                                 &op->o_req_dn, LDAP_SCOPE_DEFAULT );
248                         ber_bvarray_free( ref );
249                         mdb_entry_return( op, p );
250                         p = NULL;
251                         Debug( LDAP_DEBUG_TRACE,
252                                 LDAP_XSTRING(mdb_add) ": parent is referral\n",
253                                 0, 0, 0 );
254
255                         rs->sr_err = LDAP_REFERRAL;
256                         rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
257                         goto return_results;
258                 }
259
260         }
261
262         if ( subentry ) {
263                 /* FIXME: */
264                 /* parent must be an administrative point of the required kind */
265         }
266
267         /* free parent */
268         if ( p != (Entry *)&slap_entry_root ) {
269                 pid = p->e_id;
270                 if ( p->e_nname.bv_len ) {
271                         struct berval ppdn;
272
273                         /* ITS#5326: use parent's DN if differs from provided one */
274                         dnParent( &op->ora_e->e_name, &ppdn );
275                         if ( !dn_match( &p->e_name, &ppdn ) ) {
276                                 struct berval rdn;
277                                 struct berval newdn;
278
279                                 dnRdn( &op->ora_e->e_name, &rdn );
280
281                                 build_new_dn( &newdn, &p->e_name, &rdn, NULL ); 
282                                 if ( op->ora_e->e_name.bv_val != op->o_req_dn.bv_val )
283                                         ber_memfree( op->ora_e->e_name.bv_val );
284                                 op->ora_e->e_name = newdn;
285
286                                 /* FIXME: should check whether
287                                  * dnNormalize(newdn) == e->e_nname ... */
288                         }
289                 }
290
291                 mdb_entry_return( op, p );
292         }
293         p = NULL;
294
295         rs->sr_err = access_allowed( op, op->ora_e,
296                 entry, NULL, ACL_WADD, NULL );
297
298         if ( ! rs->sr_err ) {
299                 Debug( LDAP_DEBUG_TRACE,
300                         LDAP_XSTRING(mdb_add) ": no write access to entry\n",
301                         0, 0, 0 );
302                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
303                 rs->sr_text = "no write access to entry";
304                 goto return_results;;
305         }
306
307         /* 
308          * Check ACL for attribute write access
309          */
310         if (!acl_check_modlist(op, oe, op->ora_modlist)) {
311                 Debug( LDAP_DEBUG_TRACE,
312                         LDAP_XSTRING(mdb_add) ": no write access to attribute\n",
313                         0, 0, 0 );
314                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
315                 rs->sr_text = "no write access to attribute";
316                 goto return_results;;
317         }
318
319         rs->sr_err = mdb_cursor_open( txn, mdb->mi_id2entry, &mc );
320         if( rs->sr_err != 0 ) {
321                 Debug( LDAP_DEBUG_TRACE,
322                         LDAP_XSTRING(mdb_add) ": mdb_cursor_open failed (%d)\n",
323                         rs->sr_err, 0, 0 );
324                 rs->sr_err = LDAP_OTHER;
325                 rs->sr_text = "internal error";
326                 goto return_results;
327         }
328
329         rs->sr_err = mdb_next_id( op->o_bd, mc, &eid );
330         if( rs->sr_err != 0 ) {
331                 Debug( LDAP_DEBUG_TRACE,
332                         LDAP_XSTRING(mdb_add) ": next_id failed (%d)\n",
333                         rs->sr_err, 0, 0 );
334                 rs->sr_err = LDAP_OTHER;
335                 rs->sr_text = "internal error";
336                 goto return_results;
337         }
338         op->ora_e->e_id = eid;
339
340         /* dn2id index */
341         rs->sr_err = mdb_dn2id_add( op, mcd, mcd, pid, 1, 1, op->ora_e );
342         mdb_cursor_close( mcd );
343         if ( rs->sr_err != 0 ) {
344                 Debug( LDAP_DEBUG_TRACE,
345                         LDAP_XSTRING(mdb_add) ": dn2id_add failed: %s (%d)\n",
346                         mdb_strerror(rs->sr_err), rs->sr_err, 0 );
347
348                 switch( rs->sr_err ) {
349                 case MDB_KEYEXIST:
350                         rs->sr_err = LDAP_ALREADY_EXISTS;
351                         break;
352                 default:
353                         rs->sr_err = LDAP_OTHER;
354                 }
355                 goto return_results;
356         }
357
358         /* attribute indexes */
359         rs->sr_err = mdb_index_entry_add( op, txn, op->ora_e );
360         if ( rs->sr_err != LDAP_SUCCESS ) {
361                 Debug( LDAP_DEBUG_TRACE,
362                         LDAP_XSTRING(mdb_add) ": index_entry_add failed\n",
363                         0, 0, 0 );
364                 rs->sr_err = LDAP_OTHER;
365                 rs->sr_text = "index generation failed";
366                 goto return_results;
367         }
368
369         /* id2entry index */
370         rs->sr_err = mdb_id2entry_add( op, txn, mc, op->ora_e );
371         if ( rs->sr_err != 0 ) {
372                 Debug( LDAP_DEBUG_TRACE,
373                         LDAP_XSTRING(mdb_add) ": id2entry_add failed\n",
374                         0, 0, 0 );
375                 rs->sr_err = LDAP_OTHER;
376                 rs->sr_text = "entry store failed";
377                 goto return_results;
378         }
379
380         /* post-read */
381         if( op->o_postread ) {
382                 if( postread_ctrl == NULL ) {
383                         postread_ctrl = &ctrls[num_ctrls++];
384                         ctrls[num_ctrls] = NULL;
385                 }
386                 if ( slap_read_controls( op, rs, op->ora_e,
387                         &slap_post_read_bv, postread_ctrl ) )
388                 {
389                         Debug( LDAP_DEBUG_TRACE,
390                                 "<=- " LDAP_XSTRING(mdb_add) ": post-read "
391                                 "failed!\n", 0, 0, 0 );
392                         if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
393                                 /* FIXME: is it correct to abort
394                                  * operation if control fails? */
395                                 goto return_results;
396                         }
397                 }
398         }
399
400         if ( moi == &opinfo ) {
401                 LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.moi_oe, OpExtra, oe_next );
402                 opinfo.moi_oe.oe_key = NULL;
403                 if ( op->o_noop ) {
404                         mdb_txn_abort( txn );
405                         rs->sr_err = LDAP_X_NO_OPERATION;
406                         txn = NULL;
407                         goto return_results;
408                 }
409
410                 rs->sr_err = mdb_txn_commit( txn );
411                 txn = NULL;
412                 if ( rs->sr_err != 0 ) {
413                         rs->sr_text = "txn_commit failed";
414                         Debug( LDAP_DEBUG_ANY,
415                                 LDAP_XSTRING(mdb_add) ": %s : %s (%d)\n",
416                                 rs->sr_text, mdb_strerror(rs->sr_err), rs->sr_err );
417                         rs->sr_err = LDAP_OTHER;
418                         goto return_results;
419                 }
420         }
421
422         Debug(LDAP_DEBUG_TRACE,
423                 LDAP_XSTRING(mdb_add) ": added%s id=%08lx dn=\"%s\"\n",
424                 op->o_noop ? " (no-op)" : "",
425                 op->ora_e->e_id, op->ora_e->e_dn );
426
427         rs->sr_text = NULL;
428         if( num_ctrls ) rs->sr_ctrls = ctrls;
429
430 return_results:
431         success = rs->sr_err;
432         send_ldap_result( op, rs );
433
434         if( moi == &opinfo ) {
435                 if( txn != NULL ) {
436                         mdb_txn_abort( txn );
437                 }
438                 if ( opinfo.moi_oe.oe_key ) {
439                         LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.moi_oe, OpExtra, oe_next );
440                 }
441         } else {
442                 moi->moi_ref--;
443         }
444
445         if( success == LDAP_SUCCESS ) {
446 #if 0
447                 if ( mdb->bi_txn_cp_kbyte ) {
448                         TXN_CHECKPOINT( mdb->bi_dbenv,
449                                 mdb->bi_txn_cp_kbyte, mdb->bi_txn_cp_min, 0 );
450                 }
451 #endif
452         }
453
454         slap_graduate_commit_csn( op );
455
456         if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) {
457                 slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
458                 slap_sl_free( *postread_ctrl, op->o_tmpmemctx );
459         }
460         return rs->sr_err;
461 }