1/*	$NetBSD$	*/
2
3/* dn2id.c - routines to deal with the dn2id index */
4/* OpenLDAP: pkg/ldap/servers/slapd/back-bdb/dn2id.c,v 1.137.2.23 2010/06/23 15:57:26 quanah Exp */
5/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6 *
7 * Copyright 2000-2010 The OpenLDAP Foundation.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted only as authorized by the OpenLDAP
12 * Public License.
13 *
14 * A copy of this license is available in the file LICENSE in the
15 * top-level directory of the distribution or, alternatively, at
16 * <http://www.OpenLDAP.org/license.html>.
17 */
18
19#include "portable.h"
20
21#include <stdio.h>
22#include <ac/string.h>
23
24#include "back-bdb.h"
25#include "idl.h"
26#include "lutil.h"
27
28#define bdb_dn2id_lock					BDB_SYMBOL(dn2id_lock)
29
30static int
31bdb_dn2id_lock( struct bdb_info *bdb, struct berval *dn,
32	int rw, DB_TXN *txn, DB_LOCK *lock )
33{
34	int       rc;
35	DBT       lockobj;
36	int       db_rw;
37
38	if (!txn)
39		return 0;
40
41	if (rw)
42		db_rw = DB_LOCK_WRITE;
43	else
44		db_rw = DB_LOCK_READ;
45
46	lockobj.data = dn->bv_val;
47	lockobj.size = dn->bv_len;
48
49	rc = LOCK_GET(bdb->bi_dbenv, TXN_ID(txn), DB_LOCK_NOWAIT,
50					&lockobj, db_rw, lock);
51	return rc;
52}
53
54#ifndef BDB_HIER
55int
56bdb_dn2id_add(
57	Operation *op,
58	DB_TXN *txn,
59	EntryInfo *eip,
60	Entry		*e )
61{
62	struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
63	DB *db = bdb->bi_dn2id->bdi_db;
64	int		rc;
65	DBT		key, data;
66	ID		nid;
67	char		*buf;
68	struct berval	ptr, pdn;
69
70	Debug( LDAP_DEBUG_TRACE, "=> bdb_dn2id_add 0x%lx: \"%s\"\n",
71		e->e_id, e->e_ndn, 0 );
72	assert( e->e_id != NOID );
73
74	DBTzero( &key );
75	key.size = e->e_nname.bv_len + 2;
76	key.ulen = key.size;
77	key.flags = DB_DBT_USERMEM;
78	buf = op->o_tmpalloc( key.size, op->o_tmpmemctx );
79	key.data = buf;
80	buf[0] = DN_BASE_PREFIX;
81	ptr.bv_val = buf + 1;
82	ptr.bv_len = e->e_nname.bv_len;
83	AC_MEMCPY( ptr.bv_val, e->e_nname.bv_val, e->e_nname.bv_len );
84	ptr.bv_val[ptr.bv_len] = '\0';
85
86	DBTzero( &data );
87	data.data = &nid;
88	data.size = sizeof( nid );
89	BDB_ID2DISK( e->e_id, &nid );
90
91	/* store it -- don't override */
92	rc = db->put( db, txn, &key, &data, DB_NOOVERWRITE );
93	if( rc != 0 ) {
94		char buf[ SLAP_TEXT_BUFLEN ];
95		snprintf( buf, sizeof( buf ), "%s => bdb_dn2id_add dn=\"%s\" ID=0x%lx",
96			op->o_log_prefix, e->e_name.bv_val, e->e_id );
97		Debug( LDAP_DEBUG_ANY, "%s: put failed: %s %d\n",
98			buf, db_strerror(rc), rc );
99		goto done;
100	}
101
102#ifndef BDB_MULTIPLE_SUFFIXES
103	if( !be_issuffix( op->o_bd, &ptr ))
104#endif
105	{
106		buf[0] = DN_SUBTREE_PREFIX;
107		rc = db->put( db, txn, &key, &data, DB_NOOVERWRITE );
108		if( rc != 0 ) {
109			Debug( LDAP_DEBUG_ANY,
110			"=> bdb_dn2id_add 0x%lx: subtree (%s) put failed: %d\n",
111			e->e_id, ptr.bv_val, rc );
112			goto done;
113		}
114
115#ifdef BDB_MULTIPLE_SUFFIXES
116	if( !be_issuffix( op->o_bd, &ptr ))
117#endif
118	{
119		dnParent( &ptr, &pdn );
120
121		key.size = pdn.bv_len + 2;
122		key.ulen = key.size;
123		pdn.bv_val[-1] = DN_ONE_PREFIX;
124		key.data = pdn.bv_val-1;
125		ptr = pdn;
126
127		rc = bdb_idl_insert_key( op->o_bd, db, txn, &key, e->e_id );
128
129		if( rc != 0 ) {
130			Debug( LDAP_DEBUG_ANY,
131				"=> bdb_dn2id_add 0x%lx: parent (%s) insert failed: %d\n",
132					e->e_id, ptr.bv_val, rc );
133			goto done;
134		}
135	}
136
137#ifndef BDB_MULTIPLE_SUFFIXES
138	while( !be_issuffix( op->o_bd, &ptr ))
139#else
140	for (;;)
141#endif
142	{
143		ptr.bv_val[-1] = DN_SUBTREE_PREFIX;
144
145		rc = bdb_idl_insert_key( op->o_bd, db, txn, &key, e->e_id );
146
147		if( rc != 0 ) {
148			Debug( LDAP_DEBUG_ANY,
149				"=> bdb_dn2id_add 0x%lx: subtree (%s) insert failed: %d\n",
150					e->e_id, ptr.bv_val, rc );
151			break;
152		}
153#ifdef BDB_MULTIPLE_SUFFIXES
154		if( be_issuffix( op->o_bd, &ptr )) break;
155#endif
156		dnParent( &ptr, &pdn );
157
158		key.size = pdn.bv_len + 2;
159		key.ulen = key.size;
160		key.data = pdn.bv_val - 1;
161		ptr = pdn;
162	}
163	}
164
165done:
166	op->o_tmpfree( buf, op->o_tmpmemctx );
167	Debug( LDAP_DEBUG_TRACE, "<= bdb_dn2id_add 0x%lx: %d\n", e->e_id, rc, 0 );
168	return rc;
169}
170
171int
172bdb_dn2id_delete(
173	Operation *op,
174	DB_TXN *txn,
175	EntryInfo	*eip,
176	Entry		*e )
177{
178	struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
179	DB *db = bdb->bi_dn2id->bdi_db;
180	char		*buf;
181	DBT		key;
182	DB_LOCK	lock;
183	struct berval	pdn, ptr;
184	int		rc;
185
186	Debug( LDAP_DEBUG_TRACE, "=> bdb_dn2id_delete 0x%lx: \"%s\"\n",
187		e->e_id, e->e_ndn, 0 );
188
189	DBTzero( &key );
190	key.size = e->e_nname.bv_len + 2;
191	buf = op->o_tmpalloc( key.size, op->o_tmpmemctx );
192	key.data = buf;
193	key.flags = DB_DBT_USERMEM;
194	buf[0] = DN_BASE_PREFIX;
195	ptr.bv_val = buf+1;
196	ptr.bv_len = e->e_nname.bv_len;
197	AC_MEMCPY( ptr.bv_val, e->e_nname.bv_val, e->e_nname.bv_len );
198	ptr.bv_val[ptr.bv_len] = '\0';
199
200	/* We hold this lock until the TXN completes */
201	rc = bdb_dn2id_lock( bdb, &e->e_nname, 1, txn, &lock );
202	if ( rc ) goto done;
203
204	/* delete it */
205	rc = db->del( db, txn, &key, 0 );
206	if( rc != 0 ) {
207		Debug( LDAP_DEBUG_ANY, "=> bdb_dn2id_delete 0x%lx: delete failed: %s %d\n",
208			e->e_id, db_strerror(rc), rc );
209		goto done;
210	}
211
212#ifndef BDB_MULTIPLE_SUFFIXES
213	if( !be_issuffix( op->o_bd, &ptr ))
214#endif
215	{
216		buf[0] = DN_SUBTREE_PREFIX;
217		rc = bdb_idl_delete_key( op->o_bd, db, txn, &key, e->e_id );
218		if( rc != 0 ) {
219			Debug( LDAP_DEBUG_ANY,
220			"=> bdb_dn2id_delete 0x%lx: subtree (%s) delete failed: %d\n",
221			e->e_id, ptr.bv_val, rc );
222			goto done;
223		}
224
225#ifdef BDB_MULTIPLE_SUFFIXES
226	if( !be_issuffix( op->o_bd, &ptr ))
227#endif
228	{
229		dnParent( &ptr, &pdn );
230
231		key.size = pdn.bv_len + 2;
232		key.ulen = key.size;
233		pdn.bv_val[-1] = DN_ONE_PREFIX;
234		key.data = pdn.bv_val - 1;
235		ptr = pdn;
236
237		rc = bdb_idl_delete_key( op->o_bd, db, txn, &key, e->e_id );
238
239		if( rc != 0 ) {
240			Debug( LDAP_DEBUG_ANY,
241				"=> bdb_dn2id_delete 0x%lx: parent (%s) delete failed: %d\n",
242				e->e_id, ptr.bv_val, rc );
243			goto done;
244		}
245	}
246
247#ifndef BDB_MULTIPLE_SUFFIXES
248	while( !be_issuffix( op->o_bd, &ptr ))
249#else
250	for (;;)
251#endif
252	{
253		ptr.bv_val[-1] = DN_SUBTREE_PREFIX;
254
255		rc = bdb_idl_delete_key( op->o_bd, db, txn, &key, e->e_id );
256		if( rc != 0 ) {
257			Debug( LDAP_DEBUG_ANY,
258				"=> bdb_dn2id_delete 0x%lx: subtree (%s) delete failed: %d\n",
259				e->e_id, ptr.bv_val, rc );
260			goto done;
261		}
262#ifdef BDB_MULTIPLE_SUFFIXES
263		if( be_issuffix( op->o_bd, &ptr )) break;
264#endif
265		dnParent( &ptr, &pdn );
266
267		key.size = pdn.bv_len + 2;
268		key.ulen = key.size;
269		key.data = pdn.bv_val - 1;
270		ptr = pdn;
271	}
272	}
273
274done:
275	op->o_tmpfree( buf, op->o_tmpmemctx );
276	Debug( LDAP_DEBUG_TRACE, "<= bdb_dn2id_delete 0x%lx: %d\n", e->e_id, rc, 0 );
277	return rc;
278}
279
280int
281bdb_dn2id(
282	Operation *op,
283	struct berval	*dn,
284	EntryInfo *ei,
285	DB_TXN *txn,
286	DB_LOCK *lock )
287{
288	struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
289	DB *db = bdb->bi_dn2id->bdi_db;
290	DBC	*cursor;
291	int		rc;
292	DBT		key, data;
293	ID		nid;
294
295	Debug( LDAP_DEBUG_TRACE, "=> bdb_dn2id(\"%s\")\n", dn->bv_val, 0, 0 );
296
297	DBTzero( &key );
298	key.size = dn->bv_len + 2;
299	key.data = op->o_tmpalloc( key.size, op->o_tmpmemctx );
300	((char *)key.data)[0] = DN_BASE_PREFIX;
301	AC_MEMCPY( &((char *)key.data)[1], dn->bv_val, key.size - 1 );
302
303	/* store the ID */
304	DBTzero( &data );
305	data.data = &nid;
306	data.ulen = sizeof(ID);
307	data.flags = DB_DBT_USERMEM;
308
309	rc = db->cursor( db, txn, &cursor, bdb->bi_db_opflags );
310	if ( rc ) goto func_leave;
311
312	rc = bdb_dn2id_lock( bdb, dn, 0, txn, lock );
313	if ( rc ) goto nolock;
314
315	/* fetch it */
316	rc = cursor->c_get( cursor, &key, &data, DB_SET );
317
318nolock:
319	cursor->c_close( cursor );
320func_leave:
321
322	if( rc != 0 ) {
323		Debug( LDAP_DEBUG_TRACE, "<= bdb_dn2id: get failed: %s (%d)\n",
324			db_strerror( rc ), rc, 0 );
325	} else {
326		BDB_DISK2ID( &nid, &ei->bei_id );
327		Debug( LDAP_DEBUG_TRACE, "<= bdb_dn2id: got id=0x%lx\n",
328			ei->bei_id, 0, 0 );
329	}
330	op->o_tmpfree( key.data, op->o_tmpmemctx );
331	return rc;
332}
333
334int
335bdb_dn2id_children(
336	Operation *op,
337	DB_TXN *txn,
338	Entry *e )
339{
340	DBT		key, data;
341	struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
342	DB *db = bdb->bi_dn2id->bdi_db;
343	ID		id;
344	int		rc;
345
346	Debug( LDAP_DEBUG_TRACE, "=> bdb_dn2id_children(\"%s\")\n",
347		e->e_nname.bv_val, 0, 0 );
348	DBTzero( &key );
349	key.size = e->e_nname.bv_len + 2;
350	key.data = op->o_tmpalloc( key.size, op->o_tmpmemctx );
351	((char *)key.data)[0] = DN_ONE_PREFIX;
352	AC_MEMCPY( &((char *)key.data)[1], e->e_nname.bv_val, key.size - 1 );
353
354	if ( bdb->bi_idl_cache_size ) {
355		rc = bdb_idl_cache_get( bdb, db, &key, NULL );
356		if ( rc != LDAP_NO_SUCH_OBJECT ) {
357			op->o_tmpfree( key.data, op->o_tmpmemctx );
358			return rc;
359		}
360	}
361	/* we actually could do a empty get... */
362	DBTzero( &data );
363	data.data = &id;
364	data.ulen = sizeof(id);
365	data.flags = DB_DBT_USERMEM;
366	data.doff = 0;
367	data.dlen = sizeof(id);
368
369	rc = db->get( db, txn, &key, &data, bdb->bi_db_opflags );
370	op->o_tmpfree( key.data, op->o_tmpmemctx );
371
372	Debug( LDAP_DEBUG_TRACE, "<= bdb_dn2id_children(\"%s\"): %s (%d)\n",
373		e->e_nname.bv_val,
374		rc == 0 ? "" : ( rc == DB_NOTFOUND ? "no " :
375			db_strerror(rc) ), rc );
376
377	return rc;
378}
379
380int
381bdb_dn2idl(
382	Operation *op,
383	DB_TXN *txn,
384	struct berval *ndn,
385	EntryInfo *ei,
386	ID *ids,
387	ID *stack )
388{
389	int		rc;
390	DBT		key;
391	struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
392	DB *db = bdb->bi_dn2id->bdi_db;
393	int prefix = ( op->ors_scope == LDAP_SCOPE_ONELEVEL )
394		? DN_ONE_PREFIX : DN_SUBTREE_PREFIX;
395
396	Debug( LDAP_DEBUG_TRACE, "=> bdb_dn2idl(\"%s\")\n",
397		ndn->bv_val, 0, 0 );
398
399#ifndef	BDB_MULTIPLE_SUFFIXES
400	if ( prefix == DN_SUBTREE_PREFIX
401		&& ( ei->bei_id == 0 ||
402		( ei->bei_parent->bei_id == 0 && op->o_bd->be_suffix[0].bv_len ))) {
403		BDB_IDL_ALL(bdb, ids);
404		return 0;
405	}
406#endif
407
408	DBTzero( &key );
409	key.size = ndn->bv_len + 2;
410	key.ulen = key.size;
411	key.flags = DB_DBT_USERMEM;
412	key.data = op->o_tmpalloc( key.size, op->o_tmpmemctx );
413	((char *)key.data)[0] = prefix;
414	AC_MEMCPY( &((char *)key.data)[1], ndn->bv_val, key.size - 1 );
415
416	BDB_IDL_ZERO( ids );
417	rc = bdb_idl_fetch_key( op->o_bd, db, txn, &key, ids, NULL, 0 );
418
419	if( rc != 0 ) {
420		Debug( LDAP_DEBUG_TRACE,
421			"<= bdb_dn2idl: get failed: %s (%d)\n",
422			db_strerror( rc ), rc, 0 );
423
424	} else {
425		Debug( LDAP_DEBUG_TRACE,
426			"<= bdb_dn2idl: id=%ld first=%ld last=%ld\n",
427			(long) ids[0],
428			(long) BDB_IDL_FIRST( ids ), (long) BDB_IDL_LAST( ids ) );
429	}
430
431	op->o_tmpfree( key.data, op->o_tmpmemctx );
432	return rc;
433}
434
435#else	/* BDB_HIER */
436/* Management routines for a hierarchically structured database.
437 *
438 * Instead of a ldbm-style dn2id database, we use a hierarchical one. Each
439 * entry in this database is a struct diskNode, keyed by entryID and with
440 * the data containing the RDN and entryID of the node's children. We use
441 * a B-Tree with sorted duplicates to store all the children of a node under
442 * the same key. Also, the first item under the key contains the entry's own
443 * rdn and the ID of the node's parent, to allow bottom-up tree traversal as
444 * well as top-down. To keep this info first in the list, the high bit of all
445 * subsequent nrdnlen's is always set. This means we can only accomodate
446 * RDNs up to length 32767, but that's fine since full DNs are already
447 * restricted to 8192.
448 *
449 * The diskNode is a variable length structure. This definition is not
450 * directly usable for in-memory manipulation.
451 */
452typedef struct diskNode {
453	unsigned char nrdnlen[2];
454	char nrdn[1];
455	char rdn[1];                        /* variable placement */
456	unsigned char entryID[sizeof(ID)];  /* variable placement */
457} diskNode;
458
459/* Sort function for the sorted duplicate data items of a dn2id key.
460 * Sorts based on normalized RDN, in length order.
461 */
462int
463hdb_dup_compare(
464	DB *db,
465	const DBT *usrkey,
466	const DBT *curkey
467)
468{
469	diskNode *un, *cn;
470	int rc;
471
472	un = (diskNode *)usrkey->data;
473	cn = (diskNode *)curkey->data;
474
475	/* data is not aligned, cannot compare directly */
476	rc = un->nrdnlen[0] - cn->nrdnlen[0];
477	if ( rc ) return rc;
478	rc = un->nrdnlen[1] - cn->nrdnlen[1];
479	if ( rc ) return rc;
480
481	return strcmp( un->nrdn, cn->nrdn );
482}
483
484/* This function constructs a full DN for a given entry.
485 */
486int hdb_fix_dn(
487	Entry *e,
488	int checkit )
489{
490	EntryInfo *ei;
491	int rlen = 0, nrlen = 0;
492	char *ptr, *nptr;
493	int max = 0;
494
495	if ( !e->e_id )
496		return 0;
497
498	/* count length of all DN components */
499	for ( ei = BEI(e); ei && ei->bei_id; ei=ei->bei_parent ) {
500		rlen += ei->bei_rdn.bv_len + 1;
501		nrlen += ei->bei_nrdn.bv_len + 1;
502		if (ei->bei_modrdns > max) max = ei->bei_modrdns;
503	}
504
505	/* See if the entry DN was invalidated by a subtree rename */
506	if ( checkit ) {
507		if ( BEI(e)->bei_modrdns >= max ) {
508			return 0;
509		}
510		/* We found a mismatch, tell the caller to lock it */
511		if ( checkit == 1 ) {
512			return 1;
513		}
514		/* checkit == 2. do the fix. */
515		free( e->e_name.bv_val );
516		free( e->e_nname.bv_val );
517	}
518
519	e->e_name.bv_len = rlen - 1;
520	e->e_nname.bv_len = nrlen - 1;
521	e->e_name.bv_val = ch_malloc(rlen);
522	e->e_nname.bv_val = ch_malloc(nrlen);
523	ptr = e->e_name.bv_val;
524	nptr = e->e_nname.bv_val;
525	for ( ei = BEI(e); ei && ei->bei_id; ei=ei->bei_parent ) {
526		ptr = lutil_strcopy(ptr, ei->bei_rdn.bv_val);
527		nptr = lutil_strcopy(nptr, ei->bei_nrdn.bv_val);
528		if ( ei->bei_parent ) {
529			*ptr++ = ',';
530			*nptr++ = ',';
531		}
532	}
533	BEI(e)->bei_modrdns = max;
534	if ( ptr > e->e_name.bv_val ) ptr[-1] = '\0';
535	if ( nptr > e->e_nname.bv_val ) nptr[-1] = '\0';
536
537	return 0;
538}
539
540/* We add two elements to the DN2ID database - a data item under the parent's
541 * entryID containing the child's RDN and entryID, and an item under the
542 * child's entryID containing the parent's entryID.
543 */
544int
545hdb_dn2id_add(
546	Operation	*op,
547	DB_TXN *txn,
548	EntryInfo	*eip,
549	Entry		*e )
550{
551	struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
552	DB *db = bdb->bi_dn2id->bdi_db;
553	DBT		key, data;
554	ID		nid;
555	int		rc, rlen, nrlen;
556	diskNode *d;
557	char *ptr;
558
559	Debug( LDAP_DEBUG_TRACE, "=> hdb_dn2id_add 0x%lx: \"%s\"\n",
560		e->e_id, e->e_ndn, 0 );
561
562	nrlen = dn_rdnlen( op->o_bd, &e->e_nname );
563	if (nrlen) {
564		rlen = dn_rdnlen( op->o_bd, &e->e_name );
565	} else {
566		nrlen = e->e_nname.bv_len;
567		rlen = e->e_name.bv_len;
568	}
569
570	d = op->o_tmpalloc(sizeof(diskNode) + rlen + nrlen, op->o_tmpmemctx);
571	d->nrdnlen[1] = nrlen & 0xff;
572	d->nrdnlen[0] = (nrlen >> 8) | 0x80;
573	ptr = lutil_strncopy( d->nrdn, e->e_nname.bv_val, nrlen );
574	*ptr++ = '\0';
575	ptr = lutil_strncopy( ptr, e->e_name.bv_val, rlen );
576	*ptr++ = '\0';
577	BDB_ID2DISK( e->e_id, ptr );
578
579	DBTzero(&key);
580	DBTzero(&data);
581	key.size = sizeof(ID);
582	key.flags = DB_DBT_USERMEM;
583	BDB_ID2DISK( eip->bei_id, &nid );
584
585	key.data = &nid;
586
587	/* Need to make dummy root node once. Subsequent attempts
588	 * will fail harmlessly.
589	 */
590	if ( eip->bei_id == 0 ) {
591		diskNode dummy = {{0, 0}, "", "", ""};
592		data.data = &dummy;
593		data.size = sizeof(diskNode);
594		data.flags = DB_DBT_USERMEM;
595
596		db->put( db, txn, &key, &data, DB_NODUPDATA );
597	}
598
599	data.data = d;
600	data.size = sizeof(diskNode) + rlen + nrlen;
601	data.flags = DB_DBT_USERMEM;
602
603	rc = db->put( db, txn, &key, &data, DB_NODUPDATA );
604
605	if (rc == 0) {
606		BDB_ID2DISK( e->e_id, &nid );
607		BDB_ID2DISK( eip->bei_id, ptr );
608		d->nrdnlen[0] ^= 0x80;
609
610		rc = db->put( db, txn, &key, &data, DB_NODUPDATA );
611	}
612
613	/* Update all parents' IDL cache entries */
614	if ( rc == 0 && bdb->bi_idl_cache_size ) {
615		ID tmp[2];
616		char *ptr = ((char *)&tmp[1])-1;
617		key.data = ptr;
618		key.size = sizeof(ID)+1;
619		tmp[1] = eip->bei_id;
620		*ptr = DN_ONE_PREFIX;
621		bdb_idl_cache_add_id( bdb, db, &key, e->e_id );
622		if ( eip->bei_parent ) {
623			*ptr = DN_SUBTREE_PREFIX;
624			for (; eip && eip->bei_parent->bei_id; eip = eip->bei_parent) {
625				tmp[1] = eip->bei_id;
626				bdb_idl_cache_add_id( bdb, db, &key, e->e_id );
627			}
628			/* Handle DB with empty suffix */
629			if ( !op->o_bd->be_suffix[0].bv_len && eip ) {
630				tmp[1] = eip->bei_id;
631				bdb_idl_cache_add_id( bdb, db, &key, e->e_id );
632			}
633		}
634	}
635
636	op->o_tmpfree( d, op->o_tmpmemctx );
637	Debug( LDAP_DEBUG_TRACE, "<= hdb_dn2id_add 0x%lx: %d\n", e->e_id, rc, 0 );
638
639	return rc;
640}
641
642int
643hdb_dn2id_delete(
644	Operation	*op,
645	DB_TXN *txn,
646	EntryInfo	*eip,
647	Entry	*e )
648{
649	struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
650	DB *db = bdb->bi_dn2id->bdi_db;
651	DBT		key, data;
652	DBC	*cursor;
653	diskNode *d;
654	int rc;
655	ID	nid;
656	unsigned char dlen[2];
657	DB_LOCK	lock;
658
659	Debug( LDAP_DEBUG_TRACE, "=> hdb_dn2id_delete 0x%lx: \"%s\"\n",
660		e->e_id, e->e_ndn, 0 );
661
662	DBTzero(&key);
663	key.size = sizeof(ID);
664	key.ulen = key.size;
665	key.flags = DB_DBT_USERMEM;
666	BDB_ID2DISK( eip->bei_id, &nid );
667
668	DBTzero(&data);
669	data.size = sizeof(diskNode) + BEI(e)->bei_nrdn.bv_len - sizeof(ID) - 1;
670	data.ulen = data.size;
671	data.dlen = data.size;
672	data.flags = DB_DBT_USERMEM | DB_DBT_PARTIAL;
673
674	key.data = &nid;
675
676	d = op->o_tmpalloc( data.size, op->o_tmpmemctx );
677	d->nrdnlen[1] = BEI(e)->bei_nrdn.bv_len & 0xff;
678	d->nrdnlen[0] = (BEI(e)->bei_nrdn.bv_len >> 8) | 0x80;
679	dlen[0] = d->nrdnlen[0];
680	dlen[1] = d->nrdnlen[1];
681	memcpy( d->nrdn, BEI(e)->bei_nrdn.bv_val, BEI(e)->bei_nrdn.bv_len+1 );
682	data.data = d;
683
684	rc = db->cursor( db, txn, &cursor, bdb->bi_db_opflags );
685	if ( rc ) goto func_leave;
686
687	/* We hold this lock until the TXN completes */
688	rc = bdb_dn2id_lock( bdb, &e->e_nname, 1, txn, &lock );
689	if ( rc ) goto nolock;
690
691	/* Delete our ID from the parent's list */
692	rc = cursor->c_get( cursor, &key, &data, DB_GET_BOTH_RANGE );
693	if ( rc == 0 ) {
694		if ( dlen[1] == d->nrdnlen[1] && dlen[0] == d->nrdnlen[0] &&
695			!strcmp( d->nrdn, BEI(e)->bei_nrdn.bv_val ))
696			rc = cursor->c_del( cursor, 0 );
697		else
698			rc = DB_NOTFOUND;
699	}
700
701	/* Delete our ID from the tree. With sorted duplicates, this
702	 * will leave any child nodes still hanging around. This is OK
703	 * for modrdn, which will add our info back in later.
704	 */
705	if ( rc == 0 ) {
706		BDB_ID2DISK( e->e_id, &nid );
707		rc = cursor->c_get( cursor, &key, &data, DB_SET );
708		if ( rc == 0 )
709			rc = cursor->c_del( cursor, 0 );
710	}
711
712nolock:
713	cursor->c_close( cursor );
714func_leave:
715	op->o_tmpfree( d, op->o_tmpmemctx );
716
717	/* Delete IDL cache entries */
718	if ( rc == 0 && bdb->bi_idl_cache_size ) {
719		ID tmp[2];
720		char *ptr = ((char *)&tmp[1])-1;
721		key.data = ptr;
722		key.size = sizeof(ID)+1;
723		tmp[1] = eip->bei_id;
724		*ptr = DN_ONE_PREFIX;
725		bdb_idl_cache_del_id( bdb, db, &key, e->e_id );
726		if ( eip ->bei_parent ) {
727			*ptr = DN_SUBTREE_PREFIX;
728			for (; eip && eip->bei_parent->bei_id; eip = eip->bei_parent) {
729				tmp[1] = eip->bei_id;
730				bdb_idl_cache_del_id( bdb, db, &key, e->e_id );
731			}
732			/* Handle DB with empty suffix */
733			if ( !op->o_bd->be_suffix[0].bv_len && eip ) {
734				tmp[1] = eip->bei_id;
735				bdb_idl_cache_del_id( bdb, db, &key, e->e_id );
736			}
737		}
738	}
739	Debug( LDAP_DEBUG_TRACE, "<= hdb_dn2id_delete 0x%lx: %d\n", e->e_id, rc, 0 );
740	return rc;
741}
742
743
744int
745hdb_dn2id(
746	Operation	*op,
747	struct berval	*in,
748	EntryInfo	*ei,
749	DB_TXN *txn,
750	DB_LOCK *lock )
751{
752	struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
753	DB *db = bdb->bi_dn2id->bdi_db;
754	DBT		key, data;
755	DBC	*cursor;
756	int		rc = 0, nrlen;
757	diskNode *d;
758	char	*ptr;
759	unsigned char dlen[2];
760	ID idp, parentID;
761
762	Debug( LDAP_DEBUG_TRACE, "=> hdb_dn2id(\"%s\")\n", in->bv_val, 0, 0 );
763
764	nrlen = dn_rdnlen( op->o_bd, in );
765	if (!nrlen) nrlen = in->bv_len;
766
767	DBTzero(&key);
768	key.size = sizeof(ID);
769	key.data = &idp;
770	key.ulen = sizeof(ID);
771	key.flags = DB_DBT_USERMEM;
772	parentID = ( ei->bei_parent != NULL ) ? ei->bei_parent->bei_id : 0;
773	BDB_ID2DISK( parentID, &idp );
774
775	DBTzero(&data);
776	data.size = sizeof(diskNode) + nrlen - sizeof(ID) - 1;
777	data.ulen = data.size * 3;
778	data.dlen = data.ulen;
779	data.flags = DB_DBT_USERMEM | DB_DBT_PARTIAL;
780
781	rc = db->cursor( db, txn, &cursor, bdb->bi_db_opflags );
782	if ( rc ) return rc;
783
784	d = op->o_tmpalloc( data.size * 3, op->o_tmpmemctx );
785	d->nrdnlen[1] = nrlen & 0xff;
786	d->nrdnlen[0] = (nrlen >> 8) | 0x80;
787	dlen[0] = d->nrdnlen[0];
788	dlen[1] = d->nrdnlen[1];
789	ptr = lutil_strncopy( d->nrdn, in->bv_val, nrlen );
790	*ptr = '\0';
791	data.data = d;
792
793	rc = bdb_dn2id_lock( bdb, in, 0, txn, lock );
794	if ( rc ) goto func_leave;
795
796	rc = cursor->c_get( cursor, &key, &data, DB_GET_BOTH_RANGE );
797	if ( rc == 0 && (dlen[1] != d->nrdnlen[1] || dlen[0] != d->nrdnlen[0] ||
798		strncmp( d->nrdn, in->bv_val, nrlen ))) {
799		rc = DB_NOTFOUND;
800	}
801	if ( rc == 0 ) {
802		ptr = (char *) data.data + data.size - sizeof(ID);
803		BDB_DISK2ID( ptr, &ei->bei_id );
804		ei->bei_rdn.bv_len = data.size - sizeof(diskNode) - nrlen;
805		ptr = d->nrdn + nrlen + 1;
806		ber_str2bv( ptr, ei->bei_rdn.bv_len, 1, &ei->bei_rdn );
807		if ( ei->bei_parent != NULL && !ei->bei_parent->bei_dkids ) {
808			db_recno_t dkids;
809			/* How many children does the parent have? */
810			/* FIXME: do we need to lock the parent
811			 * entryinfo? Seems safe...
812			 */
813			cursor->c_count( cursor, &dkids, 0 );
814			ei->bei_parent->bei_dkids = dkids;
815		}
816	}
817
818func_leave:
819	cursor->c_close( cursor );
820	op->o_tmpfree( d, op->o_tmpmemctx );
821	if( rc != 0 ) {
822		Debug( LDAP_DEBUG_TRACE, "<= hdb_dn2id: get failed: %s (%d)\n",
823			db_strerror( rc ), rc, 0 );
824	} else {
825		Debug( LDAP_DEBUG_TRACE, "<= hdb_dn2id: got id=0x%lx\n",
826			ei->bei_id, 0, 0 );
827	}
828
829	return rc;
830}
831
832int
833hdb_dn2id_parent(
834	Operation *op,
835	DB_TXN *txn,
836	EntryInfo *ei,
837	ID *idp )
838{
839	struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
840	DB *db = bdb->bi_dn2id->bdi_db;
841	DBT		key, data;
842	DBC	*cursor;
843	int		rc = 0;
844	diskNode *d;
845	char	*ptr;
846	ID	nid;
847
848	DBTzero(&key);
849	key.size = sizeof(ID);
850	key.data = &nid;
851	key.ulen = sizeof(ID);
852	key.flags = DB_DBT_USERMEM;
853	BDB_ID2DISK( ei->bei_id, &nid );
854
855	DBTzero(&data);
856	data.flags = DB_DBT_USERMEM;
857
858	rc = db->cursor( db, txn, &cursor, bdb->bi_db_opflags );
859	if ( rc ) return rc;
860
861	data.ulen = sizeof(diskNode) + (SLAP_LDAPDN_MAXLEN * 2);
862	d = op->o_tmpalloc( data.ulen, op->o_tmpmemctx );
863	data.data = d;
864
865	rc = cursor->c_get( cursor, &key, &data, DB_SET );
866	if ( rc == 0 ) {
867		if (d->nrdnlen[0] & 0x80) {
868			rc = LDAP_OTHER;
869		} else {
870			db_recno_t dkids;
871			ptr = (char *) data.data + data.size - sizeof(ID);
872			BDB_DISK2ID( ptr, idp );
873			ei->bei_nrdn.bv_len = (d->nrdnlen[0] << 8) | d->nrdnlen[1];
874			ber_str2bv( d->nrdn, ei->bei_nrdn.bv_len, 1, &ei->bei_nrdn );
875			ei->bei_rdn.bv_len = data.size - sizeof(diskNode) -
876				ei->bei_nrdn.bv_len;
877			ptr = d->nrdn + ei->bei_nrdn.bv_len + 1;
878			ber_str2bv( ptr, ei->bei_rdn.bv_len, 1, &ei->bei_rdn );
879			/* How many children does this node have? */
880			cursor->c_count( cursor, &dkids, 0 );
881			ei->bei_dkids = dkids;
882		}
883	}
884	cursor->c_close( cursor );
885	op->o_tmpfree( d, op->o_tmpmemctx );
886	return rc;
887}
888
889int
890hdb_dn2id_children(
891	Operation *op,
892	DB_TXN *txn,
893	Entry *e )
894{
895	struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
896	DB *db = bdb->bi_dn2id->bdi_db;
897	DBT		key, data;
898	DBC		*cursor;
899	int		rc;
900	ID		id;
901	diskNode d;
902
903	DBTzero(&key);
904	key.size = sizeof(ID);
905	key.data = &e->e_id;
906	key.flags = DB_DBT_USERMEM;
907	BDB_ID2DISK( e->e_id, &id );
908
909	/* IDL cache is in host byte order */
910	if ( bdb->bi_idl_cache_size ) {
911		rc = bdb_idl_cache_get( bdb, db, &key, NULL );
912		if ( rc != LDAP_NO_SUCH_OBJECT ) {
913			return rc;
914		}
915	}
916
917	key.data = &id;
918	DBTzero(&data);
919	data.data = &d;
920	data.ulen = sizeof(d);
921	data.flags = DB_DBT_USERMEM | DB_DBT_PARTIAL;
922	data.dlen = sizeof(d);
923
924	rc = db->cursor( db, txn, &cursor, bdb->bi_db_opflags );
925	if ( rc ) return rc;
926
927	rc = cursor->c_get( cursor, &key, &data, DB_SET );
928	if ( rc == 0 ) {
929		db_recno_t dkids;
930		rc = cursor->c_count( cursor, &dkids, 0 );
931		if ( rc == 0 ) {
932			BEI(e)->bei_dkids = dkids;
933			if ( dkids < 2 ) rc = DB_NOTFOUND;
934		}
935	}
936	cursor->c_close( cursor );
937	return rc;
938}
939
940/* bdb_dn2idl:
941 * We can't just use bdb_idl_fetch_key because
942 * 1 - our data items are longer than just an entry ID
943 * 2 - our data items are sorted alphabetically by nrdn, not by ID.
944 *
945 * We descend the tree recursively, so we define this cookie
946 * to hold our necessary state information. The bdb_dn2idl_internal
947 * function uses this cookie when calling itself.
948 */
949
950struct dn2id_cookie {
951	struct bdb_info *bdb;
952	Operation *op;
953	DB_TXN *txn;
954	EntryInfo *ei;
955	ID *ids;
956	ID *tmp;
957	ID *buf;
958	DB *db;
959	DBC *dbc;
960	DBT key;
961	DBT data;
962	ID dbuf;
963	ID id;
964	ID nid;
965	int rc;
966	int depth;
967	char need_sort;
968	char prefix;
969};
970
971static int
972apply_func(
973	void *data,
974	void *arg )
975{
976	EntryInfo *ei = data;
977	ID *idl = arg;
978
979	bdb_idl_append_one( idl, ei->bei_id );
980	return 0;
981}
982
983static int
984hdb_dn2idl_internal(
985	struct dn2id_cookie *cx
986)
987{
988	BDB_IDL_ZERO( cx->tmp );
989
990	if ( cx->bdb->bi_idl_cache_size ) {
991		char *ptr = ((char *)&cx->id)-1;
992
993		cx->key.data = ptr;
994		cx->key.size = sizeof(ID)+1;
995		if ( cx->prefix == DN_SUBTREE_PREFIX ) {
996			ID *ids = cx->depth ? cx->tmp : cx->ids;
997			*ptr = cx->prefix;
998			cx->rc = bdb_idl_cache_get(cx->bdb, cx->db, &cx->key, ids);
999			if ( cx->rc == LDAP_SUCCESS ) {
1000				if ( cx->depth ) {
1001					bdb_idl_append( cx->ids, cx->tmp );
1002					cx->need_sort = 1;
1003				}
1004				return cx->rc;
1005			}
1006		}
1007		*ptr = DN_ONE_PREFIX;
1008		cx->rc = bdb_idl_cache_get(cx->bdb, cx->db, &cx->key, cx->tmp);
1009		if ( cx->rc == LDAP_SUCCESS ) {
1010			goto gotit;
1011		}
1012		if ( cx->rc == DB_NOTFOUND ) {
1013			return cx->rc;
1014		}
1015	}
1016
1017	bdb_cache_entryinfo_lock( cx->ei );
1018
1019	/* If number of kids in the cache differs from on-disk, load
1020	 * up all the kids from the database
1021	 */
1022	if ( cx->ei->bei_ckids+1 != cx->ei->bei_dkids ) {
1023		EntryInfo ei;
1024		db_recno_t dkids = cx->ei->bei_dkids;
1025		ei.bei_parent = cx->ei;
1026
1027		/* Only one thread should load the cache */
1028		while ( cx->ei->bei_state & CACHE_ENTRY_ONELEVEL ) {
1029			bdb_cache_entryinfo_unlock( cx->ei );
1030			ldap_pvt_thread_yield();
1031			bdb_cache_entryinfo_lock( cx->ei );
1032			if ( cx->ei->bei_ckids+1 == cx->ei->bei_dkids ) {
1033				goto synced;
1034			}
1035		}
1036
1037		cx->ei->bei_state |= CACHE_ENTRY_ONELEVEL;
1038
1039		bdb_cache_entryinfo_unlock( cx->ei );
1040
1041		cx->rc = cx->db->cursor( cx->db, NULL, &cx->dbc,
1042			cx->bdb->bi_db_opflags );
1043		if ( cx->rc )
1044			goto done_one;
1045
1046		cx->data.data = &cx->dbuf;
1047		cx->data.ulen = sizeof(ID);
1048		cx->data.dlen = sizeof(ID);
1049		cx->data.flags = DB_DBT_USERMEM | DB_DBT_PARTIAL;
1050
1051		/* The first item holds the parent ID. Ignore it. */
1052		cx->key.data = &cx->nid;
1053		cx->key.size = sizeof(ID);
1054		cx->rc = cx->dbc->c_get( cx->dbc, &cx->key, &cx->data, DB_SET );
1055		if ( cx->rc ) {
1056			cx->dbc->c_close( cx->dbc );
1057			goto done_one;
1058		}
1059
1060		/* If the on-disk count is zero we've never checked it.
1061		 * Count it now.
1062		 */
1063		if ( !dkids ) {
1064			cx->dbc->c_count( cx->dbc, &dkids, 0 );
1065			cx->ei->bei_dkids = dkids;
1066		}
1067
1068		cx->data.data = cx->buf;
1069		cx->data.ulen = BDB_IDL_UM_SIZE * sizeof(ID);
1070		cx->data.flags = DB_DBT_USERMEM;
1071
1072		if ( dkids > 1 ) {
1073			/* Fetch the rest of the IDs in a loop... */
1074			while ( (cx->rc = cx->dbc->c_get( cx->dbc, &cx->key, &cx->data,
1075				DB_MULTIPLE | DB_NEXT_DUP )) == 0 ) {
1076				u_int8_t *j;
1077				size_t len;
1078				void *ptr;
1079				DB_MULTIPLE_INIT( ptr, &cx->data );
1080				while (ptr) {
1081					DB_MULTIPLE_NEXT( ptr, &cx->data, j, len );
1082					if (j) {
1083						EntryInfo *ei2;
1084						diskNode *d = (diskNode *)j;
1085						short nrlen;
1086
1087						BDB_DISK2ID( j + len - sizeof(ID), &ei.bei_id );
1088						nrlen = ((d->nrdnlen[0] ^ 0x80) << 8) | d->nrdnlen[1];
1089						ei.bei_nrdn.bv_len = nrlen;
1090						/* nrdn/rdn are set in-place.
1091						 * hdb_cache_load will copy them as needed
1092						 */
1093						ei.bei_nrdn.bv_val = d->nrdn;
1094						ei.bei_rdn.bv_len = len - sizeof(diskNode)
1095							- ei.bei_nrdn.bv_len;
1096						ei.bei_rdn.bv_val = d->nrdn + ei.bei_nrdn.bv_len + 1;
1097						bdb_idl_append_one( cx->tmp, ei.bei_id );
1098						hdb_cache_load( cx->bdb, &ei, &ei2 );
1099					}
1100				}
1101			}
1102		}
1103
1104		cx->rc = cx->dbc->c_close( cx->dbc );
1105done_one:
1106		bdb_cache_entryinfo_lock( cx->ei );
1107		cx->ei->bei_state &= ~CACHE_ENTRY_ONELEVEL;
1108		bdb_cache_entryinfo_unlock( cx->ei );
1109		if ( cx->rc )
1110			return cx->rc;
1111
1112	} else {
1113		/* The in-memory cache is in sync with the on-disk data.
1114		 * do we have any kids?
1115		 */
1116synced:
1117		cx->rc = 0;
1118		if ( cx->ei->bei_ckids > 0 ) {
1119			/* Walk the kids tree; order is irrelevant since bdb_idl_sort
1120			 * will sort it later.
1121			 */
1122			avl_apply( cx->ei->bei_kids, apply_func,
1123				cx->tmp, -1, AVL_POSTORDER );
1124		}
1125		bdb_cache_entryinfo_unlock( cx->ei );
1126	}
1127
1128	if ( !BDB_IDL_IS_RANGE( cx->tmp ) && cx->tmp[0] > 3 )
1129		bdb_idl_sort( cx->tmp, cx->buf );
1130	if ( cx->bdb->bi_idl_cache_max_size && !BDB_IDL_IS_ZERO( cx->tmp )) {
1131		char *ptr = ((char *)&cx->id)-1;
1132		cx->key.data = ptr;
1133		cx->key.size = sizeof(ID)+1;
1134		*ptr = DN_ONE_PREFIX;
1135		bdb_idl_cache_put( cx->bdb, cx->db, &cx->key, cx->tmp, cx->rc );
1136	}
1137
1138gotit:
1139	if ( !BDB_IDL_IS_ZERO( cx->tmp )) {
1140		if ( cx->prefix == DN_SUBTREE_PREFIX ) {
1141			bdb_idl_append( cx->ids, cx->tmp );
1142			cx->need_sort = 1;
1143			if ( !(cx->ei->bei_state & CACHE_ENTRY_NO_GRANDKIDS)) {
1144				ID *save, idcurs;
1145				EntryInfo *ei = cx->ei;
1146				int nokids = 1;
1147				save = cx->op->o_tmpalloc( BDB_IDL_SIZEOF( cx->tmp ),
1148					cx->op->o_tmpmemctx );
1149				BDB_IDL_CPY( save, cx->tmp );
1150
1151				idcurs = 0;
1152				cx->depth++;
1153				for ( cx->id = bdb_idl_first( save, &idcurs );
1154					cx->id != NOID;
1155					cx->id = bdb_idl_next( save, &idcurs )) {
1156					EntryInfo *ei2;
1157					cx->ei = NULL;
1158					if ( bdb_cache_find_id( cx->op, cx->txn, cx->id, &cx->ei,
1159						ID_NOENTRY, NULL ))
1160						continue;
1161					if ( cx->ei ) {
1162						ei2 = cx->ei;
1163						if ( !( ei2->bei_state & CACHE_ENTRY_NO_KIDS )) {
1164							BDB_ID2DISK( cx->id, &cx->nid );
1165							hdb_dn2idl_internal( cx );
1166							if ( !BDB_IDL_IS_ZERO( cx->tmp ))
1167								nokids = 0;
1168						}
1169						bdb_cache_entryinfo_lock( ei2 );
1170						ei2->bei_finders--;
1171						bdb_cache_entryinfo_unlock( ei2 );
1172					}
1173				}
1174				cx->depth--;
1175				cx->op->o_tmpfree( save, cx->op->o_tmpmemctx );
1176				if ( nokids ) {
1177					bdb_cache_entryinfo_lock( ei );
1178					ei->bei_state |= CACHE_ENTRY_NO_GRANDKIDS;
1179					bdb_cache_entryinfo_unlock( ei );
1180				}
1181			}
1182			/* Make sure caller knows it had kids! */
1183			cx->tmp[0]=1;
1184
1185			cx->rc = 0;
1186		} else {
1187			BDB_IDL_CPY( cx->ids, cx->tmp );
1188		}
1189	}
1190	return cx->rc;
1191}
1192
1193int
1194hdb_dn2idl(
1195	Operation	*op,
1196	DB_TXN *txn,
1197	struct berval *ndn,
1198	EntryInfo	*ei,
1199	ID *ids,
1200	ID *stack )
1201{
1202	struct bdb_info *bdb = (struct bdb_info *)op->o_bd->be_private;
1203	struct dn2id_cookie cx;
1204
1205	Debug( LDAP_DEBUG_TRACE, "=> hdb_dn2idl(\"%s\")\n",
1206		ndn->bv_val, 0, 0 );
1207
1208#ifndef BDB_MULTIPLE_SUFFIXES
1209	if ( op->ors_scope != LDAP_SCOPE_ONELEVEL &&
1210		( ei->bei_id == 0 ||
1211		( ei->bei_parent->bei_id == 0 && op->o_bd->be_suffix[0].bv_len )))
1212	{
1213		BDB_IDL_ALL( bdb, ids );
1214		return 0;
1215	}
1216#endif
1217
1218	cx.id = ei->bei_id;
1219	BDB_ID2DISK( cx.id, &cx.nid );
1220	cx.ei = ei;
1221	cx.bdb = bdb;
1222	cx.db = cx.bdb->bi_dn2id->bdi_db;
1223	cx.prefix = (op->ors_scope == LDAP_SCOPE_ONELEVEL) ?
1224		DN_ONE_PREFIX : DN_SUBTREE_PREFIX;
1225	cx.ids = ids;
1226	cx.tmp = stack;
1227	cx.buf = stack + BDB_IDL_UM_SIZE;
1228	cx.op = op;
1229	cx.txn = txn;
1230	cx.need_sort = 0;
1231	cx.depth = 0;
1232
1233	if ( cx.prefix == DN_SUBTREE_PREFIX ) {
1234		ids[0] = 1;
1235		ids[1] = cx.id;
1236	} else {
1237		BDB_IDL_ZERO( ids );
1238	}
1239	if ( cx.ei->bei_state & CACHE_ENTRY_NO_KIDS )
1240		return LDAP_SUCCESS;
1241
1242	DBTzero(&cx.key);
1243	cx.key.ulen = sizeof(ID);
1244	cx.key.size = sizeof(ID);
1245	cx.key.flags = DB_DBT_USERMEM;
1246
1247	DBTzero(&cx.data);
1248
1249	hdb_dn2idl_internal(&cx);
1250	if ( cx.need_sort ) {
1251		char *ptr = ((char *)&cx.id)-1;
1252		if ( !BDB_IDL_IS_RANGE( cx.ids ) && cx.ids[0] > 3 )
1253			bdb_idl_sort( cx.ids, cx.tmp );
1254		cx.key.data = ptr;
1255		cx.key.size = sizeof(ID)+1;
1256		*ptr = cx.prefix;
1257		cx.id = ei->bei_id;
1258		if ( cx.bdb->bi_idl_cache_max_size )
1259			bdb_idl_cache_put( cx.bdb, cx.db, &cx.key, cx.ids, cx.rc );
1260	}
1261
1262	if ( cx.rc == DB_NOTFOUND )
1263		cx.rc = LDAP_SUCCESS;
1264
1265	return cx.rc;
1266}
1267#endif	/* BDB_HIER */
1268