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