1/* add.c - ldap BerkeleyDB back-end add routine */
2/* $OpenLDAP$ */
3/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 *
5 * Copyright 2000-2011 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-bdb.h"
23
24int
25bdb_add(Operation *op, SlapReply *rs )
26{
27	struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
28	struct berval	pdn;
29	Entry		*p = NULL, *oe = op->ora_e;
30	EntryInfo	*ei;
31	char textbuf[SLAP_TEXT_BUFLEN];
32	size_t textlen = sizeof textbuf;
33	AttributeDescription *children = slap_schema.si_ad_children;
34	AttributeDescription *entry = slap_schema.si_ad_entry;
35	DB_TXN		*ltid = NULL, *lt2;
36	ID eid = NOID;
37	struct bdb_op_info opinfo = {{{ 0 }}};
38	int subentry;
39	DB_LOCK		lock;
40
41	int		num_retries = 0;
42	int		success;
43
44	LDAPControl **postread_ctrl = NULL;
45	LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
46	int num_ctrls = 0;
47
48#ifdef LDAP_X_TXN
49	int settle = 0;
50#endif
51
52	Debug(LDAP_DEBUG_ARGS, "==> " LDAP_XSTRING(bdb_add) ": %s\n",
53		op->ora_e->e_name.bv_val, 0, 0);
54
55#ifdef LDAP_X_TXN
56	if( op->o_txnSpec ) {
57		/* acquire connection lock */
58		ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
59		if( op->o_conn->c_txn == CONN_TXN_INACTIVE ) {
60			rs->sr_text = "invalid transaction identifier";
61			rs->sr_err = LDAP_X_TXN_ID_INVALID;
62			goto txnReturn;
63		} else if( op->o_conn->c_txn == CONN_TXN_SETTLE ) {
64			settle=1;
65			goto txnReturn;
66		}
67
68		if( op->o_conn->c_txn_backend == NULL ) {
69			op->o_conn->c_txn_backend = op->o_bd;
70
71		} else if( op->o_conn->c_txn_backend != op->o_bd ) {
72			rs->sr_text = "transaction cannot span multiple database contexts";
73			rs->sr_err = LDAP_AFFECTS_MULTIPLE_DSAS;
74			goto txnReturn;
75		}
76
77		/* insert operation into transaction */
78
79		rs->sr_text = "transaction specified";
80		rs->sr_err = LDAP_X_TXN_SPECIFY_OKAY;
81
82txnReturn:
83		/* release connection lock */
84		ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
85
86		if( !settle ) {
87			send_ldap_result( op, rs );
88			return rs->sr_err;
89		}
90	}
91#endif
92
93	ctrls[num_ctrls] = 0;
94
95	/* check entry's schema */
96	rs->sr_err = entry_schema_check( op, op->ora_e, NULL,
97		get_relax(op), 1, NULL, &rs->sr_text, textbuf, textlen );
98	if ( rs->sr_err != LDAP_SUCCESS ) {
99		Debug( LDAP_DEBUG_TRACE,
100			LDAP_XSTRING(bdb_add) ": entry failed schema check: "
101			"%s (%d)\n", rs->sr_text, rs->sr_err, 0 );
102		goto return_results;
103	}
104
105	/* add opattrs to shadow as well, only missing attrs will actually
106	 * be added; helps compatibility with older OL versions */
107	rs->sr_err = slap_add_opattrs( op, &rs->sr_text, textbuf, textlen, 1 );
108	if ( rs->sr_err != LDAP_SUCCESS ) {
109		Debug( LDAP_DEBUG_TRACE,
110			LDAP_XSTRING(bdb_add) ": entry failed op attrs add: "
111			"%s (%d)\n", rs->sr_text, rs->sr_err, 0 );
112		goto return_results;
113	}
114
115	if ( get_assert( op ) &&
116		( test_filter( op, op->ora_e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
117	{
118		rs->sr_err = LDAP_ASSERTION_FAILED;
119		goto return_results;
120	}
121
122	subentry = is_entry_subentry( op->ora_e );
123
124	if( 0 ) {
125retry:	/* transaction retry */
126		if( p ) {
127			/* free parent and reader lock */
128			if ( p != (Entry *)&slap_entry_root ) {
129				bdb_unlocked_cache_return_entry_r( bdb, p );
130			}
131			p = NULL;
132		}
133		rs->sr_err = TXN_ABORT( ltid );
134		ltid = NULL;
135		LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.boi_oe, OpExtra, oe_next );
136		opinfo.boi_oe.oe_key = NULL;
137		op->o_do_not_cache = opinfo.boi_acl_cache;
138		if( rs->sr_err != 0 ) {
139			rs->sr_err = LDAP_OTHER;
140			rs->sr_text = "internal error";
141			goto return_results;
142		}
143		if ( op->o_abandon ) {
144			rs->sr_err = SLAPD_ABANDON;
145			goto return_results;
146		}
147		bdb_trans_backoff( ++num_retries );
148	}
149
150	/* begin transaction */
151	rs->sr_err = TXN_BEGIN( bdb->bi_dbenv, NULL, &ltid,
152		bdb->bi_db_opflags );
153	rs->sr_text = NULL;
154	if( rs->sr_err != 0 ) {
155		Debug( LDAP_DEBUG_TRACE,
156			LDAP_XSTRING(bdb_add) ": txn_begin failed: %s (%d)\n",
157			db_strerror(rs->sr_err), rs->sr_err, 0 );
158		rs->sr_err = LDAP_OTHER;
159		rs->sr_text = "internal error";
160		goto return_results;
161	}
162
163	opinfo.boi_oe.oe_key = bdb;
164	opinfo.boi_txn = ltid;
165	opinfo.boi_err = 0;
166	opinfo.boi_acl_cache = op->o_do_not_cache;
167	LDAP_SLIST_INSERT_HEAD( &op->o_extra, &opinfo.boi_oe, oe_next );
168
169	/*
170	 * Get the parent dn and see if the corresponding entry exists.
171	 */
172	if ( be_issuffix( op->o_bd, &op->ora_e->e_nname ) ) {
173		pdn = slap_empty_bv;
174	} else {
175		dnParent( &op->ora_e->e_nname, &pdn );
176	}
177
178	/* get entry or parent */
179	rs->sr_err = bdb_dn2entry( op, ltid, &op->ora_e->e_nname, &ei,
180		1, &lock );
181	switch( rs->sr_err ) {
182	case 0:
183		rs->sr_err = LDAP_ALREADY_EXISTS;
184		goto return_results;
185	case DB_NOTFOUND:
186		break;
187	case DB_LOCK_DEADLOCK:
188	case DB_LOCK_NOTGRANTED:
189		goto retry;
190	case LDAP_BUSY:
191		rs->sr_text = "ldap server busy";
192		goto return_results;
193	default:
194		rs->sr_err = LDAP_OTHER;
195		rs->sr_text = "internal error";
196		goto return_results;
197	}
198
199	p = ei->bei_e;
200	if ( !p )
201		p = (Entry *)&slap_entry_root;
202
203	if ( !bvmatch( &pdn, &p->e_nname ) ) {
204		rs->sr_matched = ber_strdup_x( p->e_name.bv_val,
205			op->o_tmpmemctx );
206		rs->sr_ref = is_entry_referral( p )
207			? get_entry_referrals( op, p )
208			: NULL;
209		if ( p != (Entry *)&slap_entry_root )
210			bdb_unlocked_cache_return_entry_r( bdb, p );
211		p = NULL;
212		Debug( LDAP_DEBUG_TRACE,
213			LDAP_XSTRING(bdb_add) ": parent "
214			"does not exist\n", 0, 0, 0 );
215
216		rs->sr_err = LDAP_REFERRAL;
217		rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
218		goto return_results;
219	}
220
221	rs->sr_err = access_allowed( op, p,
222		children, NULL, ACL_WADD, NULL );
223
224	if ( ! rs->sr_err ) {
225		switch( opinfo.boi_err ) {
226		case DB_LOCK_DEADLOCK:
227		case DB_LOCK_NOTGRANTED:
228			goto retry;
229		}
230
231		if ( p != (Entry *)&slap_entry_root )
232			bdb_unlocked_cache_return_entry_r( bdb, p );
233		p = NULL;
234
235		Debug( LDAP_DEBUG_TRACE,
236			LDAP_XSTRING(bdb_add) ": no write access to parent\n",
237			0, 0, 0 );
238		rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
239		rs->sr_text = "no write access to parent";
240		goto return_results;;
241	}
242
243	if ( p != (Entry *)&slap_entry_root ) {
244		if ( is_entry_subentry( p ) ) {
245			bdb_unlocked_cache_return_entry_r( bdb, p );
246			p = NULL;
247			/* parent is a subentry, don't allow add */
248			Debug( LDAP_DEBUG_TRACE,
249				LDAP_XSTRING(bdb_add) ": parent is subentry\n",
250				0, 0, 0 );
251			rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
252			rs->sr_text = "parent is a subentry";
253			goto return_results;;
254		}
255
256		if ( is_entry_alias( p ) ) {
257			bdb_unlocked_cache_return_entry_r( bdb, p );
258			p = NULL;
259			/* parent is an alias, don't allow add */
260			Debug( LDAP_DEBUG_TRACE,
261				LDAP_XSTRING(bdb_add) ": parent is alias\n",
262				0, 0, 0 );
263			rs->sr_err = LDAP_ALIAS_PROBLEM;
264			rs->sr_text = "parent is an alias";
265			goto return_results;;
266		}
267
268		if ( is_entry_referral( p ) ) {
269			/* parent is a referral, don't allow add */
270			rs->sr_matched = ber_strdup_x( p->e_name.bv_val,
271				op->o_tmpmemctx );
272			rs->sr_ref = get_entry_referrals( op, p );
273			bdb_unlocked_cache_return_entry_r( bdb, p );
274			p = NULL;
275			Debug( LDAP_DEBUG_TRACE,
276				LDAP_XSTRING(bdb_add) ": parent is referral\n",
277				0, 0, 0 );
278
279			rs->sr_err = LDAP_REFERRAL;
280			rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
281			goto return_results;
282		}
283
284	}
285
286	if ( subentry ) {
287		/* FIXME: */
288		/* parent must be an administrative point of the required kind */
289	}
290
291	/* free parent and reader lock */
292	if ( p != (Entry *)&slap_entry_root ) {
293		if ( p->e_nname.bv_len ) {
294			struct berval ppdn;
295
296			/* ITS#5326: use parent's DN if differs from provided one */
297			dnParent( &op->ora_e->e_name, &ppdn );
298			if ( !dn_match( &p->e_name, &ppdn ) ) {
299				struct berval rdn;
300				struct berval newdn;
301
302				dnRdn( &op->ora_e->e_name, &rdn );
303
304				build_new_dn( &newdn, &p->e_name, &rdn, NULL );
305				if ( op->ora_e->e_name.bv_val != op->o_req_dn.bv_val )
306					ber_memfree( op->ora_e->e_name.bv_val );
307				op->ora_e->e_name = newdn;
308
309				/* FIXME: should check whether
310				 * dnNormalize(newdn) == e->e_nname ... */
311			}
312		}
313
314		bdb_unlocked_cache_return_entry_r( bdb, p );
315	}
316	p = NULL;
317
318	rs->sr_err = access_allowed( op, op->ora_e,
319		entry, NULL, ACL_WADD, NULL );
320
321	if ( ! rs->sr_err ) {
322		switch( opinfo.boi_err ) {
323		case DB_LOCK_DEADLOCK:
324		case DB_LOCK_NOTGRANTED:
325			goto retry;
326		}
327
328		Debug( LDAP_DEBUG_TRACE,
329			LDAP_XSTRING(bdb_add) ": no write access to entry\n",
330			0, 0, 0 );
331		rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
332		rs->sr_text = "no write access to entry";
333		goto return_results;;
334	}
335
336	/*
337	 * Check ACL for attribute write access
338	 */
339	if (!acl_check_modlist(op, oe, op->ora_modlist)) {
340		switch( opinfo.boi_err ) {
341		case DB_LOCK_DEADLOCK:
342		case DB_LOCK_NOTGRANTED:
343			goto retry;
344		}
345
346		Debug( LDAP_DEBUG_TRACE,
347			LDAP_XSTRING(bdb_add) ": no write access to attribute\n",
348			0, 0, 0 );
349		rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
350		rs->sr_text = "no write access to attribute";
351		goto return_results;;
352	}
353
354	if ( eid == NOID ) {
355		rs->sr_err = bdb_next_id( op->o_bd, &eid );
356		if( rs->sr_err != 0 ) {
357			Debug( LDAP_DEBUG_TRACE,
358				LDAP_XSTRING(bdb_add) ": next_id failed (%d)\n",
359				rs->sr_err, 0, 0 );
360			rs->sr_err = LDAP_OTHER;
361			rs->sr_text = "internal error";
362			goto return_results;
363		}
364		op->ora_e->e_id = eid;
365	}
366
367	/* nested transaction */
368	rs->sr_err = TXN_BEGIN( bdb->bi_dbenv, ltid, &lt2,
369		bdb->bi_db_opflags );
370	rs->sr_text = NULL;
371	if( rs->sr_err != 0 ) {
372		Debug( LDAP_DEBUG_TRACE,
373			LDAP_XSTRING(bdb_add) ": txn_begin(2) failed: "
374			"%s (%d)\n", db_strerror(rs->sr_err), rs->sr_err, 0 );
375		rs->sr_err = LDAP_OTHER;
376		rs->sr_text = "internal error";
377		goto return_results;
378	}
379
380	/* dn2id index */
381	rs->sr_err = bdb_dn2id_add( op, lt2, ei, op->ora_e );
382	if ( rs->sr_err != 0 ) {
383		Debug( LDAP_DEBUG_TRACE,
384			LDAP_XSTRING(bdb_add) ": dn2id_add failed: %s (%d)\n",
385			db_strerror(rs->sr_err), rs->sr_err, 0 );
386
387		switch( rs->sr_err ) {
388		case DB_LOCK_DEADLOCK:
389		case DB_LOCK_NOTGRANTED:
390			goto retry;
391		case DB_KEYEXIST:
392			rs->sr_err = LDAP_ALREADY_EXISTS;
393			break;
394		default:
395			rs->sr_err = LDAP_OTHER;
396		}
397		goto return_results;
398	}
399
400	/* attribute indexes */
401	rs->sr_err = bdb_index_entry_add( op, lt2, op->ora_e );
402	if ( rs->sr_err != LDAP_SUCCESS ) {
403		Debug( LDAP_DEBUG_TRACE,
404			LDAP_XSTRING(bdb_add) ": index_entry_add failed\n",
405			0, 0, 0 );
406		switch( rs->sr_err ) {
407		case DB_LOCK_DEADLOCK:
408		case DB_LOCK_NOTGRANTED:
409			goto retry;
410		default:
411			rs->sr_err = LDAP_OTHER;
412		}
413		rs->sr_text = "index generation failed";
414		goto return_results;
415	}
416
417	/* id2entry index */
418	rs->sr_err = bdb_id2entry_add( op->o_bd, lt2, op->ora_e );
419	if ( rs->sr_err != 0 ) {
420		Debug( LDAP_DEBUG_TRACE,
421			LDAP_XSTRING(bdb_add) ": id2entry_add failed\n",
422			0, 0, 0 );
423		switch( rs->sr_err ) {
424		case DB_LOCK_DEADLOCK:
425		case DB_LOCK_NOTGRANTED:
426			goto retry;
427		default:
428			rs->sr_err = LDAP_OTHER;
429		}
430		rs->sr_text = "entry store failed";
431		goto return_results;
432	}
433
434	if ( TXN_COMMIT( lt2, 0 ) != 0 ) {
435		rs->sr_err = LDAP_OTHER;
436		rs->sr_text = "txn_commit(2) failed";
437		goto return_results;
438	}
439
440	/* post-read */
441	if( op->o_postread ) {
442		if( postread_ctrl == NULL ) {
443			postread_ctrl = &ctrls[num_ctrls++];
444			ctrls[num_ctrls] = NULL;
445		}
446		if ( slap_read_controls( op, rs, op->ora_e,
447			&slap_post_read_bv, postread_ctrl ) )
448		{
449			Debug( LDAP_DEBUG_TRACE,
450				"<=- " LDAP_XSTRING(bdb_add) ": post-read "
451				"failed!\n", 0, 0, 0 );
452			if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
453				/* FIXME: is it correct to abort
454				 * operation if control fails? */
455				goto return_results;
456			}
457		}
458	}
459
460	if ( op->o_noop ) {
461		if (( rs->sr_err=TXN_ABORT( ltid )) != 0 ) {
462			rs->sr_text = "txn_abort (no-op) failed";
463		} else {
464			rs->sr_err = LDAP_X_NO_OPERATION;
465			ltid = NULL;
466			goto return_results;
467		}
468
469	} else {
470		struct berval nrdn;
471
472		/* pick the RDN if not suffix; otherwise pick the entire DN */
473		if (pdn.bv_len) {
474			nrdn.bv_val = op->ora_e->e_nname.bv_val;
475			nrdn.bv_len = pdn.bv_val - op->ora_e->e_nname.bv_val - 1;
476		} else {
477			nrdn = op->ora_e->e_nname;
478		}
479
480		bdb_cache_add( bdb, ei, op->ora_e, &nrdn, ltid, &lock );
481
482		if(( rs->sr_err=TXN_COMMIT( ltid, 0 )) != 0 ) {
483			rs->sr_text = "txn_commit failed";
484		} else {
485			rs->sr_err = LDAP_SUCCESS;
486		}
487	}
488
489	ltid = NULL;
490	LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.boi_oe, OpExtra, oe_next );
491	opinfo.boi_oe.oe_key = NULL;
492
493	if ( rs->sr_err != LDAP_SUCCESS ) {
494		Debug( LDAP_DEBUG_TRACE,
495			LDAP_XSTRING(bdb_add) ": %s : %s (%d)\n",
496			rs->sr_text, db_strerror(rs->sr_err), rs->sr_err );
497		rs->sr_err = LDAP_OTHER;
498		goto return_results;
499	}
500
501	Debug(LDAP_DEBUG_TRACE,
502		LDAP_XSTRING(bdb_add) ": added%s id=%08lx dn=\"%s\"\n",
503		op->o_noop ? " (no-op)" : "",
504		op->ora_e->e_id, op->ora_e->e_dn );
505
506	rs->sr_text = NULL;
507	if( num_ctrls ) rs->sr_ctrls = ctrls;
508
509return_results:
510	success = rs->sr_err;
511	send_ldap_result( op, rs );
512
513	if( ltid != NULL ) {
514		TXN_ABORT( ltid );
515	}
516	if ( opinfo.boi_oe.oe_key ) {
517		LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.boi_oe, OpExtra, oe_next );
518	}
519
520	if( success == LDAP_SUCCESS ) {
521		/* We own the entry now, and it can be purged at will
522		 * Check to make sure it's the same entry we entered with.
523		 * Possibly a callback may have mucked with it, although
524		 * in general callbacks should treat the entry as read-only.
525		 */
526		bdb_cache_deref( oe->e_private );
527		if ( op->ora_e == oe )
528			op->ora_e = NULL;
529
530		if ( bdb->bi_txn_cp_kbyte ) {
531			TXN_CHECKPOINT( bdb->bi_dbenv,
532				bdb->bi_txn_cp_kbyte, bdb->bi_txn_cp_min, 0 );
533		}
534	}
535
536	slap_graduate_commit_csn( op );
537
538	if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) {
539		slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
540		slap_sl_free( *postread_ctrl, op->o_tmpmemctx );
541	}
542	return rs->sr_err;
543}
544