예제 #1
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;
}
예제 #2
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;
}
예제 #3
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;
}
예제 #4
0
int bdb_id2entry_rw(
	BackendDB *be,
	DB_TXN *tid,
	ID id,
	Entry **e,
	int rw,
	u_int32_t locker,
	DB_LOCK *lock )
{
	struct bdb_info *bdb = (struct bdb_info *) be->be_private;
	DB *db = bdb->bi_id2entry->bdi_db;
	DBT key, data;
	struct berval bv;
	int rc = 0, ret = 0;

	*e = NULL;

	DBTzero( &key );
	key.data = (char *) &id;
	key.size = sizeof(ID);

	DBTzero( &data );
	data.flags = DB_DBT_MALLOC;

	if ((*e = bdb_cache_find_entry_id(bdb->bi_dbenv, &bdb->bi_cache, id, rw, locker, lock)) != NULL) {
		return 0;
	}

	/* fetch it */
	rc = db->get( db, tid, &key, &data, bdb->bi_db_opflags | ( rw ? DB_RMW : 0 ));

	if( rc != 0 ) {
		return rc;
	}

	DBT2bv( &data, &bv );

	rc = entry_decode( &bv, e );

	if( rc == 0 ) {
		(*e)->e_id = id;
	} else {
		/* only free on error. On success, the entry was
		 * decoded in place.
		 */
		ch_free( data.data );
	}

	if ( rc == 0 ) {
#ifdef BDB_HIER
		bdb_fix_dn(be, id, *e);
#endif
		ret = bdb_cache_add_entry_rw( bdb->bi_dbenv,
				&bdb->bi_cache, *e, rw, locker, lock);
		while ( ret == 1 || ret == -1 ) {
			Entry *ee;
			int add_loop_cnt = 0;
			if ( (*e)->e_private != NULL ) {
				free ((*e)->e_private);
			}
			(*e)->e_private = NULL;
			if ( (ee = bdb_cache_find_entry_id
					(bdb->bi_dbenv, &bdb->bi_cache, id, rw, locker, lock) ) != NULL) {
				bdb_entry_return ( *e );
				*e = ee;
				return 0;
			}
			if ( ++add_loop_cnt == BDB_MAX_ADD_LOOP ) {
				bdb_entry_return ( *e );
				*e = NULL;
				return LDAP_BUSY;
			}
		}
		if ( ret != 0 ) {
			if ( (*e)->e_private != NULL )
				free ( (*e)->e_private );
			bdb_entry_return( *e );
			*e = NULL;
		}
		rc = ret;
	}

	if (rc == 0) {
		bdb_cache_entry_commit(*e);
	}

	return rc;
}