Exemple #1
0
/* Modify context structure destructor */
int modify_term(modify_context *mc,struct backend *be)
{
    ldbm_instance *inst = (ldbm_instance *) be->be_instance_info;

    slapi_mods_free(&mc->smods);
    /* Unlock and return entries */
    if (mc->old_entry)  {
        cache_unlock_entry(&inst->inst_cache, mc->old_entry);
        CACHE_RETURN( &(inst->inst_cache), &(mc->old_entry) );
        mc->old_entry= NULL;
    }

    CACHE_RETURN(&(inst->inst_cache), &(mc->new_entry));
    mc->new_entry= NULL;
    return 0;
}
Exemple #2
0
/*
 * Switch the new with the old(original) - undoing modify_switch_entries()
 * This expects modify_term() to be called next, as the old "new" entry
 * is now gone(replaced by the original entry).
 */
int
modify_unswitch_entries(modify_context *mc,backend *be)
{
	struct backentry *tmp_be;
	ldbm_instance *inst = (ldbm_instance *) be->be_instance_info;
	int ret = 0;

	if (mc->old_entry && mc->new_entry && 
	    cache_is_in_cache(&inst->inst_cache, mc->new_entry)) {
		/* switch the entries, and reset the new, new, entry */
		tmp_be = mc->new_entry;
		mc->new_entry = mc->old_entry;
		mc->new_entry->ep_state = 0;
		if (cache_has_otherref(&(inst->inst_cache), mc->new_entry)) {
			/* some other thread refers the entry */
			CACHE_RETURN(&(inst->inst_cache), &(mc->new_entry));
		} else {
			/* don't call CACHE_RETURN, that frees the entry!  */
			mc->new_entry->ep_refcnt = 0;
		}
		mc->old_entry = tmp_be;

		ret = cache_replace(&(inst->inst_cache), mc->old_entry, mc->new_entry);
		if (ret == 0) {
			/*
			 * The new entry was originally locked, so since we did the
			 * switch we need to unlock the "new" entry, and return the
			 * "old" one.  modify_term() will then return the "new" entry.
			 */
			cache_unlock_entry(&inst->inst_cache, mc->new_entry);
			cache_lock_entry(&inst->inst_cache, mc->old_entry);
		} else {
			slapi_log_err(SLAPI_LOG_CACHE, "modify_unswitch_entries", "Replacing %s with %s failed (%d)\n",
			          slapi_entry_get_dn(mc->old_entry->ep_entry), 
			          slapi_entry_get_dn(mc->new_entry->ep_entry), ret);
		}
	}

	return ret;
}
Exemple #3
0
int
ldbm_back_bind( Slapi_PBlock *pb )
{
	backend *be;
	ldbm_instance *inst;
	int			method;
	struct berval		*cred;
	struct ldbminfo		*li;
	struct backentry	*e;
	Slapi_Attr		*attr;
	Slapi_Value **bvals;
	entry_address *addr;
	back_txn txn = {NULL};

	/* get parameters */
	slapi_pblock_get( pb, SLAPI_BACKEND, &be );
	slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &li );
	slapi_pblock_get( pb, SLAPI_TARGET_ADDRESS, &addr );
	slapi_pblock_get( pb, SLAPI_BIND_METHOD, &method );
	slapi_pblock_get( pb, SLAPI_BIND_CREDENTIALS, &cred );
	slapi_pblock_get( pb, SLAPI_TXN, &txn.back_txn_txn );

	if ( !txn.back_txn_txn ) {
		dblayer_txn_init( li, &txn );
		slapi_pblock_set( pb, SLAPI_TXN, txn.back_txn_txn );
	}
	
	inst = (ldbm_instance *) be->be_instance_info;

	/* always allow noauth simple binds (front end will send the result) */
	if ( method == LDAP_AUTH_SIMPLE && cred->bv_len == 0 ) {
		return( SLAPI_BIND_ANONYMOUS );
	}

	/*
	 * find the target entry.  find_entry() takes care of referrals
	 *   and sending errors if the entry does not exist.
	 */
	if (( e = find_entry( pb, be, addr, &txn )) == NULL ) {
		return( SLAPI_BIND_FAIL );
	}

	switch ( method ) {
	case LDAP_AUTH_SIMPLE:
		{
		Slapi_Value cv;
		if ( slapi_entry_attr_find( e->ep_entry, "userpassword", &attr ) != 0 ) {
#if defined( XP_WIN32 )
			if( WindowsAuthentication( e, cred ) == LDAPWA_Success ) {
				break;
			}
#endif
			slapi_send_ldap_result( pb, LDAP_INAPPROPRIATE_AUTH, NULL,
			    NULL, 0, NULL );
			CACHE_RETURN( &inst->inst_cache, &e );
			return( SLAPI_BIND_FAIL );
		}
		bvals= attr_get_present_values(attr);
		slapi_value_init_berval(&cv,cred);
		if ( slapi_pw_find_sv( bvals, &cv ) != 0 ) {
#if defined( XP_WIN32 )
			/* One last try - attempt Windows authentication, 
			   if the user has a Windows account. */
			if( WindowsAuthentication( e, cred ) == LDAPWA_Success ) {
				break;
			}
#endif
			slapi_send_ldap_result( pb, LDAP_INVALID_CREDENTIALS, NULL,
			    NULL, 0, NULL );
			CACHE_RETURN( &inst->inst_cache, &e );
			value_done(&cv);
			return( SLAPI_BIND_FAIL );
		}
		value_done(&cv);
		}
		break;

	default:
		slapi_send_ldap_result( pb, LDAP_STRONG_AUTH_NOT_SUPPORTED, NULL,
		    "auth method not supported", 0, NULL );
		CACHE_RETURN( &inst->inst_cache, &e );
		return( SLAPI_BIND_FAIL );
	}

	CACHE_RETURN( &inst->inst_cache, &e );

	/* success:  front end will send result */
	return( SLAPI_BIND_SUCCESS );
}
Exemple #4
0
/*
 * Access the database sequentially.
 * There are 4 ways to call this routine.  In each case, the equality index
 * for "attrname" is consulted:
 *  1) If the SLAPI_SEQ_TYPE parameter is SLAPI_SEQ_FIRST, then this routine
 *     will find the smallest key greater than or equal to the SLAPI_SEQ_VAL
 *     parameter, and return all entries that key's IDList.  If SLAPI_SEQ_VAL
 *     is NULL, then the smallest key is retrieved and the associaated
 *     entries are returned.
 *  2) If the SLAPI_SEQ_TYPE parameter is SLAPI_SEQ_NEXT, then this routine
 *     will find the smallest key strictly greater than the SLAPI_SEQ_VAL
 *     parameter, and return all entries that key's IDList.
 *  3) If the SLAPI_SEQ_TYPE parameter is SLAPI_SEQ_PREV, then this routine
 *     will find the greatest key strictly less than the SLAPI_SEQ_VAL
 *     parameter, and return all entries that key's IDList.
 *  4) If the SLAPI_SEQ_TYPE parameter is SLAPI_SEQ_LAST, then this routine 
 *     will find the largest equality key in the index and return all entries
 *     which match that key.  The SLAPI_SEQ_VAL parameter is ignored.
 */
int
ldbm_back_seq( Slapi_PBlock *pb )
{
	backend         *be;
	ldbm_instance   *inst;
	struct ldbminfo *li;
	IDList          *idl = NULL;
	back_txn        txn = {NULL};
	back_txnid parent_txn;
	struct attrinfo	*ai = NULL;
	DB              *db;
	DBC             *dbc = NULL;
	char *attrname, *val;
	int err = LDAP_SUCCESS;
	int return_value = -1;
	int nentries = 0;
	int retry_count = 0;
	int isroot;
	int type;

	/* Decode arguments */
	slapi_pblock_get( pb, SLAPI_BACKEND, &be);
	slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &li );
	slapi_pblock_get( pb, SLAPI_SEQ_TYPE, &type );
	slapi_pblock_get( pb, SLAPI_SEQ_ATTRNAME, &attrname );
	slapi_pblock_get( pb, SLAPI_SEQ_VAL, &val );
	slapi_pblock_get( pb, SLAPI_REQUESTOR_ISROOT, &isroot );
	slapi_pblock_get( pb, SLAPI_TXN, (void **)&parent_txn );

	inst = (ldbm_instance *) be->be_instance_info;

	dblayer_txn_init( li, &txn );
	if (!parent_txn) {
		parent_txn = txn.back_txn_txn;
		slapi_pblock_set( pb, SLAPI_TXN, parent_txn );
	}

	/* Validate arguments */
	if ( type != SLAPI_SEQ_FIRST &&
	     type != SLAPI_SEQ_LAST &&
	     type != SLAPI_SEQ_NEXT &&
	     type != SLAPI_SEQ_PREV )
	{
	    slapi_send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL,
		    "Bad seq access type", 0, NULL );
	    return( -1 );
	}

	/* get a database */

	ainfo_get( be, attrname, &ai );
	slapi_log_err(SLAPI_LOG_ARGS,
	    "ldbm_back_seq", "indextype: %s indexmask: 0x%x seek type: %d\n",
	    ai->ai_type, ai->ai_indexmask, type );
	if ( ! (INDEX_EQUALITY & ai->ai_indexmask) ) {
		slapi_log_err(SLAPI_LOG_TRACE,
		    "ldbm_back_seq", "caller specified un-indexed attribute %s\n",
			   attrname ? attrname : "");
		slapi_send_ldap_result( pb, LDAP_UNWILLING_TO_PERFORM, NULL,
		    "Unindexed seq access type", 0, NULL );
		return -1;
	}

	if ( (return_value = dblayer_get_index_file( be, ai, &db, DBOPEN_CREATE )) != 0 ) {
		slapi_log_err(SLAPI_LOG_ERR,
		    "ldbm_back_seq", "Could not open index file for attribute %s\n",
		    attrname);
		slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
		return -1;
	}
retry:
	if (txn.back_txn_txn) {
		dblayer_read_txn_begin(be, parent_txn, &txn);
	}
	/* First, get a database cursor */
	return_value = db->cursor(db, txn.back_txn_txn, &dbc, 0);

	if (0 == return_value)
	{
		DBT data = {0};
		DBT key = {0};
		char little_buffer[SEQ_LITTLE_BUFFER_SIZE];
		char *big_buffer = NULL;
		char keystring = EQ_PREFIX;

		/* Set data */
		data.flags = DB_DBT_MALLOC;

		/* Set up key */
		key.flags = DB_DBT_MALLOC;
		if (NULL == val)
		{
			/* this means, goto the first equality key */
			/* seek to key >= "=" */
			key.data = &keystring;
			key.size = 1;
		}
		else
		{
			size_t key_length = strlen(val) + 2;
			if (key_length <= SEQ_LITTLE_BUFFER_SIZE) {
				key.data = &little_buffer;
			} else {
				big_buffer = slapi_ch_malloc(key_length);
				if (NULL == big_buffer) {
					/* memory allocation failure */
					dblayer_release_index_file( be, ai, db );
					return -1;
				}
				key.data = big_buffer;
			}
			key.size = sprintf(key.data,"%c%s",EQ_PREFIX,val);
		}

		/* decide which type of operation we're being asked to do and do the db bit */
		/* The c_get call always mallocs memory for data.data */
		/* The c_get call mallocs memory for key.data, except for DB_SET */
		/* after this, we leave data containing the retrieved IDL, or NULL if we didn't get it */

		switch (type) {
		case SLAPI_SEQ_FIRST:
			/* if (NULL == val) goto the first equality key ( seek to key >= "=" ) */
			/* else goto the first equality key >= val ( seek to key >= "=val"  )*/
			return_value = dbc->c_get(dbc,&key,&data,DB_SET_RANGE);
			break;
		case SLAPI_SEQ_NEXT:
			/* seek to the indicated =value, then seek to the next entry, */
			return_value = dbc->c_get(dbc,&key,&data,DB_SET);
			if (0 == return_value)
			{
				slapi_ch_free(&(data.data));
				return_value = dbc->c_get(dbc,&key,&data,DB_NEXT);
			}
			else
			{
				/* DB_SET doesn't allocate key data.  Make sure we don't try to free it... */
				key.data= NULL;
			}
			break;
		case SLAPI_SEQ_PREV:
			/* seek to the indicated =value, then seek to the previous entry, */
			return_value = dbc->c_get(dbc,&key,&data,DB_SET);
			if (0 == return_value )
			{
				slapi_ch_free(&(data.data));
				return_value = dbc->c_get(dbc,&key,&data,DB_PREV);
			}
			else
			{
				/* DB_SET doesn't allocate key data.  Make sure we don't try to free it... */
				key.data= NULL;
			}
			break;
		case SLAPI_SEQ_LAST:
			/* seek to the first possible key after all the equality keys (">"), then seek back one */
			{
				keystring = EQ_PREFIX + 1;
				key.data = &keystring;
				key.size = 1;
				return_value = dbc->c_get(dbc,&key,&data,DB_SET_RANGE);
				if ((0 == return_value) || (DB_NOTFOUND == return_value)) {
					slapi_ch_free(&(data.data));
					return_value = dbc->c_get(dbc,&key,&data,DB_PREV); 
				}
			}
			break;
		}

		dbc->c_close(dbc);

		if ((0 == return_value) || (DB_NOTFOUND == return_value)) {
			/* Now check that the key we eventually settled on was an equality key ! */
			if (key.data && *((char*)key.data) == EQ_PREFIX) {
				/* Retrieve the idlist for this key */
				key.flags = 0;
				for (retry_count = 0; retry_count < IDL_FETCH_RETRY_COUNT; retry_count++) {
					err = NEW_IDL_DEFAULT;
					idl_free(&idl);
					idl = idl_fetch( be, db, &key, parent_txn, ai, &err );
					if(err == DB_LOCK_DEADLOCK) {
						ldbm_nasty("ldbm_back_seq", "deadlock retry", 1600, err);
						if (txn.back_txn_txn) {
							/* just in case */
							slapi_ch_free(&(data.data));
							if ((key.data != little_buffer) && (key.data != &keystring)) {
								slapi_ch_free(&(key.data));
							}
							dblayer_read_txn_abort(be, &txn);
							goto retry;
						} else {
							continue;
						}
					} else {
						break;
					}
				}
			}
		} else {
			if (txn.back_txn_txn) {
				dblayer_read_txn_abort(be, &txn);
			}
			if (DB_LOCK_DEADLOCK == return_value) {
				ldbm_nasty("ldbm_back_seq", "deadlock retry", 1601, err);
				/* just in case */
				slapi_ch_free(&(data.data));
				if ((key.data != little_buffer) && (key.data != &keystring)) {
					slapi_ch_free(&(key.data));
				}
				goto retry;
			}
		}
		if(retry_count == IDL_FETCH_RETRY_COUNT) {
			ldbm_nasty("ldbm_back_seq", "Retry count exceeded",1645,err);
		} else if ( err != 0 && err != DB_NOTFOUND ) {
			ldbm_nasty("ldbm_back_seq", "Database error", 1650, err);
		}
		slapi_ch_free( &(data.data) );
		if ( key.data != little_buffer && key.data != &keystring ) {
		    slapi_ch_free( &(key.data) );
		}
		slapi_ch_free_string( &big_buffer );
	}

	/* null idlist means there were no matching keys */
	if ( idl != NULL )
	{
		/*
		 * Step through the IDlist.  For each ID, get the entry
		 * and send it.
		 */
		ID id;
		struct backentry	*e;
		for ( id = idl_firstid( idl ); id != NOID;
			id = idl_nextid( idl, id ))
		{
		    if (( e = id2entry( be, id, &txn, &err )) == NULL )
		    {
				if ( err != LDAP_SUCCESS )
				{
				    slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_seq", "id2entry err %d\n", err);
				}
				slapi_log_err(SLAPI_LOG_ARGS,
					"ldbm_back_seq", "candidate %lu not found\n",
					(u_long)id);
				continue;
		    }
		    if ( slapi_send_ldap_search_entry( pb, e->ep_entry, NULL, NULL, 0 ) == 0 )
		    {
				nentries++;
		    }
		    CACHE_RETURN( &inst->inst_cache, &e );
		}
		idl_free( &idl );
	}
	/* if success finally commit the transaction, otherwise abort if DB_NOTFOUND */
	if(txn.back_txn_txn){
		if (return_value == 0) {
			dblayer_read_txn_commit(be, &txn);
		} else if (DB_NOTFOUND == return_value){
			dblayer_read_txn_abort(be, &txn);
		}
	}

	dblayer_release_index_file( be, ai, db );

	slapi_send_ldap_result( pb, LDAP_SUCCESS == err ? LDAP_SUCCESS : LDAP_OPERATIONS_ERROR, NULL, NULL, nentries, NULL );

	return 0;
}
Exemple #5
0
int
ldbm_back_modify( Slapi_PBlock *pb )
{
	backend *be;
	ldbm_instance *inst = NULL;
	struct ldbminfo		*li;
	struct backentry	*e = NULL, *ec = NULL;
	struct backentry	*original_entry = NULL, *tmpentry = NULL;
	Slapi_Entry		*postentry = NULL;
	LDAPMod			**mods = NULL;
	LDAPMod			**mods_original = NULL;
	Slapi_Mods smods = {0};
	back_txn txn;
	back_txnid		parent_txn;
	modify_context		ruv_c = {0};
	int			ruv_c_init = 0;
	int			retval = -1;
	char			*msg;
	char			*errbuf = NULL;
	int retry_count = 0;
	int disk_full = 0;
	int ldap_result_code= LDAP_SUCCESS;
	char *ldap_result_message= NULL;
	int rc = 0;
	Slapi_Operation *operation;
	entry_address *addr;
	int is_fixup_operation= 0;
	int is_ruv = 0;                 /* True if the current entry is RUV */
	CSN *opcsn = NULL;
	int repl_op;
	int opreturn = 0;
	int mod_count = 0;
	int not_an_error = 0;
	int fixup_tombstone = 0;
	int ec_locked = 0;
	int result_sent = 0;

	slapi_pblock_get( pb, SLAPI_BACKEND, &be);
	slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &li );
	slapi_pblock_get( pb, SLAPI_TARGET_ADDRESS, &addr );
	slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods );
	slapi_pblock_get( pb, SLAPI_TXN, (void**)&parent_txn );
	slapi_pblock_get( pb, SLAPI_IS_REPLICATED_OPERATION, &repl_op);
	slapi_pblock_get( pb, SLAPI_OPERATION, &operation );

	fixup_tombstone = operation_is_flag_set(operation, OP_FLAG_TOMBSTONE_FIXUP);

	dblayer_txn_init(li,&txn); /* must do this before first goto error_return */
	/* the calls to perform searches require the parent txn if any
	   so set txn to the parent_txn until we begin the child transaction */
	if (parent_txn) {
		txn.back_txn_txn = parent_txn;
	} else {
		parent_txn = txn.back_txn_txn;
		slapi_pblock_set( pb, SLAPI_TXN, parent_txn );
	}

	if (NULL == operation)
	{
		ldap_result_code = LDAP_OPERATIONS_ERROR;
		goto error_return;
	}

	is_fixup_operation = operation_is_flag_set(operation, OP_FLAG_REPL_FIXUP);
	is_ruv = operation_is_flag_set(operation, OP_FLAG_REPL_RUV);
	inst = (ldbm_instance *) be->be_instance_info;

	if (NULL == addr)
	{
		goto error_return;
	}
	if (inst && inst->inst_ref_count) {
		slapi_counter_increment(inst->inst_ref_count);
	} else {
		slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_modify",
		              "Instance \"%s\" does not exist.\n",
		              inst ? inst->inst_name : "null instance");
		goto error_return;
	}

	/* no need to check the dn syntax as this is a replicated op */
	if(!repl_op){
		ldap_result_code = slapi_dn_syntax_check(pb, slapi_sdn_get_dn(addr->sdn), 1);
		if (ldap_result_code)
		{
			ldap_result_code = LDAP_INVALID_DN_SYNTAX;
			slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &ldap_result_message);
			goto error_return;
		}
	}

	/* The dblock serializes writes to the database,
	 * which reduces deadlocking in the db code,
	 * which means that we run faster.
	 *
	 * But, this lock is re-enterant for the fixup
	 * operations that the URP code in the Replication
	 * plugin generates.
	 *
	 * SERIALLOCK is moved to dblayer_txn_begin along with exposing be
	 * transaction to plugins (see slapi_back_transaction_* APIs).
	 *
	if(SERIALLOCK(li) && !operation_is_flag_set(operation,OP_FLAG_REPL_FIXUP)) {
		dblayer_lock_backend(be);
		dblock_acquired= 1;
	}
	 */
	if ( MANAGE_ENTRY_BEFORE_DBLOCK(li)) {
		/* find and lock the entry we are about to modify */
		if (fixup_tombstone) {
			e = find_entry2modify_only_ext( pb, be, addr, TOMBSTONE_INCLUDED, &txn, &result_sent );
		} else {
			e = find_entry2modify( pb, be, addr, &txn, &result_sent );
		}
		if (e == NULL) {
			ldap_result_code = -1;
			goto error_return; /* error result sent by find_entry2modify() */
		}
	}

	txn.back_txn_txn = NULL; /* ready to create the child transaction */
	for (retry_count = 0; retry_count < RETRY_TIMES; retry_count++) {
		int cache_rc = 0;
		int new_mod_count = 0;
		if (txn.back_txn_txn && (txn.back_txn_txn != parent_txn)) {
			/* don't release SERIAL LOCK */
			dblayer_txn_abort_ext(li, &txn, PR_FALSE); 
			slapi_pblock_set(pb, SLAPI_TXN, parent_txn);
			/*
			 * Since be_txn_preop functions could have modified the entry/mods,
			 * We need to grab the current mods, free them, and restore the
			 * originals.  Same thing for the entry.
			 */
			
			slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods);
			ldap_mods_free(mods, 1);
			slapi_pblock_set(pb, SLAPI_MODIFY_MODS, copy_mods(mods_original));

			/* reset ec set cache in id2entry_add_ext */
			if (ec) {
				/* must duplicate ec before returning it to cache,
				 * which could free the entry. */
				if ((tmpentry = backentry_dup(original_entry?original_entry:ec)) == NULL) {
					ldap_result_code= LDAP_OPERATIONS_ERROR;
					goto error_return;
				}
				if (cache_is_in_cache(&inst->inst_cache, ec)) {
					CACHE_REMOVE(&inst->inst_cache, ec);
				}
				CACHE_RETURN(&inst->inst_cache, &ec);
				slapi_pblock_set( pb, SLAPI_MODIFY_EXISTING_ENTRY, original_entry->ep_entry );
				ec = original_entry;
				original_entry = tmpentry;
				tmpentry = NULL;
			}

			if (ruv_c_init) {
				/* reset the ruv txn stuff */
				modify_term(&ruv_c, be);
				ruv_c_init = 0;
			}

			slapi_log_err(SLAPI_LOG_BACKLDBM, "ldbm_back_modify",
			               "Modify Retrying Transaction\n");
#ifndef LDBM_NO_BACKOFF_DELAY
			{
			PRIntervalTime interval;
			interval = PR_MillisecondsToInterval(slapi_rand() % 100);
			DS_Sleep(interval);
			}
#endif
		}

		/* Nothing above here modifies persistent store, everything after here is subject to the transaction */
		/* dblayer_txn_begin holds SERIAL lock, 
		 * which should be outside of locking the entry (find_entry2modify) */
		if (0 == retry_count) {
			/* First time, hold SERIAL LOCK */
			retval = dblayer_txn_begin(be, parent_txn, &txn);
		} else {
			/* Otherwise, no SERIAL LOCK */
			retval = dblayer_txn_begin_ext(li, parent_txn, &txn, PR_FALSE);
		}
		if (0 != retval) {
			if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1;
			ldap_result_code= LDAP_OPERATIONS_ERROR;
			goto error_return;
		}
		/* stash the transaction for plugins */
		slapi_pblock_set(pb, SLAPI_TXN, txn.back_txn_txn);

		if (0 == retry_count) { /* just once */
			if ( !MANAGE_ENTRY_BEFORE_DBLOCK(li)) {
				/* find and lock the entry we are about to modify */
				if (fixup_tombstone) {
					e = find_entry2modify_only_ext( pb, be, addr, TOMBSTONE_INCLUDED, &txn, &result_sent );
				} else {
					e = find_entry2modify( pb, be, addr, &txn, &result_sent );
				}
				if (e == NULL) {
					ldap_result_code = -1;
					goto error_return; /* error result sent by find_entry2modify() */
				}
			}
		
			if ( !is_fixup_operation && !fixup_tombstone)
			{
				if (!repl_op && slapi_entry_flag_is_set(e->ep_entry, SLAPI_ENTRY_FLAG_TOMBSTONE))
				{
					ldap_result_code = LDAP_UNWILLING_TO_PERFORM;
                			ldap_result_message = "Operation not allowed on tombstone entry.";
					slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_modify",
						"Attempt to modify a tombstone entry %s\n",
						slapi_sdn_get_dn(slapi_entry_get_sdn_const( e->ep_entry )));
					goto error_return;
				}
				opcsn = operation_get_csn (operation);
				if (NULL == opcsn && operation->o_csngen_handler)
				{
					/*
					 * Current op is a user request. Opcsn will be assigned
					 * if the dn is in an updatable replica.
					 */
					opcsn = entry_assign_operation_csn ( pb, e->ep_entry, NULL );
				}
				if (opcsn)
				{
					entry_set_maxcsn (e->ep_entry, opcsn);
				}
			}
		
			/* Save away a copy of the entry, before modifications */
			slapi_pblock_set( pb, SLAPI_ENTRY_PRE_OP, slapi_entry_dup( e->ep_entry ));
			
			if ( (ldap_result_code = plugin_call_acl_mods_access( pb, e->ep_entry, mods, &errbuf)) != LDAP_SUCCESS ) {
				ldap_result_message= errbuf;
				goto error_return;
			}
		
			/* create a copy of the entry and apply the changes to it */
			if ( (ec = backentry_dup( e )) == NULL ) {
				ldap_result_code= LDAP_OPERATIONS_ERROR;
				goto error_return;
			}
		
			if(!repl_op){
			    remove_illegal_mods(mods);
			}
		
			/* ec is the entry that our bepreop should get to mess with */
			slapi_pblock_set( pb, SLAPI_MODIFY_EXISTING_ENTRY, ec->ep_entry );
			slapi_pblock_set(pb, SLAPI_RESULT_CODE, &ldap_result_code);
		
			opreturn = plugin_call_plugins(pb, SLAPI_PLUGIN_BE_PRE_MODIFY_FN);
			if (opreturn ||
				(slapi_pblock_get(pb, SLAPI_RESULT_CODE, &ldap_result_code) && ldap_result_code) ||
				(slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &opreturn) && opreturn)) {
				slapi_pblock_get(pb, SLAPI_RESULT_CODE, &ldap_result_code);
				slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &opreturn);
				if (!ldap_result_code) {
					slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_modify",
						"SLAPI_PLUGIN_BE_PRE_MODIFY_FN "
						"returned error but did not set SLAPI_RESULT_CODE\n");
					ldap_result_code = LDAP_OPERATIONS_ERROR;
				}
				if (SLAPI_PLUGIN_NOOP == opreturn) {
					not_an_error = 1;
					rc = opreturn = LDAP_SUCCESS;
				} else if (!opreturn) {
					opreturn = SLAPI_PLUGIN_FAILURE;
					slapi_pblock_set(pb, SLAPI_PLUGIN_OPRETURN, &opreturn);
				}
				slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &ldap_result_message);
				goto error_return;
			}
			/* The Plugin may have messed about with some of the PBlock parameters... ie. mods */
			slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods );
		
			/* apply the mods, check for syntax, schema problems, etc. */
			if (modify_apply_check_expand(pb, operation, mods, e, ec, &postentry,
										  &ldap_result_code, &ldap_result_message)) {
				goto error_return;
			}
			/* the schema check could have added a repl conflict mod
			 * get the mods again */
			slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods );
			slapi_mods_init_byref(&smods,mods);
			mod_count = slapi_mods_get_num_mods(&smods);
			/*
			 * Grab a copy of the mods and the entry in case the be_txn_preop changes
			 * the them.  If we have a failure, then we need to reset the mods to their
			 * their original state;
			 */
			mods_original = copy_mods(mods);
			if ( (original_entry = backentry_dup( ec )) == NULL ) {
				ldap_result_code= LDAP_OPERATIONS_ERROR;
				goto error_return;
			}
		} /* if (0 == retry_count) just once */

		/* call the transaction pre modify plugins just after creating the transaction */
		retval = plugin_call_plugins(pb, SLAPI_PLUGIN_BE_TXN_PRE_MODIFY_FN);
		if (retval) {
			slapi_log_err(SLAPI_LOG_TRACE, "ldbm_back_modify", "SLAPI_PLUGIN_BE_TXN_PRE_MODIFY_FN plugin "
						   "returned error code %d\n", retval );
			slapi_pblock_get(pb, SLAPI_RESULT_CODE, &ldap_result_code);
			slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &opreturn);
			if (SLAPI_PLUGIN_NOOP == retval) {
				not_an_error = 1;
				rc = retval = LDAP_SUCCESS;
			}
			if (!opreturn) {
				slapi_pblock_set(pb, SLAPI_PLUGIN_OPRETURN, ldap_result_code ? &ldap_result_code : &retval);
			}
			slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &ldap_result_message);
			goto error_return;
		}

		/* the mods might have been changed, so get the latest */
		slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods );

		/* make sure the betxnpreop did not alter any of the mods that
		   had already previously been applied */
		slapi_mods_done(&smods);
		slapi_mods_init_byref(&smods,mods);
		new_mod_count = slapi_mods_get_num_mods(&smods);
		if (new_mod_count < mod_count) {
			slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_modify",
				"Error: BE_TXN_PRE_MODIFY plugin has removed "
				"mods from the original list - mod count was [%d] now [%d] "
				"mods will not be applied - mods list changes must be done "
				"in the BE_PRE_MODIFY plugin, not the BE_TXN_PRE_MODIFY\n",
				mod_count, new_mod_count );
		} else if (new_mod_count > mod_count) { /* apply the new betxnpremod mods */
			/* apply the mods, check for syntax, schema problems, etc. */
			if (modify_apply_check_expand(pb, operation, &mods[mod_count], e, ec, &postentry,
										  &ldap_result_code, &ldap_result_message)) {
				goto error_return;
			}
		} /* else if new_mod_count == mod_count then betxnpremod plugin did nothing */
			
		/*
		 * Update the ID to Entry index. 
		 * Note that id2entry_add replaces the entry, so the Entry ID 
		 * stays the same.
		 */
		retval = id2entry_add_ext( be, ec, &txn, 1, &cache_rc ); 
		if (DB_LOCK_DEADLOCK == retval)
		{
			/* Abort and re-try */
			continue;
		}
		if (0 != retval) {
			slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_modify",
				"id2entry_add failed, err=%d %s\n",
				retval, (msg = dblayer_strerror( retval )) ? msg : "");
			if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1;
			MOD_SET_ERROR(ldap_result_code, LDAP_OPERATIONS_ERROR, retry_count);
			goto error_return;
		}
		retval = index_add_mods( be, mods, e, ec, &txn );
		if (DB_LOCK_DEADLOCK == retval)
		{
			/* Abort and re-try */
			continue;
		}
		if (0 != retval) {
			slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_modify",
				"index_add_mods failed, err=%d %s\n",
				retval, (msg = dblayer_strerror( retval )) ? msg : "");
			if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1;
			MOD_SET_ERROR(ldap_result_code, LDAP_OPERATIONS_ERROR, retry_count);
			goto error_return;
		}
		/*
		 * Remove the old entry from the Virtual List View indexes.
		 * Add the new entry to the Virtual List View indexes.
		 * If the entry is ruv, no need to update vlv.
		 */
		if (!is_ruv) {
			retval= vlv_update_all_indexes(&txn, be, pb, e, ec);
			if (DB_LOCK_DEADLOCK == retval)
			{
				/* Abort and re-try */
				continue;
			}
			if (0 != retval) {
				slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_modify",
					"vlv_update_index failed, err=%d %s\n",
					retval, (msg = dblayer_strerror( retval )) ? msg : "");
				if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1;
				MOD_SET_ERROR(ldap_result_code, 
							  LDAP_OPERATIONS_ERROR, retry_count);
				goto error_return;
			}

		}

		if (!is_ruv && !is_fixup_operation && !NO_RUV_UPDATE(li)) {
			ruv_c_init = ldbm_txn_ruv_modify_context( pb, &ruv_c );
			if (-1 == ruv_c_init) {
				slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_modify",
					"ldbm_txn_ruv_modify_context failed to construct RUV modify context\n");
				ldap_result_code= LDAP_OPERATIONS_ERROR;
				retval = 0;
				goto error_return;
			}
		}

		if (ruv_c_init) {
			retval = modify_update_all( be, pb, &ruv_c, &txn );
			if (DB_LOCK_DEADLOCK == retval) {
				/* Abort and re-try */
				continue;
			}
			if (0 != retval) {
				slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_modify",
					"modify_update_all failed, err=%d %s\n", retval,
					(msg = dblayer_strerror( retval )) ? msg : "");
				if (LDBM_OS_ERR_IS_DISKFULL(retval))
					disk_full = 1;
				ldap_result_code= LDAP_OPERATIONS_ERROR;
				goto error_return;
			}
		}

		if (0 == retval) {
			break;
		}
	}
	if (retry_count == RETRY_TIMES) {
		slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_modify",
			"Retry count exceeded in modify\n");
	   	ldap_result_code= LDAP_BUSY;
		goto error_return;
	}

	if (ruv_c_init) {
		if (modify_switch_entries(&ruv_c, be) != 0 ) {
			ldap_result_code= LDAP_OPERATIONS_ERROR;
			slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_modify",
				"modify_switch_entries failed\n");
			goto error_return;
		}
	}
	
	if (cache_replace( &inst->inst_cache, e, ec ) != 0 ) {
		MOD_SET_ERROR(ldap_result_code, LDAP_OPERATIONS_ERROR, retry_count);
		goto error_return;
	}
	/* e uncached */
	/* we must return both e (which has been deleted) and new entry ec to cache */
	/* cache_replace removes e from the cache hash tables */
	cache_unlock_entry( &inst->inst_cache, e );
	CACHE_RETURN( &inst->inst_cache, &e );
	/* lock new entry in cache to prevent usage until we are complete */
	cache_lock_entry( &inst->inst_cache, ec );
	ec_locked = 1;
	postentry = slapi_entry_dup( ec->ep_entry );
	slapi_pblock_set( pb, SLAPI_ENTRY_POST_OP, postentry );

	/* invalidate virtual cache */
	ec->ep_entry->e_virtual_watermark = 0;

	/* 
	 * LP Fix of crash when the commit will fail:
	 * If the commit fail, the common error path will
	 * try to unlock the entry again and crash (PR_ASSERT
	 * in debug mode.
	 * By just setting e to NULL, we avoid this. It's OK since
	 * we don't use e after that in the normal case.
	 */
	e = NULL;
	
	/* call the transaction post modify plugins just before the commit */
	if ((retval = plugin_call_plugins(pb, SLAPI_PLUGIN_BE_TXN_POST_MODIFY_FN))) {
		slapi_log_err(SLAPI_LOG_TRACE, "ldbm_back_modify",
			"SLAPI_PLUGIN_BE_TXN_POST_MODIFY_FN plugin "
			"returned error code %d\n", retval );
		if (!ldap_result_code) {
			slapi_pblock_get(pb, SLAPI_RESULT_CODE, &ldap_result_code);
		}
		if (!opreturn) {
			slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &opreturn);
		}
		if (!opreturn) {
			slapi_pblock_set(pb, SLAPI_PLUGIN_OPRETURN, ldap_result_code ? &ldap_result_code : &retval);
		}
		slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &ldap_result_message);
		goto error_return;
	}

	/* Release SERIAL LOCK */
	retval = dblayer_txn_commit(be, &txn);
	/* after commit - txn is no longer valid - replace SLAPI_TXN with parent */
	slapi_pblock_set(pb, SLAPI_TXN, parent_txn);
	if (0 != retval) {
		if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1;
		ldap_result_code= LDAP_OPERATIONS_ERROR;
		goto error_return;
	}

	rc= 0;
	goto common_return;

error_return:
	if ( postentry != NULL ) 
	{
		slapi_entry_free( postentry );
		postentry = NULL;
		slapi_pblock_set( pb, SLAPI_ENTRY_POST_OP, NULL );
	}
	if (retval == DB_RUNRECOVERY) {
	  dblayer_remember_disk_filled(li);
	  ldbm_nasty("ldbm_back_modify","Modify",81,retval);
	  disk_full = 1;
	}

	if (disk_full) {
	    rc= return_on_disk_full(li);
	} else {
		if (txn.back_txn_txn && (txn.back_txn_txn != parent_txn)) {
			/* make sure SLAPI_RESULT_CODE and SLAPI_PLUGIN_OPRETURN are set */
			int val = 0;
			slapi_pblock_get(pb, SLAPI_RESULT_CODE, &val);
			if (!val) {
				if (!ldap_result_code) {
					ldap_result_code = LDAP_OPERATIONS_ERROR;
				}
				slapi_pblock_set(pb, SLAPI_RESULT_CODE, &ldap_result_code);
			}
			slapi_pblock_get( pb, SLAPI_PLUGIN_OPRETURN, &val );
			if (!val) {
				opreturn = -1;
				slapi_pblock_set( pb, SLAPI_PLUGIN_OPRETURN, &opreturn );
			}
			/* call the transaction post modify plugins just before the abort */
			/* plugins called before abort should check for the OPRETURN or RESULT_CODE
			   and skip processing if they don't want do anything - some plugins that
			   keep track of a counter (usn, dna) may want to "rollback" the counter
			   in this case */
			if ((retval = plugin_call_plugins(pb, SLAPI_PLUGIN_BE_TXN_POST_MODIFY_FN))) {
				slapi_log_err(SLAPI_LOG_TRACE, "ldbm_back_modify",
					"SLAPI_PLUGIN_BE_TXN_POST_MODIFY_FN plugin returned error code %d\n", retval );
				slapi_pblock_get(pb, SLAPI_RESULT_CODE, &ldap_result_code);
				slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &ldap_result_message);
				slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &opreturn);
				if (!opreturn) {
					slapi_pblock_set(pb, SLAPI_PLUGIN_OPRETURN, ldap_result_code ? &ldap_result_code : &retval);
				}
			}

			/* It is safer not to abort when the transaction is not started. */
			/* Release SERIAL LOCK */
			dblayer_txn_abort(be, &txn); /* abort crashes in case disk full */
			/* txn is no longer valid - reset the txn pointer to the parent */
			slapi_pblock_set(pb, SLAPI_TXN, parent_txn);
		}
		if (!not_an_error) {
			rc = SLAPI_FAIL_GENERAL;
		}
	}

	/* if ec is in cache, remove it, then add back e if we still have it */
	if (inst && cache_is_in_cache(&inst->inst_cache, ec)) {
		CACHE_REMOVE( &inst->inst_cache, ec );
		/* if ec was in cache, e was not - add back e */
		if (e) {
			if (CACHE_ADD( &inst->inst_cache, e, NULL ) < 0) {
				slapi_log_err(SLAPI_LOG_CACHE, "ldbm_back_modify", "CACHE_ADD %s failed\n",
				              slapi_entry_get_dn(e->ep_entry));
			}
		}
	}

common_return:
	slapi_mods_done(&smods);
	
	if (inst) {
		if (ec_locked || cache_is_in_cache(&inst->inst_cache, ec)) {
			cache_unlock_entry(&inst->inst_cache, ec);
		} else if (e) {
			/* if ec was not in cache, cache_replace was not done.
			 * i.e., e was not unlocked. */
			cache_unlock_entry(&inst->inst_cache, e);
			CACHE_RETURN(&inst->inst_cache, &e);
		}
		CACHE_RETURN(&inst->inst_cache, &ec);
		if (inst->inst_ref_count) {
			slapi_counter_decrement(inst->inst_ref_count);
		}
	}

	/* result code could be used in the bepost plugin functions. */
	slapi_pblock_set(pb, SLAPI_RESULT_CODE, &ldap_result_code);

	/* The bepostop is called even if the operation fails. */
	if (!disk_full)
		plugin_call_plugins (pb, SLAPI_PLUGIN_BE_POST_MODIFY_FN);

	if (ruv_c_init) {
		modify_term(&ruv_c, be);
	}

	if (ldap_result_code == -1) {
		/* Reset to LDAP_NO_SUCH_OBJECT*/
		ldap_result_code = LDAP_NO_SUCH_OBJECT;
		slapi_pblock_set(pb, SLAPI_RESULT_CODE, &ldap_result_code);
	} else {
		if (not_an_error) {
			/* This is mainly used by urp.  Solved conflict is not an error.
			 * And we don't want the supplier to halt sending the updates. */
			ldap_result_code = LDAP_SUCCESS;
		}
		if (!result_sent) {
			/* result is already sent in find_entry. */
			slapi_send_ldap_result( pb, ldap_result_code, NULL, ldap_result_message, 0, NULL );
		}
	}

	/* free our backups */
	ldap_mods_free(mods_original, 1);
	backentry_free(&original_entry);
	backentry_free(&tmpentry);
	slapi_ch_free_string(&errbuf);

	return rc;
}