add.c 13 KB
Newer Older
1
2
/* add.c - ldap BerkeleyDB back-end add routine */
/* $OpenLDAP$ */
Kurt Zeilenga's avatar
Notices    
Kurt Zeilenga committed
3
4
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
 *
5
 * Copyright 2000-2009 The OpenLDAP Foundation.
Kurt Zeilenga's avatar
Notices    
Kurt Zeilenga committed
6
7
8
9
10
11
12
13
14
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted only as authorized by the OpenLDAP
 * Public License.
 *
 * A copy of this license is available in the file LICENSE in the
 * top-level directory of the distribution or, alternatively, at
 * <http://www.OpenLDAP.org/license.html>.
15
16
17
18
19
20
21
22
23
24
 */

#include "portable.h"

#include <stdio.h>
#include <ac/string.h>

#include "back-bdb.h"

int
25
bdb_add(Operation *op, SlapReply *rs )
26
{
27
	struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
Howard Chu's avatar
Howard Chu committed
28
	struct berval	pdn;
29
	Entry		*p = NULL, *oe = op->ora_e;
Howard Chu's avatar
Howard Chu committed
30
	EntryInfo	*ei;
31
32
	char textbuf[SLAP_TEXT_BUFLEN];
	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, *rtxn;
36
	ID eid = NOID;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
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
46
47
	LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
	int num_ctrls = 0;

Kurt Zeilenga's avatar
Kurt Zeilenga committed
48
49
50
51
#ifdef LDAP_X_TXN
	int settle = 0;
#endif

52
	Debug(LDAP_DEBUG_ARGS, "==> " LDAP_XSTRING(bdb_add) ": %s\n",
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
53
		op->oq_add.rs_e->e_name.bv_val, 0, 0);
54

Kurt Zeilenga's avatar
Kurt Zeilenga committed
55
#ifdef LDAP_X_TXN
Kurt Zeilenga's avatar
Kurt Zeilenga committed
56
57
58
	if( op->o_txnSpec ) {
		/* acquire connection lock */
		ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
59
		if( op->o_conn->c_txn == CONN_TXN_INACTIVE ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
60
61
62
			rs->sr_text = "invalid transaction identifier";
			rs->sr_err = LDAP_X_TXN_ID_INVALID;
			goto txnReturn;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
63
64
65
		} else if( op->o_conn->c_txn == CONN_TXN_SETTLE ) {
			settle=1;
			goto txnReturn;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
66
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
67

Kurt Zeilenga's avatar
Kurt Zeilenga committed
68
69
70
71
72
73
74
75
76
77
78
79
		if( op->o_conn->c_txn_backend == NULL ) {
			op->o_conn->c_txn_backend = op->o_bd;

		} else if( op->o_conn->c_txn_backend != op->o_bd ) {
			rs->sr_text = "transaction cannot span multiple database contexts";
			rs->sr_err = LDAP_AFFECTS_MULTIPLE_DSAS;
			goto txnReturn;
		}

		/* insert operation into transaction */

		rs->sr_text = "transaction specified";
80
		rs->sr_err = LDAP_X_TXN_SPECIFY_OKAY;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
81
82
83
84
85

txnReturn:
		/* release connection lock */
		ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );

Kurt Zeilenga's avatar
Kurt Zeilenga committed
86
87
88
89
		if( !settle ) {
			send_ldap_result( op, rs );
			return rs->sr_err;
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
90
	}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
91
#endif
Kurt Zeilenga's avatar
Kurt Zeilenga committed
92

93
94
	ctrls[num_ctrls] = 0;

95
96
	/* check entry's schema */
	rs->sr_err = entry_schema_check( op, op->oq_add.rs_e, NULL,
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
97
		get_relax(op), 1, NULL, &rs->sr_text, textbuf, textlen );
98
99
	if ( rs->sr_err != LDAP_SUCCESS ) {
		Debug( LDAP_DEBUG_TRACE,
100
			LDAP_XSTRING(bdb_add) ": entry failed schema check: "
101
102
103
			"%s (%d)\n", rs->sr_text, rs->sr_err, 0 );
		goto return_results;
	}
Howard Chu's avatar
Howard Chu committed
104

105
106
107
	/* add opattrs to shadow as well, only missing attrs will actually
	 * be added; helps compatibility with older OL versions */
	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
113
114
		goto return_results;
	}

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
115
116
117
118
119
120
121
	if ( get_assert( op ) &&
		( test_filter( op, op->ora_e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
	{
		rs->sr_err = LDAP_ASSERTION_FAILED;
		goto return_results;
	}

122
	subentry = is_entry_subentry( op->oq_add.rs_e );
123

124
125
	/* Get our reader TXN */
	rs->sr_err = bdb_reader_get( op, bdb->bi_dbenv, &rtxn );
126

127
	if( 0 ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
128
retry:	/* transaction retry */
129
130
		if( p ) {
			/* free parent and reader lock */
131
			if ( p != (Entry *)&slap_entry_root ) {
Howard Chu's avatar
Howard Chu committed
132
				bdb_unlocked_cache_return_entry_r( bdb, p );
133
			}
134
135
			p = NULL;
		}
136
		rs->sr_err = TXN_ABORT( ltid );
137
		ltid = NULL;
138
139
		LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.boi_oe, OpExtra, oe_next );
		opinfo.boi_oe.oe_key = NULL;
140
		op->o_do_not_cache = opinfo.boi_acl_cache;
141
142
143
		if( rs->sr_err != 0 ) {
			rs->sr_err = LDAP_OTHER;
			rs->sr_text = "internal error";
144
145
			goto return_results;
		}
146
147
		if ( op->o_abandon ) {
			rs->sr_err = SLAPD_ABANDON;
148
			goto return_results;
149
		}
Howard Chu's avatar
Howard Chu committed
150
		bdb_trans_backoff( ++num_retries );
151
152
	}

153
	/* begin transaction */
154
	rs->sr_err = TXN_BEGIN( bdb->bi_dbenv, NULL, &ltid, 
155
		bdb->bi_db_opflags );
156
157
	rs->sr_text = NULL;
	if( rs->sr_err != 0 ) {
158
		Debug( LDAP_DEBUG_TRACE,
159
			LDAP_XSTRING(bdb_add) ": txn_begin failed: %s (%d)\n",
160
161
162
			db_strerror(rs->sr_err), rs->sr_err, 0 );
		rs->sr_err = LDAP_OTHER;
		rs->sr_text = "internal error";
163
164
		goto return_results;
	}
165

166
	opinfo.boi_oe.oe_key = bdb;
167
168
	opinfo.boi_txn = ltid;
	opinfo.boi_err = 0;
169
	opinfo.boi_acl_cache = op->o_do_not_cache;
170
171
	LDAP_SLIST_INSERT_HEAD( &op->o_extra, &opinfo.boi_oe, oe_next );

172
173
174
	/*
	 * Get the parent dn and see if the corresponding entry exists.
	 */
175
	if ( be_issuffix( op->o_bd, &op->oq_add.rs_e->e_nname ) ) {
176
		pdn = slap_empty_bv;
177
	} else {
178
		dnParent( &op->oq_add.rs_e->e_nname, &pdn );
179
	}
180

Howard Chu's avatar
Howard Chu committed
181
	/* get entry or parent */
182
	rs->sr_err = bdb_dn2entry( op, ltid, &op->ora_e->e_nname, &ei,
183
		1, &lock );
Howard Chu's avatar
Howard Chu committed
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
	switch( rs->sr_err ) {
	case 0:
		rs->sr_err = LDAP_ALREADY_EXISTS;
		goto return_results;
	case DB_NOTFOUND:
		break;
	case DB_LOCK_DEADLOCK:
	case DB_LOCK_NOTGRANTED:
		goto retry;
	case LDAP_BUSY:
		rs->sr_text = "ldap server busy";
		goto return_results;
	default:
		rs->sr_err = LDAP_OTHER;
		rs->sr_text = "internal error";
		goto return_results;
	}
201

Howard Chu's avatar
Howard Chu committed
202
	p = ei->bei_e;
203
204
205
206
207
208
209
210
211
	if ( !p )
		p = (Entry *)&slap_entry_root;

	if ( !bvmatch( &pdn, &p->e_nname ) ) {
		rs->sr_matched = ber_strdup_x( p->e_name.bv_val,
			op->o_tmpmemctx );
		rs->sr_ref = is_entry_referral( p )
			? get_entry_referrals( op, p )
			: NULL;
Howard Chu's avatar
Howard Chu committed
212
		bdb_unlocked_cache_return_entry_r( bdb, p );
213
214
215
216
		p = NULL;
		Debug( LDAP_DEBUG_TRACE,
			LDAP_XSTRING(bdb_add) ": parent "
			"does not exist\n", 0, 0, 0 );
217

218
219
220
221
		rs->sr_err = LDAP_REFERRAL;
		rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
		goto return_results;
	}
222

223
224
	rs->sr_err = access_allowed( op, p,
		children, NULL, ACL_WADD, NULL );
225

226
227
228
229
230
	if ( ! rs->sr_err ) {
		switch( opinfo.boi_err ) {
		case DB_LOCK_DEADLOCK:
		case DB_LOCK_NOTGRANTED:
			goto retry;
231
232
		}

233
234
235
236
237
238
239
		Debug( LDAP_DEBUG_TRACE,
			LDAP_XSTRING(bdb_add) ": no write access to parent\n",
			0, 0, 0 );
		rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
		rs->sr_text = "no write access to parent";
		goto return_results;;
	}
240

241
242
243
244
245
246
247
248
249
250
	if ( p != (Entry *)&slap_entry_root ) {
		if ( is_entry_subentry( p ) ) {
			/* parent is a subentry, don't allow add */
			Debug( LDAP_DEBUG_TRACE,
				LDAP_XSTRING(bdb_add) ": parent is subentry\n",
				0, 0, 0 );
			rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
			rs->sr_text = "parent is a subentry";
			goto return_results;;
		}
251

252
253
254
255
256
257
258
259
260
		if ( is_entry_alias( p ) ) {
			/* parent is an alias, don't allow add */
			Debug( LDAP_DEBUG_TRACE,
				LDAP_XSTRING(bdb_add) ": parent is alias\n",
				0, 0, 0 );
			rs->sr_err = LDAP_ALIAS_PROBLEM;
			rs->sr_text = "parent is an alias";
			goto return_results;;
		}
261

262
263
264
265
266
		if ( is_entry_referral( p ) ) {
			/* parent is a referral, don't allow add */
			rs->sr_matched = ber_strdup_x( p->e_name.bv_val,
				op->o_tmpmemctx );
			rs->sr_ref = get_entry_referrals( op, p );
Howard Chu's avatar
Howard Chu committed
267
			bdb_unlocked_cache_return_entry_r( bdb, p );
268
269
270
271
272
273
274
275
276
			p = NULL;
			Debug( LDAP_DEBUG_TRACE,
				LDAP_XSTRING(bdb_add) ": parent is referral\n",
				0, 0, 0 );

			rs->sr_err = LDAP_REFERRAL;
			rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
			goto return_results;
		}
277

Quanah Gibson-Mount's avatar
cleanup    
Quanah Gibson-Mount committed
278
279
280
281
282
	}

	if ( subentry ) {
		/* FIXME: */
		/* parent must be an administrative point of the required kind */
283
284
285
286
	}

	/* free parent and reader lock */
	if ( p != (Entry *)&slap_entry_root ) {
Howard Chu's avatar
Howard Chu committed
287
		bdb_unlocked_cache_return_entry_r( bdb, p );
288
	}
289
	p = NULL;
290

291
	rs->sr_err = access_allowed( op, op->oq_add.rs_e,
292
		entry, NULL, ACL_WADD, NULL );
293

294
	if ( ! rs->sr_err ) {
295
296
297
298
299
300
		switch( opinfo.boi_err ) {
		case DB_LOCK_DEADLOCK:
		case DB_LOCK_NOTGRANTED:
			goto retry;
		}

301
302
		Debug( LDAP_DEBUG_TRACE,
			LDAP_XSTRING(bdb_add) ": no write access to entry\n",
303
			0, 0, 0 );
304
305
		rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
		rs->sr_text = "no write access to entry";
306
307
308
		goto return_results;;
	}

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
	/* 
	 * Check ACL for attribute write access
	 */
	if (!acl_check_modlist(op, oe, op->ora_modlist)) {
		switch( opinfo.boi_err ) {
		case DB_LOCK_DEADLOCK:
		case DB_LOCK_NOTGRANTED:
			goto retry;
		}

		Debug( LDAP_DEBUG_TRACE,
			LDAP_XSTRING(bdb_add) ": no write access to attribute\n",
			0, 0, 0 );
		rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
		rs->sr_text = "no write access to attribute";
		goto return_results;;
	}

327
328
329
330
331
332
333
334
335
336
337
338
339
	if ( eid == NOID ) {
		rs->sr_err = bdb_next_id( op->o_bd, &eid );
		if( rs->sr_err != 0 ) {
			Debug( LDAP_DEBUG_TRACE,
				LDAP_XSTRING(bdb_add) ": next_id failed (%d)\n",
				rs->sr_err, 0, 0 );
			rs->sr_err = LDAP_OTHER;
			rs->sr_text = "internal error";
			goto return_results;
		}
		op->oq_add.rs_e->e_id = eid;
	}

Howard Chu's avatar
Howard Chu committed
340
341
342
343
344
345
	/* nested transaction */
	rs->sr_err = TXN_BEGIN( bdb->bi_dbenv, ltid, &lt2, 
		bdb->bi_db_opflags );
	rs->sr_text = NULL;
	if( rs->sr_err != 0 ) {
		Debug( LDAP_DEBUG_TRACE,
346
347
			LDAP_XSTRING(bdb_add) ": txn_begin(2) failed: "
			"%s (%d)\n", db_strerror(rs->sr_err), rs->sr_err, 0 );
Howard Chu's avatar
Howard Chu committed
348
349
350
351
352
		rs->sr_err = LDAP_OTHER;
		rs->sr_text = "internal error";
		goto return_results;
	}

353
	/* dn2id index */
354
	rs->sr_err = bdb_dn2id_add( op, lt2, ei, op->oq_add.rs_e );
355
	if ( rs->sr_err != 0 ) {
356
357
		Debug( LDAP_DEBUG_TRACE,
			LDAP_XSTRING(bdb_add) ": dn2id_add failed: %s (%d)\n",
358
			db_strerror(rs->sr_err), rs->sr_err, 0 );
359

360
		switch( rs->sr_err ) {
361
362
363
364
		case DB_LOCK_DEADLOCK:
		case DB_LOCK_NOTGRANTED:
			goto retry;
		case DB_KEYEXIST:
365
			rs->sr_err = LDAP_ALREADY_EXISTS;
366
367
			break;
		default:
368
			rs->sr_err = LDAP_OTHER;
369
370
371
372
		}
		goto return_results;
	}

373
374
375
	/* attribute indexes */
	rs->sr_err = bdb_index_entry_add( op, lt2, op->oq_add.rs_e );
	if ( rs->sr_err != LDAP_SUCCESS ) {
376
		Debug( LDAP_DEBUG_TRACE,
377
			LDAP_XSTRING(bdb_add) ": index_entry_add failed\n",
378
			0, 0, 0 );
379
		switch( rs->sr_err ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
380
381
382
383
		case DB_LOCK_DEADLOCK:
		case DB_LOCK_NOTGRANTED:
			goto retry;
		default:
384
			rs->sr_err = LDAP_OTHER;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
385
		}
386
		rs->sr_text = "index generation failed";
387
388
389
		goto return_results;
	}

390
391
392
	/* id2entry index */
	rs->sr_err = bdb_id2entry_add( op->o_bd, lt2, op->oq_add.rs_e );
	if ( rs->sr_err != 0 ) {
393
		Debug( LDAP_DEBUG_TRACE,
394
			LDAP_XSTRING(bdb_add) ": id2entry_add failed\n",
395
			0, 0, 0 );
396
		switch( rs->sr_err ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
397
398
399
400
		case DB_LOCK_DEADLOCK:
		case DB_LOCK_NOTGRANTED:
			goto retry;
		default:
401
			rs->sr_err = LDAP_OTHER;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
402
		}
403
		rs->sr_text = "entry store failed";
404
405
		goto return_results;
	}
406

Howard Chu's avatar
Howard Chu committed
407
408
409
410
411
	if ( TXN_COMMIT( lt2, 0 ) != 0 ) {
		rs->sr_err = LDAP_OTHER;
		rs->sr_text = "txn_commit(2) failed";
		goto return_results;
	}
412

413
414
	/* post-read */
	if( op->o_postread ) {
415
416
417
418
		if( postread_ctrl == NULL ) {
			postread_ctrl = &ctrls[num_ctrls++];
			ctrls[num_ctrls] = NULL;
		}
419
		if ( slap_read_controls( op, rs, op->oq_add.rs_e,
420
			&slap_post_read_bv, postread_ctrl ) )
421
422
		{
			Debug( LDAP_DEBUG_TRACE,
423
424
				"<=- " LDAP_XSTRING(bdb_add) ": post-read "
				"failed!\n", 0, 0, 0 );
425
426
427
428
429
			if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
				/* FIXME: is it correct to abort
				 * operation if control fails? */
				goto return_results;
			}
430
431
432
		}
	}

Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
433
	if ( op->o_noop ) {
434
435
		if (( rs->sr_err=TXN_ABORT( ltid )) != 0 ) {
			rs->sr_text = "txn_abort (no-op) failed";
436
		} else {
437
			rs->sr_err = LDAP_X_NO_OPERATION;
438
			ltid = NULL;
439
			goto return_results;
440
441
		}

442
	} else {
443
		struct berval nrdn;
444

Pierangelo Masarati's avatar
Pierangelo Masarati committed
445
446
		/* pick the RDN if not suffix; otherwise pick the entire DN */
		if (pdn.bv_len) {
447
			nrdn.bv_val = op->ora_e->e_nname.bv_val;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
448
449
			nrdn.bv_len = pdn.bv_val - op->ora_e->e_nname.bv_val - 1;
		} else {
450
			nrdn = op->ora_e->e_nname;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
451
		}
452

453
454
		/* Use the reader txn here, outside the add txn */
		bdb_cache_add( bdb, ei, op->ora_e, &nrdn, rtxn, &lock );
Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
455

456
457
458
459
		if(( rs->sr_err=TXN_COMMIT( ltid, 0 )) != 0 ) {
			rs->sr_text = "txn_commit failed";
		} else {
			rs->sr_err = LDAP_SUCCESS;
460
		}
461
	}
462

463
	ltid = NULL;
464
465
	LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.boi_oe, OpExtra, oe_next );
	opinfo.boi_oe.oe_key = NULL;
466

467
	if ( rs->sr_err != LDAP_SUCCESS ) {
468
469
		Debug( LDAP_DEBUG_TRACE,
			LDAP_XSTRING(bdb_add) ": %s : %s (%d)\n",
470
471
			rs->sr_text, db_strerror(rs->sr_err), rs->sr_err );
		rs->sr_err = LDAP_OTHER;
472
		goto return_results;
473
	}
474

475
476
	Debug(LDAP_DEBUG_TRACE,
		LDAP_XSTRING(bdb_add) ": added%s id=%08lx dn=\"%s\"\n",
477
478
479
480
481
482
		op->o_noop ? " (no-op)" : "",
		op->oq_add.rs_e->e_id, op->oq_add.rs_e->e_dn );

	rs->sr_text = NULL;
	if( num_ctrls ) rs->sr_ctrls = ctrls;

483
return_results:
484
	success = rs->sr_err;
485
	send_ldap_result( op, rs );
486
	slap_graduate_commit_csn( op );
487
488

	if( ltid != NULL ) {
489
		TXN_ABORT( ltid );
490
	}
491
492
493
	if ( opinfo.boi_oe.oe_key ) {
		LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.boi_oe, OpExtra, oe_next );
	}
494

495
	if( success == LDAP_SUCCESS ) {
496
497
498
499
500
		/* We own the entry now, and it can be purged at will
		 * Check to make sure it's the same entry we entered with.
		 * Possibly a callback may have mucked with it, although
		 * in general callbacks should treat the entry as read-only.
		 */
Howard Chu's avatar
Howard Chu committed
501
		bdb_cache_return_entry_r( bdb, oe, &lock );
502
503
504
505
506
507
508
		if ( op->ora_e == oe )
			op->ora_e = NULL;

		if ( bdb->bi_txn_cp_kbyte ) {
			TXN_CHECKPOINT( bdb->bi_dbenv,
				bdb->bi_txn_cp_kbyte, bdb->bi_txn_cp_min, 0 );
		}
Howard Chu's avatar
Howard Chu committed
509
	}
510
511
512
513
514
515

	if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) {
		slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
		slap_sl_free( *postread_ctrl, op->o_tmpmemctx );
	}

516
	return rs->sr_err;
517
}