示例#1
0
文件: cache.c 项目: benegon/openldap
/* Used by hdb_dn2idl when loading the EntryInfo for all the children
 * of a given node
 */
int hdb_cache_load(
	struct bdb_info *bdb,
	EntryInfo *ei,
	EntryInfo **res )
{
	EntryInfo *ei2;
	int rc;

	/* See if we already have this one */
	bdb_cache_entryinfo_lock( ei->bei_parent );
	ei2 = (EntryInfo *)avl_find( ei->bei_parent->bei_kids, ei, bdb_rdn_cmp );
	bdb_cache_entryinfo_unlock( ei->bei_parent );

	if ( !ei2 ) {
		/* Not found, add it */
		struct berval bv;

		/* bei_rdn was not malloc'd before, do it now */
		ber_dupbv( &bv, &ei->bei_rdn );
		ei->bei_rdn = bv;

		rc = bdb_entryinfo_add_internal( bdb, ei, res );
		bdb_cache_entryinfo_unlock( ei->bei_parent );
		ldap_pvt_thread_rdwr_wunlock( &bdb->bi_cache.c_rwlock );
	} else {
		/* Found, return it */
		*res = ei2;
		return 0;
	}
	return rc;
}
示例#2
0
文件: dn2entry.c 项目: 1ack/Impala
int
bdb_dn2entry(
	Operation *op,
	DB_TXN *tid,
	struct berval *dn,
	EntryInfo **e,
	int matched,
	DB_LOCK *lock )
{
	EntryInfo *ei = NULL;
	int rc, rc2;

	Debug(LDAP_DEBUG_TRACE, "bdb_dn2entry(\"%s\")\n",
		dn->bv_val, 0, 0 );

	*e = NULL;

	rc = bdb_cache_find_ndn( op, tid, dn, &ei );
	if ( rc ) {
		if ( matched && rc == DB_NOTFOUND ) {
			/* Set the return value, whether we have its entry
			 * or not.
			 */
			*e = ei;
			if ( ei && ei->bei_id ) {
				rc2 = bdb_cache_find_id( op, tid, ei->bei_id,
					&ei, ID_LOCKED, lock );
				if ( rc2 ) rc = rc2;
			} else if ( ei ) {
				bdb_cache_entryinfo_unlock( ei );
				memset( lock, 0, sizeof( *lock ));
				lock->mode = DB_LOCK_NG;
			}
		} else if ( ei ) {
			bdb_cache_entryinfo_unlock( ei );
		}
	} else {
		rc = bdb_cache_find_id( op, tid, ei->bei_id, &ei, ID_LOCKED,
			lock );
		if ( rc == 0 ) {
			*e = ei;
		} else if ( matched && rc == DB_NOTFOUND ) {
			/* always return EntryInfo */
			if ( ei->bei_parent ) {
				ei = ei->bei_parent;
				rc2 = bdb_cache_find_id( op, tid, ei->bei_id, &ei, 0,
					lock );
				if ( rc2 ) rc = rc2;
			}
			*e = ei;
		}
	}

	return rc;
}
示例#3
0
文件: cache.c 项目: benegon/openldap
void
bdb_cache_return_entry_rw( struct bdb_info *bdb, Entry *e,
	int rw, DB_LOCK *lock )
{
	EntryInfo *ei;
	int free = 0;

	ei = e->e_private;
	if ( ei && ( ei->bei_state & CACHE_ENTRY_NOT_CACHED )) {
		bdb_cache_entryinfo_lock( ei );
		if ( ei->bei_state & CACHE_ENTRY_NOT_CACHED ) {
			/* Releasing the entry can only be done when
			 * we know that nobody else is using it, i.e we
			 * should have an entry_db writelock.  But the
			 * flag is only set by the thread that loads the
			 * entry, and only if no other threads has found
			 * it while it was working.  All other threads
			 * clear the flag, which mean that we should be
			 * the only thread using the entry if the flag
			 * is set here.
			 */
			ei->bei_e = NULL;
			ei->bei_state ^= CACHE_ENTRY_NOT_CACHED;
			free = 1;
		}
		bdb_cache_entryinfo_unlock( ei );
	}
	bdb_cache_entry_db_unlock( bdb, lock );
	if ( free ) {
		e->e_private = NULL;
		bdb_entry_return( e );
	}
}
示例#4
0
文件: tools.c 项目: rouzier/openldap
ID bdb_tool_dn2id_get(
	Backend *be,
	struct berval *dn
)
{
	Operation op = {0};
	Opheader ohdr = {0};
	EntryInfo *ei = NULL;
	int rc;

	if ( BER_BVISEMPTY(dn) )
		return 0;

	op.o_hdr = &ohdr;
	op.o_bd = be;
	op.o_tmpmemctx = NULL;
	op.o_tmpmfuncs = &ch_mfuncs;

	rc = bdb_cache_find_ndn( &op, 0, dn, &ei );
	if ( ei ) bdb_cache_entryinfo_unlock( ei );
	if ( rc == DB_NOTFOUND )
		return NOID;
	
	return ei->bei_id;
}
示例#5
0
文件: dn2id.c 项目: cptaffe/openldap
static int
hdb_dn2idl_internal(
	struct dn2id_cookie *cx
)
{
	BDB_IDL_ZERO( cx->tmp );

	if ( cx->bdb->bi_idl_cache_size ) {
		char *ptr = ((char *)&cx->id)-1;

		cx->key.data = ptr;
		cx->key.size = sizeof(ID)+1;
		if ( cx->prefix == DN_SUBTREE_PREFIX ) {
			ID *ids = cx->depth ? cx->tmp : cx->ids;
			*ptr = cx->prefix;
			cx->rc = bdb_idl_cache_get(cx->bdb, cx->db, &cx->key, ids);
			if ( cx->rc == LDAP_SUCCESS ) {
				if ( cx->depth ) {
					bdb_idl_delete( cx->tmp, cx->id );	/* ITS#6983, drop our own ID */
					bdb_idl_append( cx->ids, cx->tmp );
					cx->need_sort = 1;
				}
				return cx->rc;
			}
		}
		*ptr = DN_ONE_PREFIX;
		cx->rc = bdb_idl_cache_get(cx->bdb, cx->db, &cx->key, cx->tmp);
		if ( cx->rc == LDAP_SUCCESS ) {
			goto gotit;
		}
		if ( cx->rc == DB_NOTFOUND ) {
			return cx->rc;
		}
	}

	bdb_cache_entryinfo_lock( cx->ei );

	/* If number of kids in the cache differs from on-disk, load
	 * up all the kids from the database
	 */
	if ( cx->ei->bei_ckids+1 != cx->ei->bei_dkids ) {
		EntryInfo ei;
		db_recno_t dkids = cx->ei->bei_dkids;
		ei.bei_parent = cx->ei;

		/* Only one thread should load the cache */
		while ( cx->ei->bei_state & CACHE_ENTRY_ONELEVEL ) {
			bdb_cache_entryinfo_unlock( cx->ei );
			ldap_pvt_thread_yield();
			bdb_cache_entryinfo_lock( cx->ei );
			if ( cx->ei->bei_ckids+1 == cx->ei->bei_dkids ) {
				goto synced;
			}
		}

		cx->ei->bei_state |= CACHE_ENTRY_ONELEVEL;

		bdb_cache_entryinfo_unlock( cx->ei );

		cx->rc = cx->db->cursor( cx->db, NULL, &cx->dbc,
			cx->bdb->bi_db_opflags );
		if ( cx->rc )
			goto done_one;

		cx->data.data = &cx->dbuf;
		cx->data.ulen = sizeof(ID);
		cx->data.dlen = sizeof(ID);
		cx->data.flags = DB_DBT_USERMEM | DB_DBT_PARTIAL;

		/* The first item holds the parent ID. Ignore it. */
		cx->key.data = &cx->nid;
		cx->key.size = sizeof(ID);
		cx->rc = cx->dbc->c_get( cx->dbc, &cx->key, &cx->data, DB_SET );
		if ( cx->rc ) {
			cx->dbc->c_close( cx->dbc );
			goto done_one;
		}

		/* If the on-disk count is zero we've never checked it.
		 * Count it now.
		 */
		if ( !dkids ) {
			cx->dbc->c_count( cx->dbc, &dkids, 0 );
			cx->ei->bei_dkids = dkids;
		}

		cx->data.data = cx->buf;
		cx->data.ulen = BDB_IDL_UM_SIZE * sizeof(ID);
		cx->data.flags = DB_DBT_USERMEM;

		if ( dkids > 1 ) {
			/* Fetch the rest of the IDs in a loop... */
			while ( (cx->rc = cx->dbc->c_get( cx->dbc, &cx->key, &cx->data,
				DB_MULTIPLE | DB_NEXT_DUP )) == 0 ) {
				u_int8_t *j;
				size_t len;
				void *ptr;
				DB_MULTIPLE_INIT( ptr, &cx->data );
				while (ptr) {
					DB_MULTIPLE_NEXT( ptr, &cx->data, j, len );
					if (j) {
						EntryInfo *ei2;
						diskNode *d = (diskNode *)j;
						short nrlen;

						BDB_DISK2ID( j + len - sizeof(ID), &ei.bei_id );
						nrlen = ((d->nrdnlen[0] ^ 0x80) << 8) | d->nrdnlen[1];
						ei.bei_nrdn.bv_len = nrlen;
						/* nrdn/rdn are set in-place.
						 * hdb_cache_load will copy them as needed
						 */
						ei.bei_nrdn.bv_val = d->nrdn;
						ei.bei_rdn.bv_len = len - sizeof(diskNode)
							- ei.bei_nrdn.bv_len;
						ei.bei_rdn.bv_val = d->nrdn + ei.bei_nrdn.bv_len + 1;
						bdb_idl_append_one( cx->tmp, ei.bei_id );
						hdb_cache_load( cx->bdb, &ei, &ei2 );
					}
				}
			}
		}

		cx->rc = cx->dbc->c_close( cx->dbc );
done_one:
		bdb_cache_entryinfo_lock( cx->ei );
		cx->ei->bei_state &= ~CACHE_ENTRY_ONELEVEL;
		bdb_cache_entryinfo_unlock( cx->ei );
		if ( cx->rc )
			return cx->rc;

	} else {
		/* The in-memory cache is in sync with the on-disk data.
		 * do we have any kids?
		 */
synced:
		cx->rc = 0;
		if ( cx->ei->bei_ckids > 0 ) {
			/* Walk the kids tree; order is irrelevant since bdb_idl_sort
			 * will sort it later.
			 */
			avl_apply( cx->ei->bei_kids, apply_func,
				cx->tmp, -1, AVL_POSTORDER );
		}
		bdb_cache_entryinfo_unlock( cx->ei );
	}

	if ( !BDB_IDL_IS_RANGE( cx->tmp ) && cx->tmp[0] > 3 )
		bdb_idl_sort( cx->tmp, cx->buf );
	if ( cx->bdb->bi_idl_cache_max_size && !BDB_IDL_IS_ZERO( cx->tmp )) {
		char *ptr = ((char *)&cx->id)-1;
		cx->key.data = ptr;
		cx->key.size = sizeof(ID)+1;
		*ptr = DN_ONE_PREFIX;
		bdb_idl_cache_put( cx->bdb, cx->db, &cx->key, cx->tmp, cx->rc );
	}

gotit:
	if ( !BDB_IDL_IS_ZERO( cx->tmp )) {
		if ( cx->prefix == DN_SUBTREE_PREFIX ) {
			bdb_idl_append( cx->ids, cx->tmp );
			cx->need_sort = 1;
			if ( !(cx->ei->bei_state & CACHE_ENTRY_NO_GRANDKIDS)) {
				ID *save, idcurs;
				EntryInfo *ei = cx->ei;
				int nokids = 1;
				save = cx->op->o_tmpalloc( BDB_IDL_SIZEOF( cx->tmp ),
					cx->op->o_tmpmemctx );
				BDB_IDL_CPY( save, cx->tmp );

				idcurs = 0;
				cx->depth++;
				for ( cx->id = bdb_idl_first( save, &idcurs );
					cx->id != NOID;
					cx->id = bdb_idl_next( save, &idcurs )) {
					EntryInfo *ei2;
					cx->ei = NULL;
					if ( bdb_cache_find_id( cx->op, cx->txn, cx->id, &cx->ei,
						ID_NOENTRY, NULL ))
						continue;
					if ( cx->ei ) {
						ei2 = cx->ei;
						if ( !( ei2->bei_state & CACHE_ENTRY_NO_KIDS )) {
							BDB_ID2DISK( cx->id, &cx->nid );
							hdb_dn2idl_internal( cx );
							if ( !BDB_IDL_IS_ZERO( cx->tmp ))
								nokids = 0;
						}
						bdb_cache_entryinfo_lock( ei2 );
						ei2->bei_finders--;
						bdb_cache_entryinfo_unlock( ei2 );
					}
				}
				cx->depth--;
				cx->op->o_tmpfree( save, cx->op->o_tmpmemctx );
				if ( nokids ) {
					bdb_cache_entryinfo_lock( ei );
					ei->bei_state |= CACHE_ENTRY_NO_GRANDKIDS;
					bdb_cache_entryinfo_unlock( ei );
				}
			}
			/* Make sure caller knows it had kids! */
			cx->tmp[0]=1;

			cx->rc = 0;
		} else {
			BDB_IDL_CPY( cx->ids, cx->tmp );
		}
	}
	return cx->rc;
}
示例#6
0
文件: cache.c 项目: benegon/openldap
int
bdb_cache_find_id(
	Operation *op,
	DB_TXN	*tid,
	ID				id,
	EntryInfo	**eip,
	int		flag,
	DB_LOCK		*lock )
{
	struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
	Entry	*ep = NULL;
	int	rc = 0, load = 0;
	EntryInfo ei = { 0 };

	ei.bei_id = id;

#ifdef SLAP_ZONE_ALLOC
	slap_zh_rlock(bdb->bi_cache.c_zctx);
#endif
	/* If we weren't given any info, see if we have it already cached */
	if ( !*eip ) {
again:	ldap_pvt_thread_rdwr_rlock( &bdb->bi_cache.c_rwlock );
		*eip = (EntryInfo *) avl_find( bdb->bi_cache.c_idtree,
			(caddr_t) &ei, bdb_id_cmp );
		if ( *eip ) {
			/* If the lock attempt fails, the info is in use */
			if ( bdb_cache_entryinfo_trylock( *eip )) {
				int del = (*eip)->bei_state & CACHE_ENTRY_DELETED;
				ldap_pvt_thread_rdwr_runlock( &bdb->bi_cache.c_rwlock );
				/* If this node is being deleted, treat
				 * as if the delete has already finished
				 */
				if ( del ) {
					return DB_NOTFOUND;
				}
				/* otherwise, wait for the info to free up */
				ldap_pvt_thread_yield();
				goto again;
			}
			/* If this info isn't hooked up to its parent yet,
			 * unlock and wait for it to be fully initialized
			 */
			if ( (*eip)->bei_state & CACHE_ENTRY_NOT_LINKED ) {
				bdb_cache_entryinfo_unlock( *eip );
				ldap_pvt_thread_rdwr_runlock( &bdb->bi_cache.c_rwlock );
				ldap_pvt_thread_yield();
				goto again;
			}
			flag |= ID_LOCKED;
		}
		ldap_pvt_thread_rdwr_runlock( &bdb->bi_cache.c_rwlock );
	}

	/* See if the ID exists in the database; add it to the cache if so */
	if ( !*eip ) {
#ifndef BDB_HIER
		rc = bdb_id2entry( op->o_bd, tid, id, &ep );
		if ( rc == 0 ) {
			rc = bdb_cache_find_ndn( op, tid,
				&ep->e_nname, eip );
			if ( *eip ) flag |= ID_LOCKED;
			if ( rc ) {
				ep->e_private = NULL;
#ifdef SLAP_ZONE_ALLOC
				bdb_entry_return( bdb, ep, (*eip)->bei_zseq );
#else
				bdb_entry_return( ep );
#endif
				ep = NULL;
			}
		}
#else
		rc = hdb_cache_find_parent(op, tid, id, eip );
		if ( rc == 0 ) flag |= ID_LOCKED;
#endif
	}

	/* Ok, we found the info, do we have the entry? */
	if ( rc == 0 ) {
		if ( !( flag & ID_LOCKED )) {
			bdb_cache_entryinfo_lock( *eip );
			flag |= ID_LOCKED;
		}

		if ( (*eip)->bei_state & CACHE_ENTRY_DELETED ) {
			rc = DB_NOTFOUND;
		} else {
			(*eip)->bei_finders++;
			(*eip)->bei_state |= CACHE_ENTRY_REFERENCED;
			if ( flag & ID_NOENTRY ) {
				bdb_cache_entryinfo_unlock( *eip );
				return 0;
			}
			/* Make sure only one thread tries to load the entry */
load1:
#ifdef SLAP_ZONE_ALLOC
			if ((*eip)->bei_e && !slap_zn_validate(
					bdb->bi_cache.c_zctx, (*eip)->bei_e, (*eip)->bei_zseq)) {
				(*eip)->bei_e = NULL;
				(*eip)->bei_zseq = 0;
			}
#endif
			if ( !(*eip)->bei_e && !((*eip)->bei_state & CACHE_ENTRY_LOADING)) {
				load = 1;
				(*eip)->bei_state |= CACHE_ENTRY_LOADING;
				flag |= ID_CHKPURGE;
			}

			if ( !load ) {
				/* Clear the uncached state if we are not
				 * loading it, i.e it is already cached or
				 * another thread is currently loading it.
				 */
				if ( (*eip)->bei_state & CACHE_ENTRY_NOT_CACHED ) {
					(*eip)->bei_state ^= CACHE_ENTRY_NOT_CACHED;
					flag |= ID_CHKPURGE;
				}
			}

			if ( flag & ID_LOCKED ) {
				bdb_cache_entryinfo_unlock( *eip );
				flag ^= ID_LOCKED;
			}
			rc = bdb_cache_entry_db_lock( bdb, tid, *eip, load, 0, lock );
			if ( (*eip)->bei_state & CACHE_ENTRY_DELETED ) {
				rc = DB_NOTFOUND;
				bdb_cache_entry_db_unlock( bdb, lock );
				bdb_cache_entryinfo_lock( *eip );
				(*eip)->bei_finders--;
				bdb_cache_entryinfo_unlock( *eip );
			} else if ( rc == 0 ) {
				if ( load ) {
					if ( !ep) {
						rc = bdb_id2entry( op->o_bd, tid, id, &ep );
					}
					if ( rc == 0 ) {
						ep->e_private = *eip;
#ifdef BDB_HIER
						while ( (*eip)->bei_state & CACHE_ENTRY_NOT_LINKED )
							ldap_pvt_thread_yield();
						bdb_fix_dn( ep, 0 );
#endif
						bdb_cache_entryinfo_lock( *eip );

						(*eip)->bei_e = ep;
#ifdef SLAP_ZONE_ALLOC
						(*eip)->bei_zseq = *((ber_len_t *)ep - 2);
#endif
						ep = NULL;
						if ( flag & ID_NOCACHE ) {
							/* Set the cached state only if no other thread
							 * found the info while we were loading the entry.
							 */
							if ( (*eip)->bei_finders == 1 ) {
								(*eip)->bei_state |= CACHE_ENTRY_NOT_CACHED;
								flag ^= ID_CHKPURGE;
							}
						}
						bdb_cache_entryinfo_unlock( *eip );
						bdb_cache_lru_link( bdb, *eip );
					}
					if ( rc == 0 ) {
						/* If we succeeded, downgrade back to a readlock. */
						rc = bdb_cache_entry_db_relock( bdb, tid,
							*eip, 0, 0, lock );
					} else {
						/* Otherwise, release the lock. */
						bdb_cache_entry_db_unlock( bdb, lock );
					}
				} else if ( !(*eip)->bei_e ) {
					/* Some other thread is trying to load the entry,
					 * wait for it to finish.
					 */
					bdb_cache_entry_db_unlock( bdb, lock );
					bdb_cache_entryinfo_lock( *eip );
					flag |= ID_LOCKED;
					goto load1;
#ifdef BDB_HIER
				} else {
					/* Check for subtree renames
					 */
					rc = bdb_fix_dn( (*eip)->bei_e, 1 );
					if ( rc ) {
						bdb_cache_entry_db_relock( bdb,
							tid, *eip, 1, 0, lock );
						/* check again in case other modifier did it already */
						if ( bdb_fix_dn( (*eip)->bei_e, 1 ) )
							rc = bdb_fix_dn( (*eip)->bei_e, 2 );
						bdb_cache_entry_db_relock( bdb,
							tid, *eip, 0, 0, lock );
					}
#endif
				}
				bdb_cache_entryinfo_lock( *eip );
				(*eip)->bei_finders--;
				if ( load )
					(*eip)->bei_state ^= CACHE_ENTRY_LOADING;
				bdb_cache_entryinfo_unlock( *eip );
			}
		}
	}
	if ( flag & ID_LOCKED ) {
		bdb_cache_entryinfo_unlock( *eip );
	}
	if ( ep ) {
		ep->e_private = NULL;
#ifdef SLAP_ZONE_ALLOC
		bdb_entry_return( bdb, ep, (*eip)->bei_zseq );
#else
		bdb_entry_return( ep );
#endif
	}
	if ( rc == 0 ) {
		int purge = 0;

		if (( flag & ID_CHKPURGE ) || bdb->bi_cache.c_eimax ) {
			ldap_pvt_thread_mutex_lock( &bdb->bi_cache.c_count_mutex );
			if ( flag & ID_CHKPURGE ) {
				bdb->bi_cache.c_cursize++;
				if ( !bdb->bi_cache.c_purging && bdb->bi_cache.c_cursize > bdb->bi_cache.c_maxsize ) {
					purge = 1;
					bdb->bi_cache.c_purging = 1;
				}
			} else if ( !bdb->bi_cache.c_purging && bdb->bi_cache.c_eimax && bdb->bi_cache.c_leaves > bdb->bi_cache.c_eimax ) {
				purge = 1;
				bdb->bi_cache.c_purging = 1;
			}
			ldap_pvt_thread_mutex_unlock( &bdb->bi_cache.c_count_mutex );
		}
		if ( purge )
			bdb_cache_lru_purge( bdb );
	}

#ifdef SLAP_ZONE_ALLOC
	if (rc == 0 && (*eip)->bei_e) {
		slap_zn_rlock(bdb->bi_cache.c_zctx, (*eip)->bei_e);
	}
	slap_zh_runlock(bdb->bi_cache.c_zctx);
#endif
	return rc;
}
示例#7
0
文件: cache.c 项目: benegon/openldap
/* This is best-effort only. If all entries in the cache are
 * busy, they will all be kept. This is unlikely to happen
 * unless the cache is very much smaller than the working set.
 */
static void
bdb_cache_lru_purge( struct bdb_info *bdb )
{
	DB_LOCK		lock, *lockp;
	EntryInfo *elru, *elnext = NULL;
	int islocked;
	ID eicount, ecount;
	ID count, efree, eifree = 0;
#ifdef LDAP_DEBUG
	int iter;
#endif

	/* Wait for the mutex; we're the only one trying to purge. */
	ldap_pvt_thread_mutex_lock( &bdb->bi_cache.c_lru_mutex );

	if ( bdb->bi_cache.c_cursize > bdb->bi_cache.c_maxsize ) {
		efree = bdb->bi_cache.c_cursize - bdb->bi_cache.c_maxsize;
		efree += bdb->bi_cache.c_minfree;
	} else {
		efree = 0;
	}

	/* maximum number of EntryInfo leaves to cache. In slapcat
	 * we always free all leaf nodes.
	 */

	if ( slapMode & SLAP_TOOL_READONLY ) {
		eifree = bdb->bi_cache.c_leaves;
	} else if ( bdb->bi_cache.c_eimax &&
		bdb->bi_cache.c_leaves > bdb->bi_cache.c_eimax ) {
		eifree = bdb->bi_cache.c_minfree * 10;
		if ( eifree >= bdb->bi_cache.c_leaves )
			eifree /= 2;
	}

	if ( !efree && !eifree ) {
		ldap_pvt_thread_mutex_unlock( &bdb->bi_cache.c_lru_mutex );
		bdb->bi_cache.c_purging = 0;
		return;
	}

	if ( bdb->bi_cache.c_txn ) {
		lockp = &lock;
	} else {
		lockp = NULL;
	}

	count = 0;
	eicount = 0;
	ecount = 0;
#ifdef LDAP_DEBUG
	iter = 0;
#endif

	/* Look for an unused entry to remove */
	for ( elru = bdb->bi_cache.c_lruhead; elru; elru = elnext ) {
		elnext = elru->bei_lrunext;

		if ( bdb_cache_entryinfo_trylock( elru ))
			goto bottom;

		/* This flag implements the clock replacement behavior */
		if ( elru->bei_state & ( CACHE_ENTRY_REFERENCED )) {
			elru->bei_state &= ~CACHE_ENTRY_REFERENCED;
			bdb_cache_entryinfo_unlock( elru );
			goto bottom;
		}

		/* If this node is in the process of linking into the cache,
		 * or this node is being deleted, skip it.
		 */
		if (( elru->bei_state & ( CACHE_ENTRY_NOT_LINKED |
			CACHE_ENTRY_DELETED | CACHE_ENTRY_LOADING |
			CACHE_ENTRY_ONELEVEL )) ||
			elru->bei_finders > 0 ) {
			bdb_cache_entryinfo_unlock( elru );
			goto bottom;
		}

		if ( bdb_cache_entryinfo_trylock( elru->bei_parent )) {
			bdb_cache_entryinfo_unlock( elru );
			goto bottom;
		}

		/* entryinfo is locked */
		islocked = 1;

		/* If we can successfully writelock it, then
		 * the object is idle.
		 */
		if ( bdb_cache_entry_db_lock( bdb,
			bdb->bi_cache.c_txn, elru, 1, 1, lockp ) == 0 ) {

			/* Free entry for this node if it's present */
			if ( elru->bei_e ) {
				ecount++;

				/* the cache may have gone over the limit while we
				 * weren't looking, so double check.
				 */
				if ( !efree && ecount > bdb->bi_cache.c_maxsize )
					efree = bdb->bi_cache.c_minfree;

				if ( count < efree ) {
					elru->bei_e->e_private = NULL;
#ifdef SLAP_ZONE_ALLOC
					bdb_entry_return( bdb, elru->bei_e, elru->bei_zseq );
#else
					bdb_entry_return( elru->bei_e );
#endif
					elru->bei_e = NULL;
					count++;
				} else {
					/* Keep this node cached, skip to next */
					bdb_cache_entry_db_unlock( bdb, lockp );
					goto next;
				}
			}
			bdb_cache_entry_db_unlock( bdb, lockp );

			/* 
			 * If it is a leaf node, and we're over the limit, free it.
			 */
			if ( elru->bei_kids ) {
				/* Drop from list, we ignore it... */
				LRU_DEL( &bdb->bi_cache, elru );
			} else if ( eicount < eifree ) {
				/* Too many leaf nodes, free this one */
				bdb_cache_delete_internal( &bdb->bi_cache, elru, 0 );
				bdb_cache_delete_cleanup( &bdb->bi_cache, elru );
				islocked = 0;
				eicount++;
			}	/* Leave on list until we need to free it */
		}

next:
		if ( islocked ) {
			bdb_cache_entryinfo_unlock( elru );
			bdb_cache_entryinfo_unlock( elru->bei_parent );
		}

		if ( count >= efree && eicount >= eifree )
			break;
bottom:
		if ( elnext == bdb->bi_cache.c_lruhead )
			break;
#ifdef LDAP_DEBUG
		iter++;
#endif
	}

	if ( count || ecount > bdb->bi_cache.c_cursize ) {
		ldap_pvt_thread_mutex_lock( &bdb->bi_cache.c_count_mutex );
		/* HACK: we seem to be losing track, fix up now */
		if ( ecount > bdb->bi_cache.c_cursize )
			bdb->bi_cache.c_cursize = ecount;
		bdb->bi_cache.c_cursize -= count;
		ldap_pvt_thread_mutex_unlock( &bdb->bi_cache.c_count_mutex );
	}
	bdb->bi_cache.c_lruhead = elnext;
	ldap_pvt_thread_mutex_unlock( &bdb->bi_cache.c_lru_mutex );
	bdb->bi_cache.c_purging = 0;
}
示例#8
0
文件: cache.c 项目: benegon/openldap
/* Walk up the tree from a child node, looking for an ID that's already
 * been linked into the cache.
 */
int
hdb_cache_find_parent(
	Operation *op,
	DB_TXN	*txn,
	ID id,
	EntryInfo **res )
{
	struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
	EntryInfo ei, eip, *ei2 = NULL, *ein = NULL, *eir = NULL;
	int rc, add;

	ei.bei_id = id;
	ei.bei_kids = NULL;
	ei.bei_ckids = 0;

	for (;;) {
		rc = hdb_dn2id_parent( op, txn, &ei, &eip.bei_id );
		if ( rc ) break;

		/* Save the previous node, if any */
		ei2 = ein;

		/* Create a new node for the current ID */
		ein = bdb_cache_entryinfo_new( &bdb->bi_cache );
		ein->bei_id = ei.bei_id;
		ein->bei_kids = ei.bei_kids;
		ein->bei_nrdn = ei.bei_nrdn;
		ein->bei_rdn = ei.bei_rdn;
		ein->bei_ckids = ei.bei_ckids;
#ifdef SLAP_ZONE_ALLOC
		ein->bei_bdb = bdb;
#endif
		ei.bei_ckids = 0;
		add = 1;
		
		/* This node is not fully connected yet */
		ein->bei_state |= CACHE_ENTRY_NOT_LINKED;

		/* If this is the first time, save this node
		 * to be returned later.
		 */
		if ( eir == NULL ) {
			eir = ein;
			ein->bei_finders++;
		}

again:
		/* Insert this node into the ID tree */
		ldap_pvt_thread_rdwr_wlock( &bdb->bi_cache.c_rwlock );
		if ( avl_insert( &bdb->bi_cache.c_idtree, (caddr_t)ein,
			bdb_id_cmp, bdb_id_dup_err ) ) {
			EntryInfo *eix = ein->bei_lrunext;

			if ( bdb_cache_entryinfo_trylock( eix )) {
				ldap_pvt_thread_rdwr_wunlock( &bdb->bi_cache.c_rwlock );
				ldap_pvt_thread_yield();
				goto again;
			}
			ldap_pvt_thread_rdwr_wunlock( &bdb->bi_cache.c_rwlock );

			/* Someone else created this node just before us.
			 * Free our new copy and use the existing one.
			 */
			bdb_cache_entryinfo_free( &bdb->bi_cache, ein );

			/* if it was the node we were looking for, just return it */
			if ( eir == ein ) {
				*res = eix;
				rc = 0;
				break;
			}

			ein = ei2;
			ei2 = eix;
			add = 0;

			/* otherwise, link up what we have and return */
			goto gotparent;
		}

		/* If there was a previous node, link it to this one */
		if ( ei2 ) ei2->bei_parent = ein;

		/* Look for this node's parent */
par2:
		if ( eip.bei_id ) {
			ei2 = (EntryInfo *) avl_find( bdb->bi_cache.c_idtree,
					(caddr_t) &eip, bdb_id_cmp );
		} else {
			ei2 = &bdb->bi_cache.c_dntree;
		}
		if ( ei2 && bdb_cache_entryinfo_trylock( ei2 )) {
			ldap_pvt_thread_rdwr_wunlock( &bdb->bi_cache.c_rwlock );
			ldap_pvt_thread_yield();
			ldap_pvt_thread_rdwr_wlock( &bdb->bi_cache.c_rwlock );
			goto par2;
		}
		if ( add )
			bdb->bi_cache.c_eiused++;
		if ( ei2 && ( ei2->bei_kids || !ei2->bei_id ))
			bdb->bi_cache.c_leaves++;
		ldap_pvt_thread_rdwr_wunlock( &bdb->bi_cache.c_rwlock );

gotparent:
		/* Got the parent, link in and we're done. */
		if ( ei2 ) {
			bdb_cache_entryinfo_lock( eir );
			ein->bei_parent = ei2;

			if ( avl_insert( &ei2->bei_kids, (caddr_t)ein, bdb_rdn_cmp,
				avl_dup_error) == 0 )
				ei2->bei_ckids++;

			/* Reset all the state info */
			for (ein = eir; ein != ei2; ein=ein->bei_parent)
				ein->bei_state &= ~CACHE_ENTRY_NOT_LINKED;

			bdb_cache_entryinfo_unlock( ei2 );
			eir->bei_finders--;

			*res = eir;
			break;
		}
		ei.bei_kids = NULL;
		ei.bei_id = eip.bei_id;
		ei.bei_ckids = 1;
		avl_insert( &ei.bei_kids, (caddr_t)ein, bdb_rdn_cmp,
			avl_dup_error );
	}
	return rc;
}
示例#9
0
文件: cache.c 项目: benegon/openldap
/* Find the EntryInfo for the requested DN. If the DN cannot be found, return
 * the info for its closest ancestor. *res should be NULL to process a
 * complete DN starting from the tree root. Otherwise *res must be the
 * immediate parent of the requested DN, and only the RDN will be searched.
 * The EntryInfo is locked upon return and must be unlocked by the caller.
 */
int
bdb_cache_find_ndn(
	Operation	*op,
	DB_TXN		*txn,
	struct berval	*ndn,
	EntryInfo	**res )
{
	struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
	EntryInfo	ei, *eip, *ei2;
	int rc = 0;
	char *ptr;

	/* this function is always called with normalized DN */
	if ( *res ) {
		/* we're doing a onelevel search for an RDN */
		ei.bei_nrdn.bv_val = ndn->bv_val;
		ei.bei_nrdn.bv_len = dn_rdnlen( op->o_bd, ndn );
		eip = *res;
	} else {
		/* we're searching a full DN from the root */
		ptr = ndn->bv_val + ndn->bv_len - op->o_bd->be_nsuffix[0].bv_len;
		ei.bei_nrdn.bv_val = ptr;
		ei.bei_nrdn.bv_len = op->o_bd->be_nsuffix[0].bv_len;
		/* Skip to next rdn if suffix is empty */
		if ( ei.bei_nrdn.bv_len == 0 ) {
			for (ptr = ei.bei_nrdn.bv_val - 2; ptr > ndn->bv_val
				&& !DN_SEPARATOR(*ptr); ptr--) /* empty */;
			if ( ptr >= ndn->bv_val ) {
				if (DN_SEPARATOR(*ptr)) ptr++;
				ei.bei_nrdn.bv_len = ei.bei_nrdn.bv_val - ptr;
				ei.bei_nrdn.bv_val = ptr;
			}
		}
		eip = &bdb->bi_cache.c_dntree;
	}
	
	for ( bdb_cache_entryinfo_lock( eip ); eip; ) {
		eip->bei_state |= CACHE_ENTRY_REFERENCED;
		ei.bei_parent = eip;
		ei2 = (EntryInfo *)avl_find( eip->bei_kids, &ei, bdb_rdn_cmp );
		if ( !ei2 ) {
			DBC *cursor;
			int len = ei.bei_nrdn.bv_len;
				
			if ( BER_BVISEMPTY( ndn )) {
				*res = eip;
				return LDAP_SUCCESS;
			}

			ei.bei_nrdn.bv_len = ndn->bv_len -
				(ei.bei_nrdn.bv_val - ndn->bv_val);
			eip->bei_finders++;
			bdb_cache_entryinfo_unlock( eip );

			BDB_LOG_PRINTF( bdb->bi_dbenv, NULL, "slapd Reading %s",
				ei.bei_nrdn.bv_val );

			cursor = NULL;
			rc = bdb_dn2id( op, &ei.bei_nrdn, &ei, txn, &cursor );
			if (rc) {
				bdb_cache_entryinfo_lock( eip );
				eip->bei_finders--;
				if ( cursor ) cursor->c_close( cursor );
				*res = eip;
				return rc;
			}

			BDB_LOG_PRINTF( bdb->bi_dbenv, NULL, "slapd Read got %s(%d)",
				ei.bei_nrdn.bv_val, ei.bei_id );

			/* DN exists but needs to be added to cache */
			ei.bei_nrdn.bv_len = len;
			rc = bdb_entryinfo_add_internal( bdb, &ei, &ei2 );
			/* add_internal left eip and c_rwlock locked */
			eip->bei_finders--;
			ldap_pvt_thread_rdwr_wunlock( &bdb->bi_cache.c_rwlock );
			if ( cursor ) cursor->c_close( cursor );
			if ( rc ) {
				*res = eip;
				return rc;
			}
		}
		bdb_cache_entryinfo_lock( ei2 );
		if ( ei2->bei_state & CACHE_ENTRY_DELETED ) {
			/* In the midst of deleting? Give it a chance to
			 * complete.
			 */
			bdb_cache_entryinfo_unlock( ei2 );
			bdb_cache_entryinfo_unlock( eip );
			ldap_pvt_thread_yield();
			bdb_cache_entryinfo_lock( eip );
			*res = eip;
			return DB_NOTFOUND;
		}
		bdb_cache_entryinfo_unlock( eip );

		eip = ei2;

		/* Advance to next lower RDN */
		for (ptr = ei.bei_nrdn.bv_val - 2; ptr > ndn->bv_val
			&& !DN_SEPARATOR(*ptr); ptr--) /* empty */;
		if ( ptr >= ndn->bv_val ) {
			if (DN_SEPARATOR(*ptr)) ptr++;
			ei.bei_nrdn.bv_len = ei.bei_nrdn.bv_val - ptr - 1;
			ei.bei_nrdn.bv_val = ptr;
		}
		if ( ptr < ndn->bv_val ) {
			*res = eip;
			break;
		}
	}

	return rc;
}
示例#10
0
文件: tools.c 项目: rouzier/openldap
static int bdb_tool_next_id(
	Operation *op,
	DB_TXN *tid,
	Entry *e,
	struct berval *text,
	int hole )
{
	struct berval dn = e->e_name;
	struct berval ndn = e->e_nname;
	struct berval pdn, npdn;
	EntryInfo *ei = NULL, eidummy;
	int rc;

	if (ndn.bv_len == 0) {
		e->e_id = 0;
		return 0;
	}

	rc = bdb_cache_find_ndn( op, tid, &ndn, &ei );
	if ( ei ) bdb_cache_entryinfo_unlock( ei );
	if ( rc == DB_NOTFOUND ) {
		if ( !be_issuffix( op->o_bd, &ndn ) ) {
			ID eid = e->e_id;
			dnParent( &dn, &pdn );
			dnParent( &ndn, &npdn );
			e->e_name = pdn;
			e->e_nname = npdn;
			rc = bdb_tool_next_id( op, tid, e, text, 1 );
			e->e_name = dn;
			e->e_nname = ndn;
			if ( rc ) {
				return rc;
			}
			/* If parent didn't exist, it was created just now
			 * and its ID is now in e->e_id. Make sure the current
			 * entry gets added under the new parent ID.
			 */
			if ( eid != e->e_id ) {
				eidummy.bei_id = e->e_id;
				ei = &eidummy;
			}
		}
		rc = bdb_next_id( op->o_bd, &e->e_id );
		if ( rc ) {
			snprintf( text->bv_val, text->bv_len,
				"next_id failed: %s (%d)",
				db_strerror(rc), rc );
		Debug( LDAP_DEBUG_ANY,
			"=> bdb_tool_next_id: %s\n", text->bv_val, 0, 0 );
			return rc;
		}
		rc = bdb_dn2id_add( op, tid, ei, e );
		if ( rc ) {
			snprintf( text->bv_val, text->bv_len, 
				"dn2id_add failed: %s (%d)",
				db_strerror(rc), rc );
		Debug( LDAP_DEBUG_ANY,
			"=> bdb_tool_next_id: %s\n", text->bv_val, 0, 0 );
		} else if ( hole ) {
			if ( nholes == nhmax - 1 ) {
				if ( holes == hbuf ) {
					holes = ch_malloc( nhmax * sizeof(dn_id) * 2 );
					AC_MEMCPY( holes, hbuf, sizeof(hbuf) );
				} else {
					holes = ch_realloc( holes, nhmax * sizeof(dn_id) * 2 );
				}
				nhmax *= 2;
			}
			ber_dupbv( &holes[nholes].dn, &ndn );
			holes[nholes++].id = e->e_id;
		}
	} else if ( !hole ) {
		unsigned i, j;

		e->e_id = ei->bei_id;

		for ( i=0; i<nholes; i++) {
			if ( holes[i].id == e->e_id ) {
				free(holes[i].dn.bv_val);
				for (j=i;j<nholes;j++) holes[j] = holes[j+1];
				holes[j].id = 0;
				nholes--;
				break;
			} else if ( holes[i].id > e->e_id ) {
				break;
			}
		}
	}
	return rc;
}
示例#11
0
文件: tools.c 项目: rouzier/openldap
static int
bdb_tool_entry_get_int( BackendDB *be, ID id, Entry **ep )
{
	Entry *e = NULL;
	char *dptr;
	int rc, eoff;

	assert( be != NULL );
	assert( slapMode & SLAP_TOOL_MODE );

	if ( ( tool_filter || tool_base ) && id == previd && tool_next_entry != NULL ) {
		*ep = tool_next_entry;
		tool_next_entry = NULL;
		return LDAP_SUCCESS;
	}

	if ( id != previd ) {
		data.ulen = data.dlen = sizeof( ehbuf );
		data.data = ehbuf;
		data.flags |= DB_DBT_PARTIAL;

		BDB_ID2DISK( id, &nid );
		rc = cursor->c_get( cursor, &key, &data, DB_SET );
		if ( rc ) {
			rc = LDAP_OTHER;
			goto done;
		}
	}

	/* Get the header */
	dptr = eh.bv.bv_val;
	eh.bv.bv_val = ehbuf;
	eh.bv.bv_len = data.size;
	rc = entry_header( &eh );
	eoff = eh.data - eh.bv.bv_val;
	eh.bv.bv_val = dptr;
	if ( rc ) {
		rc = LDAP_OTHER;
		goto done;
	}

	/* Get the size */
	data.flags &= ~DB_DBT_PARTIAL;
	data.ulen = 0;
	rc = cursor->c_get( cursor, &key, &data, DB_CURRENT );
	if ( rc != DB_BUFFER_SMALL ) {
		rc = LDAP_OTHER;
		goto done;
	}

	/* Allocate a block and retrieve the data */
	eh.bv.bv_len = eh.nvals * sizeof( struct berval ) + data.size;
	eh.bv.bv_val = ch_realloc( eh.bv.bv_val, eh.bv.bv_len );
	eh.data = eh.bv.bv_val + eh.nvals * sizeof( struct berval );
	data.data = eh.data;
	data.ulen = data.size;

	/* Skip past already parsed nattr/nvals */
	eh.data += eoff;

	rc = cursor->c_get( cursor, &key, &data, DB_CURRENT );
	if ( rc ) {
		rc = LDAP_OTHER;
		goto done;
	}

#ifndef BDB_HIER
	/* TODO: handle BDB_HIER accordingly */
	if ( tool_base != NULL ) {
		struct berval ndn;
		entry_decode_dn( &eh, NULL, &ndn );

		if ( !dnIsSuffixScope( &ndn, tool_base, tool_scope ) ) {
			return LDAP_NO_SUCH_OBJECT;
		}
	}
#endif

#ifdef SLAP_ZONE_ALLOC
	/* FIXME: will add ctx later */
	rc = entry_decode( &eh, &e, NULL );
#else
	rc = entry_decode( &eh, &e );
#endif

	if( rc == LDAP_SUCCESS ) {
		e->e_id = id;
#ifdef BDB_HIER
		if ( slapMode & SLAP_TOOL_READONLY ) {
			struct bdb_info *bdb = (struct bdb_info *) be->be_private;
			EntryInfo *ei = NULL;
			Operation op = {0};
			Opheader ohdr = {0};

			op.o_hdr = &ohdr;
			op.o_bd = be;
			op.o_tmpmemctx = NULL;
			op.o_tmpmfuncs = &ch_mfuncs;

			rc = bdb_cache_find_parent( &op, bdb->bi_cache.c_txn, id, &ei );
			if ( rc == LDAP_SUCCESS ) {
				bdb_cache_entryinfo_unlock( ei );
				e->e_private = ei;
				ei->bei_e = e;
				bdb_fix_dn( e, 0 );
				ei->bei_e = NULL;
				e->e_private = NULL;
			}
		}
#endif
	}
done:
	if ( e != NULL ) {
		*ep = e;
	}

	return rc;
}
示例#12
0
static int
equality_candidates(
	Operation *op,
	DB_TXN *rtxn,
	AttributeAssertion *ava,
	ID *ids,
	ID *tmp )
{
	struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
	DB	*db;
	int i;
	int rc;
	slap_mask_t mask;
	struct berval prefix = {0, NULL};
	struct berval *keys = NULL;
	MatchingRule *mr;

	Debug( LDAP_DEBUG_TRACE, "=> bdb_equality_candidates (%s)\n",
			ava->aa_desc->ad_cname.bv_val, 0, 0 );

	if ( ava->aa_desc == slap_schema.si_ad_entryDN ) {
		EntryInfo *ei = NULL;
		rc = bdb_cache_find_ndn( op, rtxn, &ava->aa_value, &ei );
		if ( rc == LDAP_SUCCESS ) {
			/* exactly one ID can match */
			ids[0] = 1;
			ids[1] = ei->bei_id;
		}
		if ( ei ) {
			bdb_cache_entryinfo_unlock( ei );
		}
		return rc;
	}

	BDB_IDL_ALL( bdb, ids );

	rc = bdb_index_param( op->o_bd, ava->aa_desc, LDAP_FILTER_EQUALITY,
		&db, &mask, &prefix );

	if ( rc == LDAP_INAPPROPRIATE_MATCHING ) {
		Debug( LDAP_DEBUG_ANY,
			"<= bdb_equality_candidates: (%s) not indexed\n", 
			ava->aa_desc->ad_cname.bv_val, 0, 0 );
		return 0;
	}

	if( rc != LDAP_SUCCESS ) {
		Debug( LDAP_DEBUG_ANY,
			"<= bdb_equality_candidates: (%s) "
			"index_param failed (%d)\n",
			ava->aa_desc->ad_cname.bv_val, rc, 0 );
		return 0;
	}

	mr = ava->aa_desc->ad_type->sat_equality;
	if( !mr ) {
		return 0;
	}

	if( !mr->smr_filter ) {
		return 0;
	}

	rc = (mr->smr_filter)(
		LDAP_FILTER_EQUALITY,
		mask,
		ava->aa_desc->ad_type->sat_syntax,
		mr,
		&prefix,
		&ava->aa_value,
		&keys, op->o_tmpmemctx );

	if( rc != LDAP_SUCCESS ) {
		Debug( LDAP_DEBUG_TRACE,
			"<= bdb_equality_candidates: (%s, %s) "
			"MR filter failed (%d)\n",
			prefix.bv_val, ava->aa_desc->ad_cname.bv_val, rc );
		return 0;
	}

	if( keys == NULL ) {
		Debug( LDAP_DEBUG_TRACE,
			"<= bdb_equality_candidates: (%s) no keys\n",
			ava->aa_desc->ad_cname.bv_val, 0, 0 );
		return 0;
	}

	for ( i= 0; keys[i].bv_val != NULL; i++ ) {
		rc = bdb_key_read( op->o_bd, db, rtxn, &keys[i], tmp, NULL, 0 );

		if( rc == DB_NOTFOUND ) {
			BDB_IDL_ZERO( ids );
			rc = 0;
			break;
		} else if( rc != LDAP_SUCCESS ) {
			Debug( LDAP_DEBUG_TRACE,
				"<= bdb_equality_candidates: (%s) "
				"key read failed (%d)\n",
				ava->aa_desc->ad_cname.bv_val, rc, 0 );
			break;
		}

		if( BDB_IDL_IS_ZERO( tmp ) ) {
			Debug( LDAP_DEBUG_TRACE,
				"<= bdb_equality_candidates: (%s) NULL\n", 
				ava->aa_desc->ad_cname.bv_val, 0, 0 );
			BDB_IDL_ZERO( ids );
			break;
		}

		if ( i == 0 ) {
			BDB_IDL_CPY( ids, tmp );
		} else {
			bdb_idl_intersection( ids, tmp );
		}

		if( BDB_IDL_IS_ZERO( ids ) )
			break;
	}

	ber_bvarray_free_x( keys, op->o_tmpmemctx );

	Debug( LDAP_DEBUG_TRACE,
		"<= bdb_equality_candidates: id=%ld, first=%ld, last=%ld\n",
		(long) ids[0],
		(long) BDB_IDL_FIRST(ids),
		(long) BDB_IDL_LAST(ids) );
	return( rc );
}
示例#13
0
static int
ext_candidates(
        Operation *op,
		DB_TXN *rtxn,
        MatchingRuleAssertion *mra,
        ID *ids,
        ID *tmp,
        ID *stack)
{
	struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;

#ifdef LDAP_COMP_MATCH
	/*
	 * Currently Only Component Indexing for componentFilterMatch is supported
	 * Indexing for an extensible filter is not supported yet
	 */
	if ( mra->ma_cf ) {
		return comp_candidates ( op, rtxn, mra, mra->ma_cf, ids, tmp, stack);
	}
#endif
	if ( mra->ma_desc == slap_schema.si_ad_entryDN ) {
		int rc;
		EntryInfo *ei;

		BDB_IDL_ZERO( ids );
		if ( mra->ma_rule == slap_schema.si_mr_distinguishedNameMatch ) {
			ei = NULL;
			rc = bdb_cache_find_ndn( op, rtxn, &mra->ma_value, &ei );
			if ( rc == LDAP_SUCCESS )
				bdb_idl_insert( ids, ei->bei_id );
			if ( ei )
				bdb_cache_entryinfo_unlock( ei );
			return 0;
		} else if ( mra->ma_rule && mra->ma_rule->smr_match ==
			dnRelativeMatch && dnIsSuffix( &mra->ma_value,
				op->o_bd->be_nsuffix )) {
			int scope;
			if ( mra->ma_rule == slap_schema.si_mr_dnSuperiorMatch ) {
				struct berval pdn;
				ei = NULL;
				dnParent( &mra->ma_value, &pdn );
				bdb_cache_find_ndn( op, rtxn, &pdn, &ei );
				if ( ei ) {
					bdb_cache_entryinfo_unlock( ei );
					while ( ei && ei->bei_id ) {
						bdb_idl_insert( ids, ei->bei_id );
						ei = ei->bei_parent;
					}
				}
				return 0;
			}
			if ( mra->ma_rule == slap_schema.si_mr_dnSubtreeMatch )
				scope = LDAP_SCOPE_SUBTREE;
			else if ( mra->ma_rule == slap_schema.si_mr_dnOneLevelMatch )
				scope = LDAP_SCOPE_ONELEVEL;
			else if ( mra->ma_rule == slap_schema.si_mr_dnSubordinateMatch )
				scope = LDAP_SCOPE_SUBORDINATE;
			else
				scope = LDAP_SCOPE_BASE;
			if ( scope > LDAP_SCOPE_BASE ) {
				ei = NULL;
				rc = bdb_cache_find_ndn( op, rtxn, &mra->ma_value, &ei );
				if ( ei )
					bdb_cache_entryinfo_unlock( ei );
				if ( rc == LDAP_SUCCESS ) {
					int sc = op->ors_scope;
					op->ors_scope = scope;
					rc = bdb_dn2idl( op, rtxn, &mra->ma_value, ei, ids,
						stack );
					op->ors_scope = sc;
				}
				return 0;
			}
		}
	}

	BDB_IDL_ALL( bdb, ids );
	return 0;
}
示例#14
0
int
bdb_modrdn( Operation	*op, SlapReply *rs )
{
	struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
	AttributeDescription *children = slap_schema.si_ad_children;
	AttributeDescription *entry = slap_schema.si_ad_entry;
	struct berval	p_dn, p_ndn;
	struct berval	new_dn = {0, NULL}, new_ndn = {0, NULL};
	Entry		*e = NULL;
	Entry		*p = NULL;
	EntryInfo	*ei = NULL, *eip = NULL, *nei = NULL, *neip = NULL;
	/* LDAP v2 supporting correct attribute handling. */
	char textbuf[SLAP_TEXT_BUFLEN];
	size_t textlen = sizeof textbuf;
	DB_TXN		*ltid = NULL, *lt2;
	struct bdb_op_info opinfo = {{{ 0 }}};
	Entry dummy = {0};

	Entry		*np = NULL;			/* newSuperior Entry */
	struct berval	*np_dn = NULL;			/* newSuperior dn */
	struct berval	*np_ndn = NULL;			/* newSuperior ndn */
	struct berval	*new_parent_dn = NULL;	/* np_dn, p_dn, or NULL */

	int		manageDSAit = get_manageDSAit( op );

	DB_LOCK		lock, plock, nplock;

	int		num_retries = 0;

	LDAPControl **preread_ctrl = NULL;
	LDAPControl **postread_ctrl = NULL;
	LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
	int num_ctrls = 0;

	int	rc;

	int parent_is_glue = 0;
	int parent_is_leaf = 0;

#ifdef LDAP_X_TXN
	int settle = 0;
#endif

	Debug( LDAP_DEBUG_TRACE, "==>" LDAP_XSTRING(bdb_modrdn) "(%s,%s,%s)\n",
		op->o_req_dn.bv_val,op->oq_modrdn.rs_newrdn.bv_val,
		op->oq_modrdn.rs_newSup ? op->oq_modrdn.rs_newSup->bv_val : "NULL" );

#ifdef LDAP_X_TXN
	if( op->o_txnSpec ) {
		/* acquire connection lock */
		ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
		if( op->o_conn->c_txn == CONN_TXN_INACTIVE ) {
			rs->sr_text = "invalid transaction identifier";
			rs->sr_err = LDAP_X_TXN_ID_INVALID;
			goto txnReturn;
		} else if( op->o_conn->c_txn == CONN_TXN_SETTLE ) {
			settle=1;
			goto txnReturn;
		}

		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";
		rs->sr_err = LDAP_X_TXN_SPECIFY_OKAY;

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

		if( !settle ) {
			send_ldap_result( op, rs );
			return rs->sr_err;
		}
	}
#endif

	ctrls[num_ctrls] = NULL;

	slap_mods_opattrs( op, &op->orr_modlist, 1 );

	if( 0 ) {
retry:	/* transaction retry */
		if ( dummy.e_attrs ) {
			attrs_free( dummy.e_attrs );
			dummy.e_attrs = NULL;
		}
		if (e != NULL) {
			bdb_unlocked_cache_return_entry_w(&bdb->bi_cache, e);
			e = NULL;
		}
		if (p != NULL) {
			bdb_unlocked_cache_return_entry_r(&bdb->bi_cache, p);
			p = NULL;
		}
		if (np != NULL) {
			bdb_unlocked_cache_return_entry_r(&bdb->bi_cache, np);
			np = NULL;
		}
		Debug( LDAP_DEBUG_TRACE, "==>" LDAP_XSTRING(bdb_modrdn)
				": retrying...\n", 0, 0, 0 );

		rs->sr_err = TXN_ABORT( ltid );
		ltid = NULL;
		LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.boi_oe, OpExtra, oe_next );
		opinfo.boi_oe.oe_key = NULL;
		op->o_do_not_cache = opinfo.boi_acl_cache;
		if( rs->sr_err != 0 ) {
			rs->sr_err = LDAP_OTHER;
			rs->sr_text = "internal error";
			goto return_results;
		}
		if ( op->o_abandon ) {
			rs->sr_err = SLAPD_ABANDON;
			goto return_results;
		}
		parent_is_glue = 0;
		parent_is_leaf = 0;
		bdb_trans_backoff( ++num_retries );
	}

	/* begin transaction */
	rs->sr_err = TXN_BEGIN( bdb->bi_dbenv, NULL, &ltid, 
		bdb->bi_db_opflags );
	rs->sr_text = NULL;
	if( rs->sr_err != 0 ) {
		Debug( LDAP_DEBUG_TRACE,
			LDAP_XSTRING(bdb_modrdn) ": txn_begin failed: "
			"%s (%d)\n", db_strerror(rs->sr_err), rs->sr_err, 0 );
		rs->sr_err = LDAP_OTHER;
		rs->sr_text = "internal error";
		goto return_results;
	}
	Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(bdb_modrdn) ": txn1 id: %x\n",
		ltid->id(ltid), 0, 0 );

	opinfo.boi_oe.oe_key = bdb;
	opinfo.boi_txn = ltid;
	opinfo.boi_err = 0;
	opinfo.boi_acl_cache = op->o_do_not_cache;
	LDAP_SLIST_INSERT_HEAD( &op->o_extra, &opinfo.boi_oe, oe_next );

	/* get entry */
	rs->sr_err = bdb_dn2entry( op, ltid, &op->o_req_ndn, &ei, 1,
		&lock );

	switch( rs->sr_err ) {
	case 0:
	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;
	}

	e = ei->bei_e;
	/* FIXME: dn2entry() should return non-glue entry */
	if (( rs->sr_err == DB_NOTFOUND ) ||
		( !manageDSAit && e && is_entry_glue( e )))
	{
		if( e != NULL ) {
			rs->sr_matched = ch_strdup( e->e_dn );
			rs->sr_ref = is_entry_referral( e )
				? get_entry_referrals( op, e )
				: NULL;
			bdb_unlocked_cache_return_entry_r( &bdb->bi_cache, e);
			e = NULL;

		} else {
			rs->sr_ref = referral_rewrite( default_referral, NULL,
					&op->o_req_dn, LDAP_SCOPE_DEFAULT );
		}

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

		ber_bvarray_free( rs->sr_ref );
		free( (char *)rs->sr_matched );
		rs->sr_ref = NULL;
		rs->sr_matched = NULL;

		goto done;
	}

	if ( get_assert( op ) &&
		( test_filter( op, e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
	{
		rs->sr_err = LDAP_ASSERTION_FAILED;
		goto return_results;
	}

	/* check write on old entry */
	rs->sr_err = access_allowed( op, e, entry, NULL, ACL_WRITE, NULL );
	if ( ! rs->sr_err ) {
		switch( opinfo.boi_err ) {
		case DB_LOCK_DEADLOCK:
		case DB_LOCK_NOTGRANTED:
			goto retry;
		}

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

#ifndef BDB_HIER
	rs->sr_err = bdb_cache_children( op, ltid, e );
	if ( rs->sr_err != DB_NOTFOUND ) {
		switch( rs->sr_err ) {
		case DB_LOCK_DEADLOCK:
		case DB_LOCK_NOTGRANTED:
			goto retry;
		case 0:
			Debug(LDAP_DEBUG_ARGS,
				"<=- " LDAP_XSTRING(bdb_modrdn)
				": non-leaf %s\n",
				op->o_req_dn.bv_val, 0, 0);
			rs->sr_err = LDAP_NOT_ALLOWED_ON_NONLEAF;
			rs->sr_text = "subtree rename not supported";
			break;
		default:
			Debug(LDAP_DEBUG_ARGS,
				"<=- " LDAP_XSTRING(bdb_modrdn)
				": has_children failed: %s (%d)\n",
				db_strerror(rs->sr_err), rs->sr_err, 0 );
			rs->sr_err = LDAP_OTHER;
			rs->sr_text = "internal error";
		}
		goto return_results;
	}
	ei->bei_state |= CACHE_ENTRY_NO_KIDS;
#endif

	if (!manageDSAit && is_entry_referral( e ) ) {
		/* parent is a referral, don't allow add */
		rs->sr_ref = get_entry_referrals( op, e );

		Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(bdb_modrdn)
			": entry %s is referral\n", e->e_dn, 0, 0 );

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

		ber_bvarray_free( rs->sr_ref );
		rs->sr_ref = NULL;
		rs->sr_matched = NULL;
		goto done;
	}

	if ( be_issuffix( op->o_bd, &e->e_nname ) ) {
#ifdef BDB_MULTIPLE_SUFFIXES
		/* Allow renaming one suffix entry to another */
		p_ndn = slap_empty_bv;
#else
		/* There can only be one suffix entry */
		rs->sr_err = LDAP_NAMING_VIOLATION;
		rs->sr_text = "cannot rename suffix entry";
		goto return_results;
#endif
	} else {
		dnParent( &e->e_nname, &p_ndn );
	}
	np_ndn = &p_ndn;
	eip = ei->bei_parent;
	if ( eip && eip->bei_id ) {
		/* Make sure parent entry exist and we can write its 
		 * children.
		 */
		rs->sr_err = bdb_cache_find_id( op, ltid,
			eip->bei_id, &eip, 0, &plock );

		switch( rs->sr_err ) {
		case 0:
		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;
		}

		p = eip->bei_e;
		if( p == NULL) {
			Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(bdb_modrdn)
				": parent does not exist\n", 0, 0, 0);
			rs->sr_err = LDAP_OTHER;
			rs->sr_text = "old entry's parent does not exist";
			goto return_results;
		}
	} else {
		p = (Entry *)&slap_entry_root;
	}

	/* check parent for "children" acl */
	rs->sr_err = access_allowed( op, p,
		children, NULL,
		op->oq_modrdn.rs_newSup == NULL ?
			ACL_WRITE : ACL_WDEL,
		NULL );

	if ( !p_ndn.bv_len )
		p = NULL;

	if ( ! rs->sr_err ) {
		switch( opinfo.boi_err ) {
		case DB_LOCK_DEADLOCK:
		case DB_LOCK_NOTGRANTED:
			goto retry;
		}

		rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
		Debug( LDAP_DEBUG_TRACE, "no access to parent\n", 0,
			0, 0 );
		rs->sr_text = "no write access to old parent's children";
		goto return_results;
	}

	Debug( LDAP_DEBUG_TRACE,
		LDAP_XSTRING(bdb_modrdn) ": wr to children "
		"of entry %s OK\n", p_ndn.bv_val, 0, 0 );
	
	if ( p_ndn.bv_val == slap_empty_bv.bv_val ) {
		p_dn = slap_empty_bv;
	} else {
		dnParent( &e->e_name, &p_dn );
	}

	Debug( LDAP_DEBUG_TRACE,
		LDAP_XSTRING(bdb_modrdn) ": parent dn=%s\n",
		p_dn.bv_val, 0, 0 );

	new_parent_dn = &p_dn;	/* New Parent unless newSuperior given */

	if ( op->oq_modrdn.rs_newSup != NULL ) {
		Debug( LDAP_DEBUG_TRACE, 
			LDAP_XSTRING(bdb_modrdn)
			": new parent \"%s\" requested...\n",
			op->oq_modrdn.rs_newSup->bv_val, 0, 0 );

		/*  newSuperior == oldParent? */
		if( dn_match( &p_ndn, op->oq_modrdn.rs_nnewSup ) ) {
			Debug( LDAP_DEBUG_TRACE, "bdb_back_modrdn: "
				"new parent \"%s\" same as the old parent \"%s\"\n",
				op->oq_modrdn.rs_newSup->bv_val, p_dn.bv_val, 0 );
			op->oq_modrdn.rs_newSup = NULL; /* ignore newSuperior */
		}
	}

	/* There's a BDB_MULTIPLE_SUFFIXES case here that this code doesn't
	 * support. E.g., two suffixes dc=foo,dc=com and dc=bar,dc=net.
	 * We do not allow modDN
	 *   dc=foo,dc=com
	 *    newrdn dc=bar
	 *    newsup dc=net
	 * and we probably should. But since MULTIPLE_SUFFIXES is deprecated
	 * I'm ignoring this problem for now.
	 */
	if ( op->oq_modrdn.rs_newSup != NULL ) {
		if ( op->oq_modrdn.rs_newSup->bv_len ) {
			np_dn = op->oq_modrdn.rs_newSup;
			np_ndn = op->oq_modrdn.rs_nnewSup;

			/* newSuperior == oldParent? - checked above */
			/* newSuperior == entry being moved?, if so ==> ERROR */
			if ( dnIsSuffix( np_ndn, &e->e_nname )) {
				rs->sr_err = LDAP_NO_SUCH_OBJECT;
				rs->sr_text = "new superior not found";
				goto return_results;
			}
			/* Get Entry with dn=newSuperior. Does newSuperior exist? */

			rs->sr_err = bdb_dn2entry( op, ltid, np_ndn,
				&neip, 0, &nplock );

			switch( rs->sr_err ) {
			case 0: np = neip->bei_e;
			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;
			}

			if( np == NULL) {
				Debug( LDAP_DEBUG_TRACE,
					LDAP_XSTRING(bdb_modrdn)
					": newSup(ndn=%s) not here!\n",
					np_ndn->bv_val, 0, 0);
				rs->sr_text = "new superior not found";
				rs->sr_err = LDAP_NO_SUCH_OBJECT;
				goto return_results;
			}

			Debug( LDAP_DEBUG_TRACE,
				LDAP_XSTRING(bdb_modrdn)
				": wr to new parent OK np=%p, id=%ld\n",
				(void *) np, (long) np->e_id, 0 );

			/* check newSuperior for "children" acl */
			rs->sr_err = access_allowed( op, np, children,
				NULL, ACL_WADD, NULL );

			if( ! rs->sr_err ) {
				switch( opinfo.boi_err ) {
				case DB_LOCK_DEADLOCK:
				case DB_LOCK_NOTGRANTED:
					goto retry;
				}

				Debug( LDAP_DEBUG_TRACE,
					LDAP_XSTRING(bdb_modrdn)
					": no wr to newSup children\n",
					0, 0, 0 );
				rs->sr_text = "no write access to new superior's children";
				rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
				goto return_results;
			}

			if ( is_entry_alias( np ) ) {
				/* parent is an alias, don't allow add */
				Debug( LDAP_DEBUG_TRACE,
					LDAP_XSTRING(bdb_modrdn)
					": entry is alias\n",
					0, 0, 0 );
				rs->sr_text = "new superior is an alias";
				rs->sr_err = LDAP_ALIAS_PROBLEM;
				goto return_results;
			}

			if ( is_entry_referral( np ) ) {
				/* parent is a referral, don't allow add */
				Debug( LDAP_DEBUG_TRACE,
					LDAP_XSTRING(bdb_modrdn)
					": entry is referral\n",
					0, 0, 0 );
				rs->sr_text = "new superior is a referral";
				rs->sr_err = LDAP_OTHER;
				goto return_results;
			}

		} else {
			np_dn = NULL;

			/* no parent, modrdn entry directly under root */
			if ( be_issuffix( op->o_bd, (struct berval *)&slap_empty_bv )
				|| be_isupdate( op ) ) {
				np = (Entry *)&slap_entry_root;

				/* check parent for "children" acl */
				rs->sr_err = access_allowed( op, np,
					children, NULL, ACL_WADD, NULL );

				np = NULL;

				if ( ! rs->sr_err ) {
					switch( opinfo.boi_err ) {
					case DB_LOCK_DEADLOCK:
					case DB_LOCK_NOTGRANTED:
						goto retry;
					}

					rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
					Debug( LDAP_DEBUG_TRACE, 
						"no access to new superior\n", 
						0, 0, 0 );
					rs->sr_text =
						"no write access to new superior's children";
					goto return_results;
				}
			}
		}

		Debug( LDAP_DEBUG_TRACE,
			LDAP_XSTRING(bdb_modrdn)
			": wr to new parent's children OK\n",
			0, 0, 0 );

		new_parent_dn = np_dn;
	}

	/* Build target dn and make sure target entry doesn't exist already. */
	if (!new_dn.bv_val) {
		build_new_dn( &new_dn, new_parent_dn, &op->oq_modrdn.rs_newrdn, NULL ); 
	}

	if (!new_ndn.bv_val) {
		struct berval bv = {0, NULL};
		dnNormalize( 0, NULL, NULL, &new_dn, &bv, op->o_tmpmemctx );
		ber_dupbv( &new_ndn, &bv );
		/* FIXME: why not call dnNormalize() w/o ctx? */
		op->o_tmpfree( bv.bv_val, op->o_tmpmemctx );
	}

	Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(bdb_modrdn) ": new ndn=%s\n",
		new_ndn.bv_val, 0, 0 );

	/* Shortcut the search */
	nei = neip ? neip : eip;
	rs->sr_err = bdb_cache_find_ndn ( op, ltid, &new_ndn, &nei );
	if ( nei ) bdb_cache_entryinfo_unlock( nei );
	switch( rs->sr_err ) {
	case DB_LOCK_DEADLOCK:
	case DB_LOCK_NOTGRANTED:
		goto retry;
	case DB_NOTFOUND:
		break;
	case 0:
		/* Allow rename to same DN */
		if ( nei == ei )
			break;
		rs->sr_err = LDAP_ALREADY_EXISTS;
		goto return_results;
	default:
		rs->sr_err = LDAP_OTHER;
		rs->sr_text = "internal error";
		goto return_results;
	}

	assert( op->orr_modlist != NULL );

	if( op->o_preread ) {
		if( preread_ctrl == NULL ) {
			preread_ctrl = &ctrls[num_ctrls++];
			ctrls[num_ctrls] = NULL;
		}
		if( slap_read_controls( op, rs, e,
			&slap_pre_read_bv, preread_ctrl ) )
		{
			Debug( LDAP_DEBUG_TRACE,        
				"<=- " LDAP_XSTRING(bdb_modrdn)
				": 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;
			}
		}                   
	}

	/* 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,
			LDAP_XSTRING(bdb_modrdn)
			": txn_begin(2) failed: %s (%d)\n",
			db_strerror(rs->sr_err), rs->sr_err, 0 );
		rs->sr_err = LDAP_OTHER;
		rs->sr_text = "internal error";
		goto return_results;
	}
	Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(bdb_modrdn) ": txn2 id: %x\n",
		lt2->id(lt2), 0, 0 );

	/* delete old DN */
	rs->sr_err = bdb_dn2id_delete( op, lt2, eip, e );
	if ( rs->sr_err != 0 ) {
		Debug(LDAP_DEBUG_TRACE,
			"<=- " LDAP_XSTRING(bdb_modrdn)
			": dn2id del failed: %s (%d)\n",
			db_strerror(rs->sr_err), rs->sr_err, 0 );
		switch( rs->sr_err ) {
		case DB_LOCK_DEADLOCK:
		case DB_LOCK_NOTGRANTED:
			goto retry;
		}
		rs->sr_err = LDAP_OTHER;
		rs->sr_text = "DN index delete fail";
		goto return_results;
	}

	/* copy the entry, then override some fields */
	dummy = *e;
	dummy.e_name = new_dn;
	dummy.e_nname = new_ndn;
	dummy.e_attrs = NULL;

	/* add new DN */
	rs->sr_err = bdb_dn2id_add( op, lt2, neip ? neip : eip, &dummy );
	if ( rs->sr_err != 0 ) {
		Debug(LDAP_DEBUG_TRACE,
			"<=- " LDAP_XSTRING(bdb_modrdn)
			": dn2id add failed: %s (%d)\n",
			db_strerror(rs->sr_err), rs->sr_err, 0 );
		switch( rs->sr_err ) {
		case DB_LOCK_DEADLOCK:
		case DB_LOCK_NOTGRANTED:
			goto retry;
		}
		rs->sr_err = LDAP_OTHER;
		rs->sr_text = "DN index add failed";
		goto return_results;
	}

	dummy.e_attrs = e->e_attrs;

	/* modify entry */
	rs->sr_err = bdb_modify_internal( op, lt2, op->orr_modlist, &dummy,
		&rs->sr_text, textbuf, textlen );
	if( rs->sr_err != LDAP_SUCCESS ) {
		Debug(LDAP_DEBUG_TRACE,
			"<=- " LDAP_XSTRING(bdb_modrdn)
			": modify failed: %s (%d)\n",
			db_strerror(rs->sr_err), rs->sr_err, 0 );
		if ( ( rs->sr_err == LDAP_INSUFFICIENT_ACCESS ) && opinfo.boi_err ) {
			rs->sr_err = opinfo.boi_err;
		}
		if ( dummy.e_attrs == e->e_attrs ) dummy.e_attrs = NULL;
		switch( rs->sr_err ) {
		case DB_LOCK_DEADLOCK:
		case DB_LOCK_NOTGRANTED:
			goto retry;
		}
		goto return_results;
	}

	/* id2entry index */
	rs->sr_err = bdb_id2entry_update( op->o_bd, lt2, &dummy );
	if ( rs->sr_err != 0 ) {
		Debug(LDAP_DEBUG_TRACE,
			"<=- " LDAP_XSTRING(bdb_modrdn)
			": id2entry failed: %s (%d)\n",
			db_strerror(rs->sr_err), rs->sr_err, 0 );
		switch( rs->sr_err ) {
		case DB_LOCK_DEADLOCK:
		case DB_LOCK_NOTGRANTED:
			goto retry;
		}
		rs->sr_err = LDAP_OTHER;
		rs->sr_text = "entry update failed";
		goto return_results;
	}

	if ( p_ndn.bv_len != 0 ) {
		parent_is_glue = is_entry_glue(p);
		rs->sr_err = bdb_cache_children( op, lt2, p );
		if ( rs->sr_err != DB_NOTFOUND ) {
			switch( rs->sr_err ) {
			case DB_LOCK_DEADLOCK:
			case DB_LOCK_NOTGRANTED:
				goto retry;
			case 0:
				break;
			default:
				Debug(LDAP_DEBUG_ARGS,
					"<=- " LDAP_XSTRING(bdb_modrdn)
					": has_children failed: %s (%d)\n",
					db_strerror(rs->sr_err), rs->sr_err, 0 );
				rs->sr_err = LDAP_OTHER;
				rs->sr_text = "internal error";
				goto return_results;
			}
			parent_is_leaf = 1;
		}
		bdb_unlocked_cache_return_entry_r(&bdb->bi_cache, p);
		p = NULL;
	}

	if ( TXN_COMMIT( lt2, 0 ) != 0 ) {
		rs->sr_err = LDAP_OTHER;
		rs->sr_text = "txn_commit(2) failed";
		goto return_results;
	}

	if( op->o_postread ) {
		if( postread_ctrl == NULL ) {
			postread_ctrl = &ctrls[num_ctrls++];
			ctrls[num_ctrls] = NULL;
		}
		if( slap_read_controls( op, rs, &dummy,
			&slap_post_read_bv, postread_ctrl ) )
		{
			Debug( LDAP_DEBUG_TRACE,        
				"<=- " LDAP_XSTRING(bdb_modrdn)
				": post-read failed!\n", 0, 0, 0 );
			if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
				/* FIXME: is it correct to abort
				 * operation if control fails? */
				goto return_results;
			}
		}                   
	}

	if( op->o_noop ) {
		if(( rs->sr_err=TXN_ABORT( ltid )) != 0 ) {
			rs->sr_text = "txn_abort (no-op) failed";
		} else {
			rs->sr_err = LDAP_X_NO_OPERATION;
			ltid = NULL;
			/* Only free attrs if they were dup'd.  */
			if ( dummy.e_attrs == e->e_attrs ) dummy.e_attrs = NULL;
			goto return_results;
		}

	} else {
		rc = bdb_cache_modrdn( bdb, e, &op->orr_nnewrdn, &dummy, neip,
			ltid, &lock );
		switch( rc ) {
		case DB_LOCK_DEADLOCK:
		case DB_LOCK_NOTGRANTED:
			goto retry;
		}
		dummy.e_attrs = NULL;
		new_dn.bv_val = NULL;
		new_ndn.bv_val = NULL;

		if(( rs->sr_err=TXN_COMMIT( ltid, 0 )) != 0 ) {
			rs->sr_text = "txn_commit failed";
		} else {
			rs->sr_err = LDAP_SUCCESS;
		}
	}
 
	ltid = NULL;
	LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.boi_oe, OpExtra, oe_next );
	opinfo.boi_oe.oe_key = NULL;
 
	if( rs->sr_err != LDAP_SUCCESS ) {
		Debug( LDAP_DEBUG_TRACE,
			LDAP_XSTRING(bdb_modrdn) ": %s : %s (%d)\n",
			rs->sr_text, db_strerror(rs->sr_err), rs->sr_err );
		rs->sr_err = LDAP_OTHER;

		goto return_results;
	}

	Debug(LDAP_DEBUG_TRACE,
		LDAP_XSTRING(bdb_modrdn)
		": rdn modified%s id=%08lx dn=\"%s\"\n",
		op->o_noop ? " (no-op)" : "",
		dummy.e_id, op->o_req_dn.bv_val );
	rs->sr_text = NULL;
	if( num_ctrls ) rs->sr_ctrls = ctrls;

return_results:
	if ( dummy.e_attrs ) {
		attrs_free( dummy.e_attrs );
	}
	send_ldap_result( op, rs );

	if( rs->sr_err == LDAP_SUCCESS && bdb->bi_txn_cp_kbyte ) {
		TXN_CHECKPOINT( bdb->bi_dbenv,
			bdb->bi_txn_cp_kbyte, bdb->bi_txn_cp_min, 0 );
	}
	
	if ( rs->sr_err == LDAP_SUCCESS && parent_is_glue && parent_is_leaf ) {
		op->o_delete_glue_parent = 1;
	}

done:
	slap_graduate_commit_csn( op );

	if( new_dn.bv_val != NULL ) free( new_dn.bv_val );
	if( new_ndn.bv_val != NULL ) free( new_ndn.bv_val );

	/* LDAP v3 Support */
	if( np != NULL ) {
		/* free new parent and reader lock */
		bdb_unlocked_cache_return_entry_r(&bdb->bi_cache, np);
	}

	if( p != NULL ) {
		/* free parent and reader lock */
		bdb_unlocked_cache_return_entry_r(&bdb->bi_cache, p);
	}

	/* free entry */
	if( e != NULL ) {
		bdb_unlocked_cache_return_entry_w( &bdb->bi_cache, e);
	}

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

	if( preread_ctrl != NULL && (*preread_ctrl) != NULL ) {
		slap_sl_free( (*preread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
		slap_sl_free( *preread_ctrl, op->o_tmpmemctx );
	}
	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 );
	}
	return rs->sr_err;
}
示例#15
0
Entry* bdb_tool_entry_get( BackendDB *be, ID id )
{
	Entry *e = NULL;
	char *dptr;
	int rc, eoff;

	assert( be != NULL );
	assert( slapMode & SLAP_TOOL_MODE );

	if ( id != previd ) {
		data.ulen = data.dlen = sizeof( ehbuf );
		data.data = ehbuf;
		data.flags |= DB_DBT_PARTIAL;

		BDB_ID2DISK( id, &nid );
		rc = cursor->c_get( cursor, &key, &data, DB_SET );
		if ( rc ) goto done;
	}

	/* Get the header */
	dptr = eh.bv.bv_val;
	eh.bv.bv_val = ehbuf;
	eh.bv.bv_len = data.size;
	rc = entry_header( &eh );
	eoff = eh.data - eh.bv.bv_val;
	eh.bv.bv_val = dptr;
	if ( rc ) goto done;

	/* Get the size */
	data.flags &= ~DB_DBT_PARTIAL;
	data.ulen = 0;
    rc = cursor->c_get( cursor, &key, &data, DB_CURRENT );
	if ( rc != DB_BUFFER_SMALL ) goto done;

	/* Allocate a block and retrieve the data */
	eh.bv.bv_len = eh.nvals * sizeof( struct berval ) + data.size;
	eh.bv.bv_val = ch_realloc( eh.bv.bv_val, eh.bv.bv_len );
	eh.data = eh.bv.bv_val + eh.nvals * sizeof( struct berval );
	data.data = eh.data;
	data.ulen = data.size;

	/* Skip past already parsed nattr/nvals */
	eh.data += eoff;

    rc = cursor->c_get( cursor, &key, &data, DB_CURRENT );
	if ( rc ) goto done;

#ifdef SLAP_ZONE_ALLOC
	/* FIXME: will add ctx later */
	rc = entry_decode( &eh, &e, NULL );
#else
	rc = entry_decode( &eh, &e );
#endif

	if( rc == LDAP_SUCCESS ) {
		e->e_id = id;
#ifdef BDB_HIER
		if ( slapMode & SLAP_TOOL_READONLY ) {
			EntryInfo *ei = NULL;
			Operation op = {0};
			Opheader ohdr = {0};

			op.o_hdr = &ohdr;
			op.o_bd = be;
			op.o_tmpmemctx = NULL;
			op.o_tmpmfuncs = &ch_mfuncs;

			rc = bdb_cache_find_parent( &op, CURSOR_GETLOCKER(cursor), id, &ei );
			if ( rc == LDAP_SUCCESS ) {
				bdb_cache_entryinfo_unlock( ei );
				e->e_private = ei;
				ei->bei_e = e;
				bdb_fix_dn( e, 0 );
				ei->bei_e = NULL;
				e->e_private = NULL;
			}
		}
#endif
	}
done:
	return e;
}