modify.c 17.8 KB
Newer Older
1
2
/* modify.c - bdb backend modify routine */
/* $OpenLDAP$ */
Kurt Zeilenga's avatar
Notices    
Kurt Zeilenga committed
3
4
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
 *
Kurt Zeilenga's avatar
Kurt Zeilenga committed
5
 * Copyright 2000-2008 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 <ac/time.h>

#include "back-bdb.h"

25
26
27
28
29
static struct berval scbva[] = {
	BER_BVC("glue"),
	BER_BVNULL
};

30
31
32
33
34
int bdb_modify_internal(
	Operation *op,
	DB_TXN *tid,
	Modifications *modlist,
	Entry *e,
35
36
37
	const char **text,
	char *textbuf,
	size_t textlen )
38
39
40
41
42
{
	int rc, err;
	Modification	*mod;
	Modifications	*ml;
	Attribute	*save_attrs;
43
	Attribute 	*ap;
44
	int			glue_attr_delete = 0;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
45
46
	int			got_delete;
	AttrInfo *ai;
47

Kurt Zeilenga's avatar
Kurt Zeilenga committed
48
49
	Debug( LDAP_DEBUG_TRACE, "bdb_modify_internal: 0x%08lx: %s\n",
		e->e_id, e->e_dn, 0);
50

51
	if ( !acl_check_modlist( op, e, modlist )) {
52
53
54
		return LDAP_INSUFFICIENT_ACCESS;
	}

Howard Chu's avatar
Howard Chu committed
55
	/* save_attrs will be disposed of by bdb_cache_modify */
56
57
58
	save_attrs = e->e_attrs;
	e->e_attrs = attrs_dup( e->e_attrs );

59
	for ( ml = modlist; ml != NULL; ml = ml->sml_next ) {
60
		int match;
61
62
63
64
65
		mod = &ml->sml_mod;
		switch( mod->sm_op ) {
		case LDAP_MOD_ADD:
		case LDAP_MOD_REPLACE:
			if ( mod->sm_desc == slap_schema.si_ad_structuralObjectClass ) {
66
				value_match( &match, slap_schema.si_ad_structuralObjectClass,
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
67
68
69
70
71
					slap_schema.si_ad_structuralObjectClass->
						ad_type->sat_equality,
					SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
					&mod->sm_values[0], &scbva[0], text );
				if ( !match ) glue_attr_delete = 1;
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
			}
		}
		if ( glue_attr_delete )
			break;
	}

	if ( glue_attr_delete ) {
		Attribute	**app = &e->e_attrs;
		while ( *app != NULL ) {
			if ( !is_at_operational( (*app)->a_desc->ad_type )) {
				Attribute *save = *app;
				*app = (*app)->a_next;
				attr_free( save );
				continue;
			}
			app = &(*app)->a_next;
		}
	}

91
	for ( ml = modlist; ml != NULL; ml = ml->sml_next ) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
92
		struct berval ix_at;
93
		mod = &ml->sml_mod;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
94
		got_delete = 0;
95
96
97

		switch ( mod->sm_op ) {
		case LDAP_MOD_ADD:
98
99
100
			Debug(LDAP_DEBUG_ARGS,
				"bdb_modify_internal: add %s\n",
				mod->sm_desc->ad_cname.bv_val, 0, 0);
101
			err = modify_add_values( e, mod, get_permissiveModify(op),
102
				text, textbuf, textlen );
103
104
105
106
107
108
109
			if( err != LDAP_SUCCESS ) {
				Debug(LDAP_DEBUG_ARGS, "bdb_modify_internal: %d %s\n",
					err, *text, 0);
			}
			break;

		case LDAP_MOD_DELETE:
110
111
			if ( glue_attr_delete ) {
				err = LDAP_SUCCESS;
112
				break;
113
114
			}

115
116
117
			Debug(LDAP_DEBUG_ARGS,
				"bdb_modify_internal: delete %s\n",
				mod->sm_desc->ad_cname.bv_val, 0, 0);
118
			err = modify_delete_values( e, mod, get_permissiveModify(op),
119
				text, textbuf, textlen );
120
121
122
123
			assert( err != LDAP_TYPE_OR_VALUE_EXISTS );
			if( err != LDAP_SUCCESS ) {
				Debug(LDAP_DEBUG_ARGS, "bdb_modify_internal: %d %s\n",
					err, *text, 0);
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
124
125
			} else {
				got_delete = 1;
126
127
128
129
			}
			break;

		case LDAP_MOD_REPLACE:
130
131
132
			Debug(LDAP_DEBUG_ARGS,
				"bdb_modify_internal: replace %s\n",
				mod->sm_desc->ad_cname.bv_val, 0, 0);
133
			err = modify_replace_values( e, mod, get_permissiveModify(op),
134
				text, textbuf, textlen );
135
136
137
			if( err != LDAP_SUCCESS ) {
				Debug(LDAP_DEBUG_ARGS, "bdb_modify_internal: %d %s\n",
					err, *text, 0);
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
138
139
			} else {
				got_delete = 1;
140
141
142
			}
			break;

143
144
		case LDAP_MOD_INCREMENT:
			Debug(LDAP_DEBUG_ARGS,
145
146
				"bdb_modify_internal: increment %s\n",
				mod->sm_desc->ad_cname.bv_val, 0, 0);
147
148
149
150
151
152
			err = modify_increment_values( e, mod, get_permissiveModify(op),
				text, textbuf, textlen );
			if( err != LDAP_SUCCESS ) {
				Debug(LDAP_DEBUG_ARGS,
					"bdb_modify_internal: %d %s\n",
					err, *text, 0);
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
153
154
			} else {
				got_delete = 1;
155
156
157
			}
			break;

158
		case SLAP_MOD_SOFTADD:
159
160
161
			Debug(LDAP_DEBUG_ARGS,
				"bdb_modify_internal: softadd %s\n",
				mod->sm_desc->ad_cname.bv_val, 0, 0);
162
163
164
165
166
 			/* Avoid problems in index_add_mods()
 			 * We need to add index if necessary.
 			 */
 			mod->sm_op = LDAP_MOD_ADD;

167
			err = modify_add_values( e, mod, get_permissiveModify(op),
168
				text, textbuf, textlen );
Howard Chu's avatar
Howard Chu committed
169
170
171

 			mod->sm_op = SLAP_MOD_SOFTADD;

172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
 			if ( err == LDAP_TYPE_OR_VALUE_EXISTS ) {
 				err = LDAP_SUCCESS;
 			}

			if( err != LDAP_SUCCESS ) {
				Debug(LDAP_DEBUG_ARGS, "bdb_modify_internal: %d %s\n",
					err, *text, 0);
			}
 			break;

		default:
			Debug(LDAP_DEBUG_ANY, "bdb_modify_internal: invalid op %d\n",
				mod->sm_op, 0, 0);
			*text = "Invalid modify operation";
			err = LDAP_OTHER;
			Debug(LDAP_DEBUG_ARGS, "bdb_modify_internal: %d %s\n",
				err, *text, 0);
		}

		if ( err != LDAP_SUCCESS ) {
			attrs_free( e->e_attrs );
			e->e_attrs = save_attrs;
			/* unlock entry, delete from cache */
			return err; 
		}
197

198
199
200
201
		/* If objectClass was modified, reset the flags */
		if ( mod->sm_desc == slap_schema.si_ad_objectClass ) {
			e->e_ocflags = 0;
		}
Howard Chu's avatar
Howard Chu committed
202

Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
203
		if ( glue_attr_delete ) e->e_ocflags = 0;
204

Howard Chu's avatar
Howard Chu committed
205
206
		/* check if modified attribute was indexed
		 * but not in case of NOOP... */
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
		ai = bdb_index_mask( op->o_bd, mod->sm_desc, &ix_at );
		if ( ai && !op->o_noop ) {
			if ( got_delete ) {
				struct berval ix2;

				ap = attr_find( save_attrs, mod->sm_desc );
				if ( ap ) ap->a_flags |= SLAP_ATTR_IXDEL;

				/* Find all other attrs that index to same slot */
				for ( ap = e->e_attrs; ap; ap=ap->a_next ) {
					ai = bdb_index_mask( op->o_bd, ap->a_desc, &ix2 );
					if ( ai && ix2.bv_val == ix_at.bv_val )
						ap->a_flags |= SLAP_ATTR_IXADD;
				}
			} else {
				ap = attr_find( e->e_attrs, mod->sm_desc );
				if ( ap ) ap->a_flags |= SLAP_ATTR_IXADD;
			}
Howard Chu's avatar
Howard Chu committed
225
		}
226
227
228
	}

	/* check that the entry still obeys the schema */
229
	rc = entry_schema_check( op, e, save_attrs, get_relax(op), 0,
230
		text, textbuf, textlen );
231
	if ( rc != LDAP_SUCCESS || op->o_noop ) {
232
		attrs_free( e->e_attrs );
233
234
		/* clear the indexing flags */
		for ( ap = save_attrs; ap != NULL; ap = ap->a_next ) {
Howard Chu's avatar
Howard Chu committed
235
			ap->a_flags &= ~(SLAP_ATTR_IXADD|SLAP_ATTR_IXDEL);
236
		}
237
		e->e_attrs = save_attrs;
238
239
240
241
242
243
244
245

		if ( rc != LDAP_SUCCESS ) {
			Debug( LDAP_DEBUG_ANY,
				"entry failed schema check: %s\n",
				*text, 0, 0 );
		}

		/* if NOOP then silently revert to saved attrs */
246
247
248
		return rc;
	}

249
	/* update the indices of the modified attributes */
Howard Chu's avatar
Howard Chu committed
250
251
252
253

	/* start with deleting the old index entries */
	for ( ap = save_attrs; ap != NULL; ap = ap->a_next ) {
		if ( ap->a_flags & SLAP_ATTR_IXDEL ) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
254
			struct berval *vals;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
255
			Attribute *a2;
Howard Chu's avatar
Howard Chu committed
256
			ap->a_flags &= ~SLAP_ATTR_IXDEL;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
			a2 = attr_find( e->e_attrs, ap->a_desc );
			if ( a2 ) {
				/* need to detect which values were deleted */
				int i, j;
				struct berval tmp;
				j = ap->a_numvals;
				for ( i=0; i<j; ) {
					rc = attr_valfind( a2, SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
						&ap->a_nvals[i], NULL, op->o_tmpmemctx );
					/* Move deleted values to end of array */
					if ( rc == LDAP_NO_SUCH_ATTRIBUTE ) {
						j--;
						if ( i != j ) {
							tmp = ap->a_nvals[j];
							ap->a_nvals[j] = ap->a_nvals[i];
							ap->a_nvals[i] = tmp;
							tmp = ap->a_vals[j];
							ap->a_vals[j] = ap->a_vals[i];
							ap->a_vals[i] = tmp;
						}
						continue;
					}
					i++;
				}
				vals = &ap->a_nvals[j];
			} else {
				/* attribute was completely deleted */
				vals = ap->a_nvals;
			}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
286
			if ( !BER_BVISNULL( vals )) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
287
288
289
290
291
292
293
294
295
296
297
				rc = bdb_index_values( op, tid, ap->a_desc,
					vals, e->e_id, SLAP_INDEX_DELETE_OP );
				if ( rc != LDAP_SUCCESS ) {
					attrs_free( e->e_attrs );
					e->e_attrs = save_attrs;
					Debug( LDAP_DEBUG_ANY,
						   "Attribute index delete failure",
						   0, 0, 0 );
					return rc;
				}
			}
298
		}
Howard Chu's avatar
Howard Chu committed
299
300
301
302
303
	}

	/* add the new index entries */
	for ( ap = e->e_attrs; ap != NULL; ap = ap->a_next ) {
		if (ap->a_flags & SLAP_ATTR_IXADD) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
304
			ap->a_flags &= ~SLAP_ATTR_IXADD;
Howard Chu's avatar
Howard Chu committed
305
306
307
			rc = bdb_index_values( op, tid, ap->a_desc,
				ap->a_nvals,
				e->e_id, SLAP_INDEX_ADD_OP );
308
309
310
311
			if ( rc != LDAP_SUCCESS ) {
				attrs_free( e->e_attrs );
				e->e_attrs = save_attrs;
				Debug( LDAP_DEBUG_ANY,
Howard Chu's avatar
Howard Chu committed
312
				       "Attribute index add failure",
313
				       0, 0, 0 );
Howard Chu's avatar
Howard Chu committed
314
				return rc;
315
316
			}
		}
317
	}
318
319
320
321
322
323

	return rc;
}


int
324
bdb_modify( Operation *op, SlapReply *rs )
325
{
326
	struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
327
	Entry		*e = NULL;
Howard Chu's avatar
Howard Chu committed
328
	EntryInfo	*ei = NULL;
329
	int		manageDSAit = get_manageDSAit( op );
330
331
	char textbuf[SLAP_TEXT_BUFLEN];
	size_t textlen = sizeof textbuf;
Howard Chu's avatar
Howard Chu committed
332
	DB_TXN	*ltid = NULL, *lt2;
333
	struct bdb_op_info opinfo = {0};
334
	Entry		dummy = {0};
335
	int			fakeroot = 0;
336

337
	BDB_LOCKER	locker = 0;
338
339
	DB_LOCK		lock;

340
341
	int		num_retries = 0;

342
343
	LDAPControl **preread_ctrl = NULL;
	LDAPControl **postread_ctrl = NULL;
344
345
346
	LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
	int num_ctrls = 0;

Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
347
	int rc;
348

Kurt Zeilenga's avatar
Kurt Zeilenga committed
349
350
351
352
#ifdef LDAP_X_TXN
	int settle = 0;
#endif

353
	Debug( LDAP_DEBUG_ARGS, LDAP_XSTRING(bdb_modify) ": %s\n",
354
		op->o_req_dn.bv_val, 0, 0 );
355

Kurt Zeilenga's avatar
Kurt Zeilenga committed
356
#ifdef LDAP_X_TXN
Kurt Zeilenga's avatar
Kurt Zeilenga committed
357
358
359
	if( op->o_txnSpec ) {
		/* acquire connection lock */
		ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
360
		if( op->o_conn->c_txn == CONN_TXN_INACTIVE ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
361
362
363
			rs->sr_text = "invalid transaction identifier";
			rs->sr_err = LDAP_X_TXN_ID_INVALID;
			goto txnReturn;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
364
365
366
		} else if( op->o_conn->c_txn == CONN_TXN_SETTLE ) {
			settle=1;
			goto txnReturn;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
367
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
368

Kurt Zeilenga's avatar
Kurt Zeilenga committed
369
370
371
372
373
374
375
376
377
378
379
380
		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";
381
		rs->sr_err = LDAP_X_TXN_SPECIFY_OKAY;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
382
383
384
385
386

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

Kurt Zeilenga's avatar
Kurt Zeilenga committed
387
388
389
390
		if( !settle ) {
			send_ldap_result( op, rs );
			return rs->sr_err;
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
391
	}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
392
#endif
Kurt Zeilenga's avatar
Kurt Zeilenga committed
393

394
395
	ctrls[num_ctrls] = NULL;

396
	slap_mods_opattrs( op, &op->orm_modlist, 1 );
Howard Chu's avatar
Howard Chu committed
397

398
	if( 0 ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
399
retry:	/* transaction retry */
400
401
402
403
		if ( dummy.e_attrs ) {
			attrs_free( dummy.e_attrs );
			dummy.e_attrs = NULL;
		}
404
		if( e != NULL ) {
405
			bdb_unlocked_cache_return_entry_w(&bdb->bi_cache, e);
Howard Chu's avatar
Howard Chu committed
406
			e = NULL;
407
		}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
408
		Debug(LDAP_DEBUG_TRACE,
409
			LDAP_XSTRING(bdb_modify) ": retrying...\n", 0, 0, 0);
410

411
		rs->sr_err = TXN_ABORT( ltid );
412
413
		ltid = NULL;
		op->o_private = NULL;
414
		op->o_do_not_cache = opinfo.boi_acl_cache;
415
416
417
		if( rs->sr_err != 0 ) {
			rs->sr_err = LDAP_OTHER;
			rs->sr_text = "internal error";
418
419
			goto return_results;
		}
420
421
		if ( op->o_abandon ) {
			rs->sr_err = SLAPD_ABANDON;
422
			goto return_results;
423
		}
Howard Chu's avatar
Howard Chu committed
424
		bdb_trans_backoff( ++num_retries );
425
426
	}

427
	/* begin transaction */
428
	rs->sr_err = TXN_BEGIN( bdb->bi_dbenv, NULL, &ltid, 
429
		bdb->bi_db_opflags );
430
431
	rs->sr_text = NULL;
	if( rs->sr_err != 0 ) {
432
		Debug( LDAP_DEBUG_TRACE,
433
434
			LDAP_XSTRING(bdb_modify) ": txn_begin failed: "
			"%s (%d)\n", db_strerror(rs->sr_err), rs->sr_err, 0 );
435
436
		rs->sr_err = LDAP_OTHER;
		rs->sr_text = "internal error";
437
		goto return_results;
438
439
	}

440
441
	locker = TXN_ID ( ltid );

442
	opinfo.boi_bdb = op->o_bd;
443
444
	opinfo.boi_txn = ltid;
	opinfo.boi_err = 0;
445
	opinfo.boi_acl_cache = op->o_do_not_cache;
446
447
	op->o_private = &opinfo;

Howard Chu's avatar
Howard Chu committed
448
	/* get entry or ancestor */
449
450
	rs->sr_err = bdb_dn2entry( op, ltid, &op->o_req_ndn, &ei, 1,
		locker, &lock );
451

452
	if ( rs->sr_err != 0 ) {
453
		Debug( LDAP_DEBUG_TRACE,
454
			LDAP_XSTRING(bdb_modify) ": dn2entry failed (%d)\n",
455
456
			rs->sr_err, 0, 0 );
		switch( rs->sr_err ) {
457
458
459
460
		case DB_LOCK_DEADLOCK:
		case DB_LOCK_NOTGRANTED:
			goto retry;
		case DB_NOTFOUND:
461
			if ( BER_BVISEMPTY( &op->o_req_ndn )) {
462
				struct berval gluebv = BER_BVC("glue");
463
464
465
				e = ch_calloc( 1, sizeof(Entry));
				e->e_name.bv_val = ch_strdup( "" );
				ber_dupbv( &e->e_nname, &e->e_name );
466
467
				attr_merge_one( e, slap_schema.si_ad_objectClass,
					&gluebv, NULL );
468
				attr_merge_one( e, slap_schema.si_ad_structuralObjectClass,
469
					&gluebv, NULL );
Howard Chu's avatar
Howard Chu committed
470
				e->e_private = ei;
471
472
473
				fakeroot = 1;
				rs->sr_err = 0;
			}
474
			break;
Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
475
		case LDAP_BUSY:
476
			rs->sr_text = "ldap server busy";
Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
477
			goto return_results;
478
		default:
479
			rs->sr_err = LDAP_OTHER;
480
481
			rs->sr_text = "internal error";
			goto return_results;
482
483
484
		}
	}

485
486
487
	if ( !fakeroot ) {
		e = ei->bei_e;
	}
488

489
	/* acquire and lock entry */
490
	/* FIXME: dn2entry() should return non-glue entry */
491
492
493
	if (( rs->sr_err == DB_NOTFOUND ) ||
		( !manageDSAit && e && is_entry_glue( e )))
	{
Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
494
		if ( e != NULL ) {
Howard Chu's avatar
Howard Chu committed
495
496
497
			rs->sr_matched = ch_strdup( e->e_dn );
			rs->sr_ref = is_entry_referral( e )
				? get_entry_referrals( op, e )
498
				: NULL;
Howard Chu's avatar
Howard Chu committed
499
500
			bdb_unlocked_cache_return_entry_r (&bdb->bi_cache, e);
			e = NULL;
501
502

		} else {
503
			rs->sr_ref = referral_rewrite( default_referral, NULL,
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
504
				&op->o_req_dn, LDAP_SCOPE_DEFAULT );
505
506
		}

507
508
		rs->sr_err = LDAP_REFERRAL;
		send_ldap_result( op, rs );
509

510
		if ( rs->sr_ref != default_referral ) {
511
512
			ber_bvarray_free( rs->sr_ref );
		}
513
514
515
		free( (char *)rs->sr_matched );
		rs->sr_ref = NULL;
		rs->sr_matched = NULL;
516

517
		goto done;
518
519
520
	}

	if ( !manageDSAit && is_entry_referral( e ) ) {
521
		/* entry is a referral, don't allow modify */
522
		rs->sr_ref = get_entry_referrals( op, e );
523
524

		Debug( LDAP_DEBUG_TRACE,
525
			LDAP_XSTRING(bdb_modify) ": entry is referral\n",
526
527
			0, 0, 0 );

528
529
530
		rs->sr_err = LDAP_REFERRAL;
		rs->sr_matched = e->e_name.bv_val;
		send_ldap_result( op, rs );
531

532
533
534
		ber_bvarray_free( rs->sr_ref );
		rs->sr_ref = NULL;
		rs->sr_matched = NULL;
535
536
		goto done;
	}
537

538
539
540
541
542
543
544
	if ( get_assert( op ) &&
		( test_filter( op, e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
	{
		rs->sr_err = LDAP_ASSERTION_FAILED;
		goto return_results;
	}

545
	if( op->o_preread ) {
546
547
548
549
		if( preread_ctrl == NULL ) {
			preread_ctrl = &ctrls[num_ctrls++];
			ctrls[num_ctrls] = NULL;
		}
550
		if ( slap_read_controls( op, rs, e,
551
			&slap_pre_read_bv, preread_ctrl ) )
552
553
		{
			Debug( LDAP_DEBUG_TRACE,
554
555
556
557
558
559
560
				"<=- " LDAP_XSTRING(bdb_modify) ": pre-read "
				"failed!\n", 0, 0, 0 );
			if ( op->o_preread & SLAP_CONTROL_CRITICAL ) {
				/* FIXME: is it correct to abort
				 * operation if control fails? */
				goto return_results;
			}
561
562
563
		}
	}

Howard Chu's avatar
Howard Chu committed
564
	/* nested transaction */
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
565
	rs->sr_err = TXN_BEGIN( bdb->bi_dbenv, ltid, &lt2, bdb->bi_db_opflags );
Howard Chu's avatar
Howard Chu committed
566
567
568
	rs->sr_text = NULL;
	if( rs->sr_err != 0 ) {
		Debug( LDAP_DEBUG_TRACE,
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
569
			LDAP_XSTRING(bdb_modify) ": txn_begin(2) failed: " "%s (%d)\n",
Howard Chu's avatar
Howard Chu committed
570
571
572
573
574
			db_strerror(rs->sr_err), rs->sr_err, 0 );
		rs->sr_err = LDAP_OTHER;
		rs->sr_text = "internal error";
		goto return_results;
	}
575
	/* Modify the entry */
Howard Chu's avatar
Howard Chu committed
576
	dummy = *e;
577
	rs->sr_err = bdb_modify_internal( op, lt2, op->orm_modlist,
Howard Chu's avatar
Howard Chu committed
578
		&dummy, &rs->sr_text, textbuf, textlen );
579

580
	if( rs->sr_err != LDAP_SUCCESS ) {
581
		Debug( LDAP_DEBUG_TRACE,
582
			LDAP_XSTRING(bdb_modify) ": modify failed (%d)\n",
583
584
585
			rs->sr_err, 0, 0 );
		if ( (rs->sr_err == LDAP_INSUFFICIENT_ACCESS) && opinfo.boi_err ) {
			rs->sr_err = opinfo.boi_err;
586
		}
587
588
		/* Only free attrs if they were dup'd.  */
		if ( dummy.e_attrs == e->e_attrs ) dummy.e_attrs = NULL;
589
		switch( rs->sr_err ) {
590
591
592
593
594
595
596
597
		case DB_LOCK_DEADLOCK:
		case DB_LOCK_NOTGRANTED:
			goto retry;
		}
		goto return_results;
	}

	/* change the entry itself */
Howard Chu's avatar
Howard Chu committed
598
	rs->sr_err = bdb_id2entry_update( op->o_bd, lt2, &dummy );
599
	if ( rs->sr_err != 0 ) {
600
		Debug( LDAP_DEBUG_TRACE,
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
601
602
			LDAP_XSTRING(bdb_modify) ": id2entry update failed " "(%d)\n",
			rs->sr_err, 0, 0 );
603
		switch( rs->sr_err ) {
604
605
606
607
		case DB_LOCK_DEADLOCK:
		case DB_LOCK_NOTGRANTED:
			goto retry;
		}
608
		rs->sr_text = "entry update failed";
609
610
		goto return_results;
	}
611

Howard Chu's avatar
Howard Chu committed
612
613
614
615
616
	if ( TXN_COMMIT( lt2, 0 ) != 0 ) {
		rs->sr_err = LDAP_OTHER;
		rs->sr_text = "txn_commit(2) failed";
		goto return_results;
	}
617

618
	if( op->o_postread ) {
619
620
621
622
		if( postread_ctrl == NULL ) {
			postread_ctrl = &ctrls[num_ctrls++];
			ctrls[num_ctrls] = NULL;
		}
623
		if( slap_read_controls( op, rs, &dummy,
624
			&slap_post_read_bv, postread_ctrl ) )
625
626
		{
			Debug( LDAP_DEBUG_TRACE,
627
628
				"<=- " LDAP_XSTRING(bdb_modify)
				": post-read failed!\n", 0, 0, 0 );
629
630
631
632
633
			if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
				/* FIXME: is it correct to abort
				 * operation if control fails? */
				goto return_results;
			}
634
635
636
		}
	}

637
	if( op->o_noop ) {
638
639
		if ( ( rs->sr_err = TXN_ABORT( ltid ) ) != 0 ) {
			rs->sr_text = "txn_abort (no-op) failed";
640
		} else {
641
			rs->sr_err = LDAP_X_NO_OPERATION;
642
			ltid = NULL;
643
644
			/* Only free attrs if they were dup'd.  */
			if ( dummy.e_attrs == e->e_attrs ) dummy.e_attrs = NULL;
645
			goto return_results;
646
		}
647
	} else {
648
649
		/* may have changed in bdb_modify_internal() */
		e->e_ocflags = dummy.e_ocflags;
650
		if ( fakeroot ) {
Howard Chu's avatar
Howard Chu committed
651
			e->e_private = NULL;
652
			entry_free( e );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
653
			e = NULL;
654
			attrs_free( dummy.e_attrs );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
655

656
		} else {
Howard Chu's avatar
Howard Chu committed
657
			rc = bdb_cache_modify( bdb, e, dummy.e_attrs, locker, &lock );
658
659
660
661
662
			switch( rc ) {
			case DB_LOCK_DEADLOCK:
			case DB_LOCK_NOTGRANTED:
				goto retry;
			}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
663
		}
664
		dummy.e_attrs = NULL;
Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
665

666
		rs->sr_err = TXN_COMMIT( ltid, 0 );
667
	}
668
669
670
	ltid = NULL;
	op->o_private = NULL;

671
	if( rs->sr_err != 0 ) {
672
		Debug( LDAP_DEBUG_TRACE,
673
			LDAP_XSTRING(bdb_modify) ": txn_%s failed: %s (%d)\n",
674
			op->o_noop ? "abort (no-op)" : "commit",
675
676
677
			db_strerror(rs->sr_err), rs->sr_err );
		rs->sr_err = LDAP_OTHER;
		rs->sr_text = "commit failed";
678

679
680
681
682
		goto return_results;
	}

	Debug( LDAP_DEBUG_TRACE,
683
		LDAP_XSTRING(bdb_modify) ": updated%s id=%08lx dn=\"%s\"\n",
684
		op->o_noop ? " (no-op)" : "",
685
		dummy.e_id, op->o_req_dn.bv_val );
686
687
688
689

	rs->sr_err = LDAP_SUCCESS;
	rs->sr_text = NULL;
	if( num_ctrls ) rs->sr_ctrls = ctrls;
690
691

return_results:
692
693
694
	if( dummy.e_attrs ) {
		attrs_free( dummy.e_attrs );
	}
695
	send_ldap_result( op, rs );
696

697
	if( rs->sr_err == LDAP_SUCCESS && bdb->bi_txn_cp_kbyte ) {
698
		TXN_CHECKPOINT( bdb->bi_dbenv,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
699
700
701
			bdb->bi_txn_cp_kbyte, bdb->bi_txn_cp_min, 0 );
	}

702
done:
703
704
	slap_graduate_commit_csn( op );

705
	if( ltid != NULL ) {
706
		TXN_ABORT( ltid );
707
	}
708
	op->o_private = NULL;
709

710
	if( e != NULL ) {
711
		bdb_unlocked_cache_return_entry_w (&bdb->bi_cache, e);
712
	}
713

714
	if( preread_ctrl != NULL && (*preread_ctrl) != NULL ) {
715
716
		slap_sl_free( (*preread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
		slap_sl_free( *preread_ctrl, op->o_tmpmemctx );
717
	}
718
	if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) {
719
720
		slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
		slap_sl_free( *postread_ctrl, op->o_tmpmemctx );
721
	}
722
723
724

	rs->sr_text = NULL;

725
	return rs->sr_err;
726
}