]> git.sur5r.net Git - openldap/blob - servers/slapd/back-mdb/dn2id.c
More porting
[openldap] / servers / slapd / back-mdb / 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-2011 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-mdb.h"
23 #include "idl.h"
24 #include "lutil.h"
25
26 /* Management routines for a hierarchically structured database.
27  *
28  * Instead of a ldbm-style dn2id database, we use a hierarchical one. Each
29  * entry in this database is a struct diskNode, keyed by entryID and with
30  * the data containing the RDN and entryID of the node's children. We use
31  * a B-Tree with sorted duplicates to store all the children of a node under
32  * the same key. Also, the first item under the key contains the entry's own
33  * rdn and the ID of the node's parent, to allow bottom-up tree traversal as
34  * well as top-down. To keep this info first in the list, the high bit of all
35  * subsequent nrdnlen's is always set. This means we can only accomodate
36  * RDNs up to length 32767, but that's fine since full DNs are already
37  * restricted to 8192.
38  *
39  * The diskNode is a variable length structure. This definition is not
40  * directly usable for in-memory manipulation.
41  */
42 typedef struct diskNode {
43         unsigned char nrdnlen[2];
44         char nrdn[1];
45         char rdn[1];                        /* variable placement */
46         unsigned char entryID[sizeof(ID)];  /* variable placement */
47 } diskNode;
48
49 /* Sort function for the sorted duplicate data items of a dn2id key.
50  * Sorts based on normalized RDN, in length order.
51  */
52 int
53 mdb_dup_compare(
54         const MDB_val *usrkey,
55         const MDB_val *curkey
56 )
57 {
58         diskNode *un, *cn;
59         int rc, nrlen;
60
61         un = (diskNode *)usrkey->mv_data;
62         cn = (diskNode *)curkey->mv_data;
63
64         /* data is not aligned, cannot compare directly */
65         rc = un->nrdnlen[0] - cn->nrdnlen[0];
66         if ( rc ) return rc;
67         rc = un->nrdnlen[1] - cn->nrdnlen[1];
68         if ( rc ) return rc;
69
70         nrlen = (un->nrdnlen[0] << 8) | un->nrdnlen[1];
71         return strncmp( un->nrdn, cn->nrdn, nrlen );
72 }
73
74 #if 0
75 /* This function constructs a full DN for a given entry.
76  */
77 int mdb_fix_dn(
78         Entry *e,
79         int checkit )
80 {
81         EntryInfo *ei;
82         int rlen = 0, nrlen = 0;
83         char *ptr, *nptr;
84         int max = 0;
85
86         if ( !e->e_id )
87                 return 0;
88
89         /* count length of all DN components */
90         for ( ei = BEI(e); ei && ei->bei_id; ei=ei->bei_parent ) {
91                 rlen += ei->bei_rdn.bv_len + 1;
92                 nrlen += ei->bei_nrdn.bv_len + 1;
93                 if (ei->bei_modrdns > max) max = ei->bei_modrdns;
94         }
95
96         /* See if the entry DN was invalidated by a subtree rename */
97         if ( checkit ) {
98                 if ( BEI(e)->bei_modrdns >= max ) {
99                         return 0;
100                 }
101                 /* We found a mismatch, tell the caller to lock it */
102                 if ( checkit == 1 ) {
103                         return 1;
104                 }
105                 /* checkit == 2. do the fix. */
106                 free( e->e_name.bv_val );
107                 free( e->e_nname.bv_val );
108         }
109
110         e->e_name.bv_len = rlen - 1;
111         e->e_nname.bv_len = nrlen - 1;
112         e->e_name.bv_val = ch_malloc(rlen);
113         e->e_nname.bv_val = ch_malloc(nrlen);
114         ptr = e->e_name.bv_val;
115         nptr = e->e_nname.bv_val;
116         for ( ei = BEI(e); ei && ei->bei_id; ei=ei->bei_parent ) {
117                 ptr = lutil_strcopy(ptr, ei->bei_rdn.bv_val);
118                 nptr = lutil_strcopy(nptr, ei->bei_nrdn.bv_val);
119                 if ( ei->bei_parent ) {
120                         *ptr++ = ',';
121                         *nptr++ = ',';
122                 }
123         }
124         BEI(e)->bei_modrdns = max;
125         if ( ptr > e->e_name.bv_val ) ptr[-1] = '\0';
126         if ( nptr > e->e_nname.bv_val ) nptr[-1] = '\0';
127
128         return 0;
129 }
130 #endif
131
132 /* We add two elements to the DN2ID database - a data item under the parent's
133  * entryID containing the child's RDN and entryID, and an item under the
134  * child's entryID containing the parent's entryID.
135  */
136 int
137 mdb_dn2id_add(
138         Operation       *op,
139         MDB_txn *txn,
140         ID pid,
141         Entry           *e )
142 {
143         struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
144         MDB_dbi dbi = mdb->mi_dn2id->mdi_dbi;
145         MDB_val         key, data;
146         ID              nid;
147         int             rc, rlen, nrlen;
148         diskNode *d;
149         char *ptr;
150
151         Debug( LDAP_DEBUG_TRACE, "=> mdb_dn2id_add 0x%lx: \"%s\"\n",
152                 e->e_id, e->e_ndn, 0 );
153
154         nrlen = dn_rdnlen( op->o_bd, &e->e_nname );
155         if (nrlen) {
156                 rlen = dn_rdnlen( op->o_bd, &e->e_name );
157         } else {
158                 nrlen = e->e_nname.bv_len;
159                 rlen = e->e_name.bv_len;
160         }
161
162         d = op->o_tmpalloc(sizeof(diskNode) + rlen + nrlen, op->o_tmpmemctx);
163         d->nrdnlen[1] = nrlen & 0xff;
164         d->nrdnlen[0] = (nrlen >> 8) | 0x80;
165         ptr = lutil_strncopy( d->nrdn, e->e_nname.bv_val, nrlen );
166         *ptr++ = '\0';
167         ptr = lutil_strncopy( ptr, e->e_name.bv_val, rlen );
168         *ptr++ = '\0';
169         memcpy( ptr, &e->e_id, sizeof( ID ));
170
171         key.mv_size = sizeof(ID);
172         key.mv_data = &nid;
173
174         nid = pid;
175
176         /* Need to make dummy root node once. Subsequent attempts
177          * will fail harmlessly.
178          */
179         if ( pid == 0 ) {
180                 diskNode dummy = {{0, 0}, "", "", ""};
181                 data.mv_data = &dummy;
182                 data.mv_size = sizeof(diskNode);
183
184                 mdb_put( txn, dbi, &key, &data, MDB_NODUPDATA );
185         }
186
187         data.mv_data = d;
188         data.mv_size = sizeof(diskNode) + rlen + nrlen;
189
190         rc = mdb_put( txn, dbi, &key, &data, MDB_NODUPDATA );
191
192         if (rc == 0) {
193                 nid = e->e_id;
194                 memcpy( ptr, &pid, sizeof( ID ));
195                 d->nrdnlen[0] ^= 0x80;
196
197                 rc = mdb_put( txn, dbi, &key, &data, MDB_NODUPDATA );
198         }
199
200         op->o_tmpfree( d, op->o_tmpmemctx );
201         Debug( LDAP_DEBUG_TRACE, "<= mdb_dn2id_add 0x%lx: %d\n", e->e_id, rc, 0 );
202
203         return rc;
204 }
205
206 int
207 mdb_dn2id_delete(
208         Operation       *op,
209         MDB_txn *txn,
210         ID pid,
211         Entry   *e )
212 {
213         struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
214         MDB_dbi dbi = mdb->mi_dn2id->mdi_dbi;
215         MDB_val key, data;
216         diskNode *d;
217         int rc, nrlen;
218         ID      nid;
219
220         Debug( LDAP_DEBUG_TRACE, "=> mdb_dn2id_delete 0x%lx: \"%s\"\n",
221                 e->e_id, e->e_ndn, 0 );
222
223         key.mv_size = sizeof(ID);
224         key.mv_data = &nid;
225         nid = pid;
226
227         nrlen = dn_rdnlen( op->o_bd, &e->e_nname );
228         data.mv_size = sizeof(diskNode) + nrlen - sizeof(ID) - 1;
229
230         d = op->o_tmpalloc( data.mv_size, op->o_tmpmemctx );
231         d->nrdnlen[1] = nrlen & 0xff;
232         d->nrdnlen[0] = (nrlen >> 8) | 0x80;
233         memcpy( d->nrdn, e->e_nname.bv_val, nrlen );
234         data.mv_data = d;
235
236         /* Delete our ID from the parent's list */
237         rc = mdb_del( txn, dbi, &key, &data, MDB_DEL_DUP );
238
239         /* Delete our ID from the tree. With sorted duplicates, this
240          * will leave any child nodes still hanging around. This is OK
241          * for modrdn, which will add our info back in later.
242          */
243         if ( rc == 0 ) {
244                 nid = e->e_id;
245                 d->nrdnlen[0] ^= 0x80;
246                 rc = mdb_del( txn, dbi, &key, &data, MDB_DEL_DUP );
247         }
248
249 func_leave:
250         op->o_tmpfree( d, op->o_tmpmemctx );
251
252         Debug( LDAP_DEBUG_TRACE, "<= mdb_dn2id_delete 0x%lx: %d\n", e->e_id, rc, 0 );
253         return rc;
254 }
255
256 /* return last found ID in *id if no match */
257 int
258 mdb_dn2id(
259         Operation       *op,
260         MDB_txn *txn,
261         struct berval   *in,
262         ID      *id,
263         struct berval   *matched )
264 {
265         struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
266         MDB_cursor *cursor;
267         MDB_dbi dbi = mdb->mi_dn2id->mdi_dbi;
268         MDB_val         key, data;
269         int             rc = 0, nrlen;
270         diskNode *d;
271         char    *ptr;
272         char dn[SLAP_LDAPDN_MAXLEN];
273         unsigned char dlen[2];
274         ID pid, nid;
275         struct berval tmp;
276
277         Debug( LDAP_DEBUG_TRACE, "=> mdb_dn2id(\"%s\")\n", in->bv_val, 0, 0 );
278
279         if ( !in->bv_len ) {
280                 *id = 0;
281                 nid = 0;
282                 goto done;
283         }
284
285         tmp = *in;
286
287         if ( matched ) {
288                 matched->bv_val = dn + sizeof(dn) - 1;
289                 matched->bv_len = 0;
290                 *matched->bv_val-- = '\0';
291         }
292
293         nrlen = tmp.bv_len - op->o_bd->be_nsuffix[0].bv_len;
294         tmp.bv_val += nrlen;
295         tmp.bv_len = op->o_bd->be_nsuffix[0].bv_len;
296         nid = 0;
297         key.mv_size = sizeof(ID);
298
299         rc = mdb_cursor_open( txn, dbi, &cursor );
300         if ( rc ) return rc;
301
302         for (;;) {
303                 key.mv_data = &pid;
304                 pid = nid;
305
306                 data.mv_size = sizeof(diskNode) + tmp.bv_len;
307                 d = op->o_tmpalloc( data.mv_size, op->o_tmpmemctx );
308                 d->nrdnlen[1] = tmp.bv_len & 0xff;
309                 d->nrdnlen[0] = (tmp.bv_len >> 8) | 0x80;
310                 ptr = lutil_strncopy( d->nrdn, tmp.bv_val, tmp.bv_len );
311                 *ptr = '\0';
312                 data.mv_data = d;
313                 rc = mdb_cursor_get( cursor, &key, &data, MDB_GET_BOTH );
314                 op->o_tmpfree( d, op->o_tmpmemctx );
315                 if ( rc == MDB_NOTFOUND ) {
316                         if ( matched && matched->bv_len ) {
317                                 ptr = op->o_tmpalloc( matched->bv_len+1, op->o_tmpmemctx );
318                                 strcpy( ptr, matched->bv_val );
319                                 matched->bv_val = ptr;
320                         }
321                 }
322                 if ( rc ) {
323                         mdb_cursor_close( cursor );
324                         break;
325                 }
326                 ptr = (char *) data.mv_data + data.mv_size - sizeof(ID);
327                 memcpy( &nid, ptr, sizeof(ID));
328
329                 /* grab the non-normalized RDN */
330                 if ( matched ) {
331                         int rlen;
332                         d = data.mv_data;
333                         rlen = data.mv_size - sizeof(diskNode) - tmp.bv_len;
334                         matched->bv_len += rlen;
335                         matched->bv_val -= rlen + 1;
336                         ptr = lutil_strcopy( matched->bv_val, d->rdn + tmp.bv_len );
337                         if ( pid ) {
338                                 *ptr = ',';
339                                 matched->bv_len++;
340                         }
341                 }
342                 if ( tmp.bv_val > in->bv_val ) {
343                         for (ptr = tmp.bv_val - 2; ptr > in->bv_val &&
344                                 !DN_SEPARATOR(*ptr); ptr--)     /* empty */;
345                         if ( ptr >= in->bv_val ) {
346                                 if (DN_SEPARATOR(*ptr)) ptr++;
347                                 tmp.bv_len = tmp.bv_val - ptr - 1;
348                                 tmp.bv_val = ptr;
349                         }
350                 } else {
351                         break;
352                 }
353         }
354         *id = nid; 
355
356 done:
357         if( rc != 0 ) {
358                 Debug( LDAP_DEBUG_TRACE, "<= mdb_dn2id: get failed: %s (%d)\n",
359                         mdb_strerror( rc ), rc, 0 );
360         } else {
361                 Debug( LDAP_DEBUG_TRACE, "<= mdb_dn2id: got id=0x%lx\n",
362                         nid, 0, 0 );
363         }
364
365         return rc;
366 }
367
368 #if 0
369 int
370 mdb_dn2id_parent(
371         Operation *op,
372         DB_TXN *txn,
373         EntryInfo *ei,
374         ID *idp )
375 {
376         struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
377         DB *db = mdb->bi_dn2id->bdi_db;
378         DBT             key, data;
379         DBC     *cursor;
380         int             rc = 0;
381         diskNode *d;
382         char    *ptr;
383         ID      nid;
384
385         DBTzero(&key);
386         key.size = sizeof(ID);
387         key.data = &nid;
388         key.ulen = sizeof(ID);
389         key.flags = DB_DBT_USERMEM;
390         MDB_ID2DISK( ei->bei_id, &nid );
391
392         DBTzero(&data);
393         data.flags = DB_DBT_USERMEM;
394
395         rc = db->cursor( db, txn, &cursor, mdb->bi_db_opflags );
396         if ( rc ) return rc;
397
398         data.ulen = sizeof(diskNode) + (SLAP_LDAPDN_MAXLEN * 2);
399         d = op->o_tmpalloc( data.ulen, op->o_tmpmemctx );
400         data.data = d;
401
402         rc = cursor->c_get( cursor, &key, &data, DB_SET );
403         if ( rc == 0 ) {
404                 if (d->nrdnlen[0] & 0x80) {
405                         rc = LDAP_OTHER;
406                 } else {
407                         db_recno_t dkids;
408                         ptr = (char *) data.data + data.size - sizeof(ID);
409                         MDB_DISK2ID( ptr, idp );
410                         ei->bei_nrdn.bv_len = (d->nrdnlen[0] << 8) | d->nrdnlen[1];
411                         ber_str2bv( d->nrdn, ei->bei_nrdn.bv_len, 1, &ei->bei_nrdn );
412                         ei->bei_rdn.bv_len = data.size - sizeof(diskNode) -
413                                 ei->bei_nrdn.bv_len;
414                         ptr = d->nrdn + ei->bei_nrdn.bv_len + 1;
415                         ber_str2bv( ptr, ei->bei_rdn.bv_len, 1, &ei->bei_rdn );
416                         /* How many children does this node have? */
417                         cursor->c_count( cursor, &dkids, 0 );
418                         ei->bei_dkids = dkids;
419                 }
420         }
421         cursor->c_close( cursor );
422         op->o_tmpfree( d, op->o_tmpmemctx );
423         return rc;
424 }
425 #endif
426
427 int
428 mdb_dn2id_children(
429         Operation *op,
430         MDB_txn *txn,
431         Entry *e )
432 {
433         struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
434         MDB_dbi dbi = mdb->mi_dn2id->mdi_dbi;
435         MDB_val         key, data;
436         MDB_cursor      *cursor;
437         int             rc;
438         ID              id;
439
440         key.mv_size = sizeof(ID);
441         key.mv_data = &id;
442         id = e->e_id;
443
444         rc = mdb_cursor_open( txn, dbi, &cursor );
445         if ( rc ) return rc;
446
447         rc = mdb_cursor_get( cursor, &key, &data, MDB_SET );
448         if ( rc == 0 ) {
449                 unsigned long dkids;
450                 rc = mdb_cursor_count( cursor, &dkids );
451                 if ( rc == 0 ) {
452                         if ( dkids < 2 ) rc = MDB_NOTFOUND;
453                 }
454         }
455         mdb_cursor_close( cursor );
456         return rc;
457 }
458
459 int
460 mdb_id2name(
461         Operation *op,
462         MDB_txn *txn,
463         ID id,
464         struct berval *name,
465         struct berval *nname )
466 {
467         struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
468         MDB_dbi dbi = mdb->mi_dn2id->mdi_dbi;
469         MDB_val         key, data;
470         MDB_cursor      *cursor;
471         int             rc, len, nlen;
472         char dn[SLAP_LDAPDN_MAXLEN], ndn[SLAP_LDAPDN_MAXLEN], *ptr;
473         char *dptr, *nptr;
474         diskNode *d;
475
476         key.mv_size = sizeof(ID);
477
478         rc = mdb_cursor_open( txn, dbi, &cursor );
479         if ( rc ) return rc;
480
481         len = 0;
482         nlen = 0;
483         dptr = dn;
484         nptr = ndn;
485         while (id) {
486                 int nrlen, rlen;
487                 key.mv_data = &id;
488                 data.mv_size = 0;
489                 data.mv_data = "";
490                 rc = mdb_cursor_get( cursor, &key, &data, MDB_SET );
491                 if ( rc ) break;
492                 ptr = data.mv_data;
493                 ptr += data.mv_size - sizeof(ID);
494                 memcpy( &id, ptr, sizeof(ID) );
495                 d = data.mv_data;
496                 nrlen = (d->nrdnlen[0] << 8) | d->nrdnlen[1];
497                 if (nptr > ndn) {
498                         *nptr++ = ',';
499                         *dptr++ = ',';
500                 }
501                 /* copy name and trailing NUL */
502                 memcpy( nptr, d->nrdn, nrlen+1 );
503                 rlen = data.mv_size - sizeof(diskNode) - nrlen;
504                 memcpy( dptr, d->nrdn+nrlen+1, rlen+1 );
505                 nptr += nrlen;
506                 dptr += rlen;
507         }
508         if ( rc == 0 ) {
509                 name->bv_len = dptr - dn;
510                 nname->bv_len = nptr - ndn;
511                 name->bv_val = op->o_tmpalloc( name->bv_len + 1, op->o_tmpmemctx );
512                 nname->bv_val = op->o_tmpalloc( nname->bv_len + 1, op->o_tmpmemctx );
513                 memcpy( name->bv_val, dn, name->bv_len );
514                 name->bv_val[name->bv_len] = '\0';
515                 memcpy( nname->bv_val, ndn, nname->bv_len );
516                 nname->bv_val[nname->bv_len] = '\0';
517         }
518         mdb_cursor_close( cursor );
519         return rc;
520 }
521
522 #if 0
523 /* mdb_dn2idl:
524  * We can't just use mdb_idl_fetch_key because
525  * 1 - our data items are longer than just an entry ID
526  * 2 - our data items are sorted alphabetically by nrdn, not by ID.
527  *
528  * We descend the tree recursively, so we define this cookie
529  * to hold our necessary state information. The mdb_dn2idl_internal
530  * function uses this cookie when calling itself.
531  */
532
533 struct dn2id_cookie {
534         struct mdb_info *mdb;
535         Operation *op;
536         DB_TXN *txn;
537         EntryInfo *ei;
538         ID *ids;
539         ID *tmp;
540         ID *buf;
541         DB *db;
542         DBC *dbc;
543         DBT key;
544         DBT data;
545         ID dbuf;
546         ID id;
547         ID nid;
548         int rc;
549         int depth;
550         char need_sort;
551         char prefix;
552 };
553
554 static int
555 apply_func(
556         void *data,
557         void *arg )
558 {
559         EntryInfo *ei = data;
560         ID *idl = arg;
561
562         mdb_idl_append_one( idl, ei->bei_id );
563         return 0;
564 }
565
566 static int
567 mdb_dn2idl_internal(
568         struct dn2id_cookie *cx
569 )
570 {
571         MDB_IDL_ZERO( cx->tmp );
572
573         if ( cx->mdb->bi_idl_cache_size ) {
574                 char *ptr = ((char *)&cx->id)-1;
575
576                 cx->key.data = ptr;
577                 cx->key.size = sizeof(ID)+1;
578                 if ( cx->prefix == DN_SUBTREE_PREFIX ) {
579                         ID *ids = cx->depth ? cx->tmp : cx->ids;
580                         *ptr = cx->prefix;
581                         cx->rc = mdb_idl_cache_get(cx->mdb, cx->db, &cx->key, ids);
582                         if ( cx->rc == LDAP_SUCCESS ) {
583                                 if ( cx->depth ) {
584                                         mdb_idl_append( cx->ids, cx->tmp );
585                                         cx->need_sort = 1;
586                                 }
587                                 return cx->rc;
588                         }
589                 }
590                 *ptr = DN_ONE_PREFIX;
591                 cx->rc = mdb_idl_cache_get(cx->mdb, cx->db, &cx->key, cx->tmp);
592                 if ( cx->rc == LDAP_SUCCESS ) {
593                         goto gotit;
594                 }
595                 if ( cx->rc == DB_NOTFOUND ) {
596                         return cx->rc;
597                 }
598         }
599
600         mdb_cache_entryinfo_lock( cx->ei );
601
602         /* If number of kids in the cache differs from on-disk, load
603          * up all the kids from the database
604          */
605         if ( cx->ei->bei_ckids+1 != cx->ei->bei_dkids ) {
606                 EntryInfo ei;
607                 db_recno_t dkids = cx->ei->bei_dkids;
608                 ei.bei_parent = cx->ei;
609
610                 /* Only one thread should load the cache */
611                 while ( cx->ei->bei_state & CACHE_ENTRY_ONELEVEL ) {
612                         mdb_cache_entryinfo_unlock( cx->ei );
613                         ldap_pvt_thread_yield();
614                         mdb_cache_entryinfo_lock( cx->ei );
615                         if ( cx->ei->bei_ckids+1 == cx->ei->bei_dkids ) {
616                                 goto synced;
617                         }
618                 }
619
620                 cx->ei->bei_state |= CACHE_ENTRY_ONELEVEL;
621
622                 mdb_cache_entryinfo_unlock( cx->ei );
623
624                 cx->rc = cx->db->cursor( cx->db, NULL, &cx->dbc,
625                         cx->mdb->bi_db_opflags );
626                 if ( cx->rc )
627                         goto done_one;
628
629                 cx->data.data = &cx->dbuf;
630                 cx->data.ulen = sizeof(ID);
631                 cx->data.dlen = sizeof(ID);
632                 cx->data.flags = DB_DBT_USERMEM | DB_DBT_PARTIAL;
633
634                 /* The first item holds the parent ID. Ignore it. */
635                 cx->key.data = &cx->nid;
636                 cx->key.size = sizeof(ID);
637                 cx->rc = cx->dbc->c_get( cx->dbc, &cx->key, &cx->data, DB_SET );
638                 if ( cx->rc ) {
639                         cx->dbc->c_close( cx->dbc );
640                         goto done_one;
641                 }
642
643                 /* If the on-disk count is zero we've never checked it.
644                  * Count it now.
645                  */
646                 if ( !dkids ) {
647                         cx->dbc->c_count( cx->dbc, &dkids, 0 );
648                         cx->ei->bei_dkids = dkids;
649                 }
650
651                 cx->data.data = cx->buf;
652                 cx->data.ulen = MDB_IDL_UM_SIZE * sizeof(ID);
653                 cx->data.flags = DB_DBT_USERMEM;
654
655                 if ( dkids > 1 ) {
656                         /* Fetch the rest of the IDs in a loop... */
657                         while ( (cx->rc = cx->dbc->c_get( cx->dbc, &cx->key, &cx->data,
658                                 DB_MULTIPLE | DB_NEXT_DUP )) == 0 ) {
659                                 u_int8_t *j;
660                                 size_t len;
661                                 void *ptr;
662                                 DB_MULTIPLE_INIT( ptr, &cx->data );
663                                 while (ptr) {
664                                         DB_MULTIPLE_NEXT( ptr, &cx->data, j, len );
665                                         if (j) {
666                                                 EntryInfo *ei2;
667                                                 diskNode *d = (diskNode *)j;
668                                                 short nrlen;
669
670                                                 MDB_DISK2ID( j + len - sizeof(ID), &ei.bei_id );
671                                                 nrlen = ((d->nrdnlen[0] ^ 0x80) << 8) | d->nrdnlen[1];
672                                                 ei.bei_nrdn.bv_len = nrlen;
673                                                 /* nrdn/rdn are set in-place.
674                                                  * mdb_cache_load will copy them as needed
675                                                  */
676                                                 ei.bei_nrdn.bv_val = d->nrdn;
677                                                 ei.bei_rdn.bv_len = len - sizeof(diskNode)
678                                                         - ei.bei_nrdn.bv_len;
679                                                 ei.bei_rdn.bv_val = d->nrdn + ei.bei_nrdn.bv_len + 1;
680                                                 mdb_idl_append_one( cx->tmp, ei.bei_id );
681                                                 mdb_cache_load( cx->mdb, &ei, &ei2 );
682                                         }
683                                 }
684                         }
685                 }
686
687                 cx->rc = cx->dbc->c_close( cx->dbc );
688 done_one:
689                 mdb_cache_entryinfo_lock( cx->ei );
690                 cx->ei->bei_state &= ~CACHE_ENTRY_ONELEVEL;
691                 mdb_cache_entryinfo_unlock( cx->ei );
692                 if ( cx->rc )
693                         return cx->rc;
694
695         } else {
696                 /* The in-memory cache is in sync with the on-disk data.
697                  * do we have any kids?
698                  */
699 synced:
700                 cx->rc = 0;
701                 if ( cx->ei->bei_ckids > 0 ) {
702                         /* Walk the kids tree; order is irrelevant since mdb_idl_sort
703                          * will sort it later.
704                          */
705                         avl_apply( cx->ei->bei_kids, apply_func,
706                                 cx->tmp, -1, AVL_POSTORDER );
707                 }
708                 mdb_cache_entryinfo_unlock( cx->ei );
709         }
710
711         if ( !MDB_IDL_IS_RANGE( cx->tmp ) && cx->tmp[0] > 3 )
712                 mdb_idl_sort( cx->tmp, cx->buf );
713         if ( cx->mdb->bi_idl_cache_max_size && !MDB_IDL_IS_ZERO( cx->tmp )) {
714                 char *ptr = ((char *)&cx->id)-1;
715                 cx->key.data = ptr;
716                 cx->key.size = sizeof(ID)+1;
717                 *ptr = DN_ONE_PREFIX;
718                 mdb_idl_cache_put( cx->mdb, cx->db, &cx->key, cx->tmp, cx->rc );
719         }
720
721 gotit:
722         if ( !MDB_IDL_IS_ZERO( cx->tmp )) {
723                 if ( cx->prefix == DN_SUBTREE_PREFIX ) {
724                         mdb_idl_append( cx->ids, cx->tmp );
725                         cx->need_sort = 1;
726                         if ( !(cx->ei->bei_state & CACHE_ENTRY_NO_GRANDKIDS)) {
727                                 ID *save, idcurs;
728                                 EntryInfo *ei = cx->ei;
729                                 int nokids = 1;
730                                 save = cx->op->o_tmpalloc( MDB_IDL_SIZEOF( cx->tmp ),
731                                         cx->op->o_tmpmemctx );
732                                 MDB_IDL_CPY( save, cx->tmp );
733
734                                 idcurs = 0;
735                                 cx->depth++;
736                                 for ( cx->id = mdb_idl_first( save, &idcurs );
737                                         cx->id != NOID;
738                                         cx->id = mdb_idl_next( save, &idcurs )) {
739                                         EntryInfo *ei2;
740                                         cx->ei = NULL;
741                                         if ( mdb_cache_find_id( cx->op, cx->txn, cx->id, &cx->ei,
742                                                 ID_NOENTRY, NULL ))
743                                                 continue;
744                                         if ( cx->ei ) {
745                                                 ei2 = cx->ei;
746                                                 if ( !( ei2->bei_state & CACHE_ENTRY_NO_KIDS )) {
747                                                         MDB_ID2DISK( cx->id, &cx->nid );
748                                                         mdb_dn2idl_internal( cx );
749                                                         if ( !MDB_IDL_IS_ZERO( cx->tmp ))
750                                                                 nokids = 0;
751                                                 }
752                                                 mdb_cache_entryinfo_lock( ei2 );
753                                                 ei2->bei_finders--;
754                                                 mdb_cache_entryinfo_unlock( ei2 );
755                                         }
756                                 }
757                                 cx->depth--;
758                                 cx->op->o_tmpfree( save, cx->op->o_tmpmemctx );
759                                 if ( nokids ) {
760                                         mdb_cache_entryinfo_lock( ei );
761                                         ei->bei_state |= CACHE_ENTRY_NO_GRANDKIDS;
762                                         mdb_cache_entryinfo_unlock( ei );
763                                 }
764                         }
765                         /* Make sure caller knows it had kids! */
766                         cx->tmp[0]=1;
767
768                         cx->rc = 0;
769                 } else {
770                         MDB_IDL_CPY( cx->ids, cx->tmp );
771                 }
772         }
773         return cx->rc;
774 }
775
776 int
777 mdb_dn2idl(
778         Operation       *op,
779         DB_TXN *txn,
780         struct berval *ndn,
781         EntryInfo       *ei,
782         ID *ids,
783         ID *stack )
784 {
785         struct mdb_info *mdb = (struct mdb_info *)op->o_bd->be_private;
786         struct dn2id_cookie cx;
787
788         Debug( LDAP_DEBUG_TRACE, "=> mdb_dn2idl(\"%s\")\n",
789                 ndn->bv_val, 0, 0 );
790
791 #ifndef MDB_MULTIPLE_SUFFIXES
792         if ( op->ors_scope != LDAP_SCOPE_ONELEVEL && 
793                 ( ei->bei_id == 0 ||
794                 ( ei->bei_parent->bei_id == 0 && op->o_bd->be_suffix[0].bv_len )))
795         {
796                 MDB_IDL_ALL( mdb, ids );
797                 return 0;
798         }
799 #endif
800
801         cx.id = ei->bei_id;
802         MDB_ID2DISK( cx.id, &cx.nid );
803         cx.ei = ei;
804         cx.mdb = mdb;
805         cx.db = cx.mdb->bi_dn2id->bdi_db;
806         cx.prefix = (op->ors_scope == LDAP_SCOPE_ONELEVEL) ?
807                 DN_ONE_PREFIX : DN_SUBTREE_PREFIX;
808         cx.ids = ids;
809         cx.tmp = stack;
810         cx.buf = stack + MDB_IDL_UM_SIZE;
811         cx.op = op;
812         cx.txn = txn;
813         cx.need_sort = 0;
814         cx.depth = 0;
815
816         if ( cx.prefix == DN_SUBTREE_PREFIX ) {
817                 ids[0] = 1;
818                 ids[1] = cx.id;
819         } else {
820                 MDB_IDL_ZERO( ids );
821         }
822         if ( cx.ei->bei_state & CACHE_ENTRY_NO_KIDS )
823                 return LDAP_SUCCESS;
824
825         DBTzero(&cx.key);
826         cx.key.ulen = sizeof(ID);
827         cx.key.size = sizeof(ID);
828         cx.key.flags = DB_DBT_USERMEM;
829
830         DBTzero(&cx.data);
831
832         mdb_dn2idl_internal(&cx);
833         if ( cx.need_sort ) {
834                 char *ptr = ((char *)&cx.id)-1;
835                 if ( !MDB_IDL_IS_RANGE( cx.ids ) && cx.ids[0] > 3 ) 
836                         mdb_idl_sort( cx.ids, cx.tmp );
837                 cx.key.data = ptr;
838                 cx.key.size = sizeof(ID)+1;
839                 *ptr = cx.prefix;
840                 cx.id = ei->bei_id;
841                 if ( cx.mdb->bi_idl_cache_max_size )
842                         mdb_idl_cache_put( cx.mdb, cx.db, &cx.key, cx.ids, cx.rc );
843         }
844
845         if ( cx.rc == DB_NOTFOUND )
846                 cx.rc = LDAP_SUCCESS;
847
848         return cx.rc;
849 }
850 #endif