]> git.sur5r.net Git - openldap/blob - servers/slapd/back-mdb/dn2id.c
Plug memleak
[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;
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;
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         op->o_tmpfree( d, op->o_tmpmemctx );
250
251         Debug( LDAP_DEBUG_TRACE, "<= mdb_dn2id_delete 0x%lx: %d\n", e->e_id, rc, 0 );
252         return rc;
253 }
254
255 /* return last found ID in *id if no match */
256 int
257 mdb_dn2id(
258         Operation       *op,
259         MDB_txn *txn,
260         struct berval   *in,
261         ID      *id,
262         struct berval   *matched )
263 {
264         struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
265         MDB_cursor *cursor;
266         MDB_dbi dbi = mdb->mi_dn2id;
267         MDB_val         key, data;
268         int             rc = 0, nrlen;
269         diskNode *d;
270         char    *ptr;
271         char dn[SLAP_LDAPDN_MAXLEN];
272         ID pid, nid;
273         struct berval tmp;
274
275         Debug( LDAP_DEBUG_TRACE, "=> mdb_dn2id(\"%s\")\n", in->bv_val, 0, 0 );
276
277         if ( !in->bv_len ) {
278                 *id = 0;
279                 nid = 0;
280                 goto done;
281         }
282
283         tmp = *in;
284
285         if ( matched ) {
286                 matched->bv_val = dn + sizeof(dn) - 1;
287                 matched->bv_len = 0;
288                 *matched->bv_val-- = '\0';
289         }
290
291         nrlen = tmp.bv_len - op->o_bd->be_nsuffix[0].bv_len;
292         tmp.bv_val += nrlen;
293         tmp.bv_len = op->o_bd->be_nsuffix[0].bv_len;
294         nid = 0;
295         key.mv_size = sizeof(ID);
296
297         rc = mdb_cursor_open( txn, dbi, &cursor );
298         if ( rc ) return rc;
299
300         for (;;) {
301                 key.mv_data = &pid;
302                 pid = nid;
303
304                 data.mv_size = sizeof(diskNode) + tmp.bv_len;
305                 d = op->o_tmpalloc( data.mv_size, op->o_tmpmemctx );
306                 d->nrdnlen[1] = tmp.bv_len & 0xff;
307                 d->nrdnlen[0] = (tmp.bv_len >> 8) | 0x80;
308                 ptr = lutil_strncopy( d->nrdn, tmp.bv_val, tmp.bv_len );
309                 *ptr = '\0';
310                 data.mv_data = d;
311                 rc = mdb_cursor_get( cursor, &key, &data, MDB_GET_BOTH );
312                 op->o_tmpfree( d, op->o_tmpmemctx );
313                 if ( rc )
314                         break;
315                 ptr = (char *) data.mv_data + data.mv_size - sizeof(ID);
316                 memcpy( &nid, ptr, sizeof(ID));
317
318                 /* grab the non-normalized RDN */
319                 if ( matched ) {
320                         int rlen;
321                         d = data.mv_data;
322                         rlen = data.mv_size - sizeof(diskNode) - tmp.bv_len;
323                         matched->bv_len += rlen;
324                         matched->bv_val -= rlen + 1;
325                         ptr = lutil_strcopy( matched->bv_val, d->rdn + tmp.bv_len );
326                         if ( pid ) {
327                                 *ptr = ',';
328                                 matched->bv_len++;
329                         }
330                 }
331                 if ( tmp.bv_val > in->bv_val ) {
332                         for (ptr = tmp.bv_val - 2; ptr > in->bv_val &&
333                                 !DN_SEPARATOR(*ptr); ptr--)     /* empty */;
334                         if ( ptr >= in->bv_val ) {
335                                 if (DN_SEPARATOR(*ptr)) ptr++;
336                                 tmp.bv_len = tmp.bv_val - ptr - 1;
337                                 tmp.bv_val = ptr;
338                         }
339                 } else {
340                         break;
341                 }
342         }
343         *id = nid; 
344         mdb_cursor_close( cursor );
345         if ( matched && matched->bv_len ) {
346                 ptr = op->o_tmpalloc( matched->bv_len+1, op->o_tmpmemctx );
347                 strcpy( ptr, matched->bv_val );
348                 matched->bv_val = ptr;
349         }
350
351 done:
352         if( rc != 0 ) {
353                 Debug( LDAP_DEBUG_TRACE, "<= mdb_dn2id: get failed: %s (%d)\n",
354                         mdb_strerror( rc ), rc, 0 );
355         } else {
356                 Debug( LDAP_DEBUG_TRACE, "<= mdb_dn2id: got id=0x%lx\n",
357                         nid, 0, 0 );
358         }
359
360         return rc;
361 }
362
363 /* return IDs from root to parent of DN */
364 int
365 mdb_dn2sups(
366         Operation       *op,
367         MDB_txn *txn,
368         struct berval   *in,
369         ID      *ids )
370 {
371         struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
372         MDB_cursor *cursor;
373         MDB_dbi dbi = mdb->mi_dn2id;
374         MDB_val         key, data;
375         int             rc = 0, nrlen;
376         diskNode *d;
377         char    *ptr;
378         ID pid, nid;
379         struct berval tmp;
380
381         Debug( LDAP_DEBUG_TRACE, "=> mdb_dn2sups(\"%s\")\n", in->bv_val, 0, 0 );
382
383         if ( !in->bv_len ) {
384                 goto done;
385         }
386
387         tmp = *in;
388
389         nrlen = tmp.bv_len - op->o_bd->be_nsuffix[0].bv_len;
390         tmp.bv_val += nrlen;
391         tmp.bv_len = op->o_bd->be_nsuffix[0].bv_len;
392         nid = 0;
393         key.mv_size = sizeof(ID);
394
395         rc = mdb_cursor_open( txn, dbi, &cursor );
396         if ( rc ) return rc;
397
398         for (;;) {
399                 key.mv_data = &pid;
400                 pid = nid;
401
402                 data.mv_size = sizeof(diskNode) + tmp.bv_len;
403                 d = op->o_tmpalloc( data.mv_size, op->o_tmpmemctx );
404                 d->nrdnlen[1] = tmp.bv_len & 0xff;
405                 d->nrdnlen[0] = (tmp.bv_len >> 8) | 0x80;
406                 ptr = lutil_strncopy( d->nrdn, tmp.bv_val, tmp.bv_len );
407                 *ptr = '\0';
408                 data.mv_data = d;
409                 rc = mdb_cursor_get( cursor, &key, &data, MDB_GET_BOTH );
410                 op->o_tmpfree( d, op->o_tmpmemctx );
411                 if ( rc ) {
412                         mdb_cursor_close( cursor );
413                         break;
414                 }
415                 ptr = (char *) data.mv_data + data.mv_size - sizeof(ID);
416                 memcpy( &nid, ptr, sizeof(ID));
417
418                 if ( pid )
419                         mdb_idl_insert( ids, pid );
420
421                 if ( tmp.bv_val > in->bv_val ) {
422                         for (ptr = tmp.bv_val - 2; ptr > in->bv_val &&
423                                 !DN_SEPARATOR(*ptr); ptr--)     /* empty */;
424                         if ( ptr >= in->bv_val ) {
425                                 if (DN_SEPARATOR(*ptr)) ptr++;
426                                 tmp.bv_len = tmp.bv_val - ptr - 1;
427                                 tmp.bv_val = ptr;
428                         }
429                 } else {
430                         break;
431                 }
432         }
433
434 done:
435         if( rc != 0 ) {
436                 Debug( LDAP_DEBUG_TRACE, "<= mdb_dn2sups: get failed: %s (%d)\n",
437                         mdb_strerror( rc ), rc, 0 );
438         }
439
440         return rc;
441 }
442
443 #if 0
444 int
445 mdb_dn2id_parent(
446         Operation *op,
447         DB_TXN *txn,
448         EntryInfo *ei,
449         ID *idp )
450 {
451         struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
452         DB *db = mdb->bi_dn2id->bdi_db;
453         DBT             key, data;
454         DBC     *cursor;
455         int             rc = 0;
456         diskNode *d;
457         char    *ptr;
458         ID      nid;
459
460         DBTzero(&key);
461         key.size = sizeof(ID);
462         key.data = &nid;
463         key.ulen = sizeof(ID);
464         key.flags = DB_DBT_USERMEM;
465         MDB_ID2DISK( ei->bei_id, &nid );
466
467         DBTzero(&data);
468         data.flags = DB_DBT_USERMEM;
469
470         rc = db->cursor( db, txn, &cursor, mdb->bi_db_opflags );
471         if ( rc ) return rc;
472
473         data.ulen = sizeof(diskNode) + (SLAP_LDAPDN_MAXLEN * 2);
474         d = op->o_tmpalloc( data.ulen, op->o_tmpmemctx );
475         data.data = d;
476
477         rc = cursor->c_get( cursor, &key, &data, DB_SET );
478         if ( rc == 0 ) {
479                 if (d->nrdnlen[0] & 0x80) {
480                         rc = LDAP_OTHER;
481                 } else {
482                         db_recno_t dkids;
483                         ptr = (char *) data.data + data.size - sizeof(ID);
484                         MDB_DISK2ID( ptr, idp );
485                         ei->bei_nrdn.bv_len = (d->nrdnlen[0] << 8) | d->nrdnlen[1];
486                         ber_str2bv( d->nrdn, ei->bei_nrdn.bv_len, 1, &ei->bei_nrdn );
487                         ei->bei_rdn.bv_len = data.size - sizeof(diskNode) -
488                                 ei->bei_nrdn.bv_len;
489                         ptr = d->nrdn + ei->bei_nrdn.bv_len + 1;
490                         ber_str2bv( ptr, ei->bei_rdn.bv_len, 1, &ei->bei_rdn );
491                         /* How many children does this node have? */
492                         cursor->c_count( cursor, &dkids, 0 );
493                         ei->bei_dkids = dkids;
494                 }
495         }
496         cursor->c_close( cursor );
497         op->o_tmpfree( d, op->o_tmpmemctx );
498         return rc;
499 }
500 #endif
501
502 int
503 mdb_dn2id_children(
504         Operation *op,
505         MDB_txn *txn,
506         Entry *e )
507 {
508         struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
509         MDB_dbi dbi = mdb->mi_dn2id;
510         MDB_val         key, data;
511         MDB_cursor      *cursor;
512         int             rc;
513         ID              id;
514
515         key.mv_size = sizeof(ID);
516         key.mv_data = &id;
517         id = e->e_id;
518
519         rc = mdb_cursor_open( txn, dbi, &cursor );
520         if ( rc ) return rc;
521
522         rc = mdb_cursor_get( cursor, &key, &data, MDB_SET );
523         if ( rc == 0 ) {
524                 unsigned long dkids;
525                 rc = mdb_cursor_count( cursor, &dkids );
526                 if ( rc == 0 ) {
527                         if ( dkids < 2 ) rc = MDB_NOTFOUND;
528                 }
529         }
530         mdb_cursor_close( cursor );
531         return rc;
532 }
533
534 int
535 mdb_id2name(
536         Operation *op,
537         MDB_txn *txn,
538         MDB_cursor **cursp,
539         ID id,
540         struct berval *name,
541         struct berval *nname )
542 {
543         struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
544         MDB_dbi dbi = mdb->mi_dn2id;
545         MDB_val         key, data;
546         MDB_cursor      *cursor;
547         int             rc, len, nlen;
548         char dn[SLAP_LDAPDN_MAXLEN], ndn[SLAP_LDAPDN_MAXLEN], *ptr;
549         char *dptr, *nptr;
550         diskNode *d;
551
552         key.mv_size = sizeof(ID);
553
554         if ( !*cursp ) {
555                 rc = mdb_cursor_open( txn, dbi, cursp );
556                 if ( rc ) return rc;
557         }
558         cursor = *cursp;
559
560         len = 0;
561         nlen = 0;
562         dptr = dn;
563         nptr = ndn;
564         while (id) {
565                 int nrlen, rlen;
566                 key.mv_data = &id;
567                 data.mv_size = 0;
568                 data.mv_data = "";
569                 rc = mdb_cursor_get( cursor, &key, &data, MDB_SET );
570                 if ( rc ) break;
571                 ptr = data.mv_data;
572                 ptr += data.mv_size - sizeof(ID);
573                 memcpy( &id, ptr, sizeof(ID) );
574                 d = data.mv_data;
575                 nrlen = (d->nrdnlen[0] << 8) | d->nrdnlen[1];
576                 if (nptr > ndn) {
577                         *nptr++ = ',';
578                         *dptr++ = ',';
579                 }
580                 /* copy name and trailing NUL */
581                 memcpy( nptr, d->nrdn, nrlen+1 );
582                 rlen = data.mv_size - sizeof(diskNode) - nrlen;
583                 memcpy( dptr, d->nrdn+nrlen+1, rlen+1 );
584                 nptr += nrlen;
585                 dptr += rlen;
586         }
587         if ( rc == 0 ) {
588                 name->bv_len = dptr - dn;
589                 nname->bv_len = nptr - ndn;
590                 name->bv_val = op->o_tmpalloc( name->bv_len + 1, op->o_tmpmemctx );
591                 nname->bv_val = op->o_tmpalloc( nname->bv_len + 1, op->o_tmpmemctx );
592                 memcpy( name->bv_val, dn, name->bv_len );
593                 name->bv_val[name->bv_len] = '\0';
594                 memcpy( nname->bv_val, ndn, nname->bv_len );
595                 nname->bv_val[nname->bv_len] = '\0';
596         }
597         return rc;
598 }
599
600 /* Find each id in ids that is a child of base and move it to res.
601  */
602 int
603 mdb_idscope(
604         Operation *op,
605         MDB_txn *txn,
606         ID base,
607         ID *ids,
608         ID *res )
609 {
610         struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
611         MDB_dbi dbi = mdb->mi_dn2id;
612         MDB_val         key, data;
613         MDB_cursor      *cursor;
614         ID ida, id, cid, ci0, idc = 0;
615         char    *ptr;
616         int             rc;
617
618         key.mv_size = sizeof(ID);
619
620         MDB_IDL_ZERO( res );
621
622         rc = mdb_cursor_open( txn, dbi, &cursor );
623         if ( rc ) return rc;
624
625         ida = mdb_idl_first( ids, &cid );
626
627         /* Don't bother moving out of ids if it's a range */
628         if (!MDB_IDL_IS_RANGE(ids)) {
629                 idc = ids[0];
630                 ci0 = cid;
631         }
632
633         while (ida != NOID) {
634                 id = ida;
635                 while (id) {
636                         key.mv_data = &id;
637                         rc = mdb_cursor_get( cursor, &key, &data, MDB_SET );
638                         if ( rc ) {
639                                 /* not found, move on to next */
640                                 if (idc) {
641                                         if (ci0 != cid)
642                                                 ids[ci0] = ids[cid];
643                                         ci0++;
644                                 }
645                                 break;
646                         }
647                         ptr = data.mv_data;
648                         ptr += data.mv_size - sizeof(ID);
649                         memcpy( &id, ptr, sizeof(ID) );
650                         if ( id == base ) {
651                                 res[0]++;
652                                 res[res[0]] = ida;
653                                 if (idc)
654                                         idc--;
655                                 break;
656                         } else {
657                                 if (idc) {
658                                         if (ci0 != cid)
659                                                 ids[ci0] = ids[cid];
660                                         ci0++;
661                                 }
662                         }
663                         if ( op->ors_scope == LDAP_SCOPE_ONELEVEL )
664                                 break;
665                 }
666                 ida = mdb_idl_next( ids, &cid );
667         }
668         if (!MDB_IDL_IS_RANGE( ids ))
669                 ids[0] = idc;
670
671         mdb_cursor_close( cursor );
672         return rc;
673 }
674
675 /* See if base is a child of any of the scopes
676  */
677 int
678 mdb_idscopes(
679         Operation *op,
680         MDB_txn *txn,
681         MDB_cursor **cursp,
682         ID base,
683         ID *scopes )
684 {
685         struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
686         MDB_dbi dbi = mdb->mi_dn2id;
687         MDB_val         key, data;
688         MDB_cursor      *cursor;
689         ID id;
690         char    *ptr;
691         int             rc;
692         unsigned int x;
693
694         key.mv_size = sizeof(ID);
695
696         if ( !*cursp ) {
697                 rc = mdb_cursor_open( txn, dbi, cursp );
698                 if ( rc ) return rc;
699         }
700         cursor = *cursp;
701
702         id = base;
703         while (id) {
704                 key.mv_data = &id;
705                 rc = mdb_cursor_get( cursor, &key, &data, MDB_SET );
706                 if ( rc )
707                         break;
708                 ptr = data.mv_data;
709                 ptr += data.mv_size - sizeof(ID);
710                 memcpy( &id, ptr, sizeof(ID) );
711                 x = mdb_idl_search( scopes, id );
712                 if ( scopes[x] == id )
713                         return MDB_SUCCESS;
714                 if ( op->ors_scope == LDAP_SCOPE_ONELEVEL )
715                         break;
716         }
717         return MDB_NOTFOUND;
718 }
719
720 #if 0
721 /* mdb_dn2idl:
722  * We can't just use mdb_idl_fetch_key because
723  * 1 - our data items are longer than just an entry ID
724  * 2 - our data items are sorted alphabetically by nrdn, not by ID.
725  *
726  * We descend the tree recursively, so we define this cookie
727  * to hold our necessary state information. The mdb_dn2idl_internal
728  * function uses this cookie when calling itself.
729  */
730
731 struct dn2id_cookie {
732         struct mdb_info *mdb;
733         Operation *op;
734         DB_TXN *txn;
735         EntryInfo *ei;
736         ID *ids;
737         ID *tmp;
738         ID *buf;
739         DB *db;
740         DBC *dbc;
741         DBT key;
742         DBT data;
743         ID dbuf;
744         ID id;
745         ID nid;
746         int rc;
747         int depth;
748         char need_sort;
749         char prefix;
750 };
751
752 static int
753 apply_func(
754         void *data,
755         void *arg )
756 {
757         EntryInfo *ei = data;
758         ID *idl = arg;
759
760         mdb_idl_append_one( idl, ei->bei_id );
761         return 0;
762 }
763
764 static int
765 mdb_dn2idl_internal(
766         struct dn2id_cookie *cx
767 )
768 {
769         MDB_IDL_ZERO( cx->tmp );
770
771         if ( cx->mdb->bi_idl_cache_size ) {
772                 char *ptr = ((char *)&cx->id)-1;
773
774                 cx->key.data = ptr;
775                 cx->key.size = sizeof(ID)+1;
776                 if ( cx->prefix == DN_SUBTREE_PREFIX ) {
777                         ID *ids = cx->depth ? cx->tmp : cx->ids;
778                         *ptr = cx->prefix;
779                         cx->rc = mdb_idl_cache_get(cx->mdb, cx->db, &cx->key, ids);
780                         if ( cx->rc == LDAP_SUCCESS ) {
781                                 if ( cx->depth ) {
782                                         mdb_idl_append( cx->ids, cx->tmp );
783                                         cx->need_sort = 1;
784                                 }
785                                 return cx->rc;
786                         }
787                 }
788                 *ptr = DN_ONE_PREFIX;
789                 cx->rc = mdb_idl_cache_get(cx->mdb, cx->db, &cx->key, cx->tmp);
790                 if ( cx->rc == LDAP_SUCCESS ) {
791                         goto gotit;
792                 }
793                 if ( cx->rc == DB_NOTFOUND ) {
794                         return cx->rc;
795                 }
796         }
797
798         mdb_cache_entryinfo_lock( cx->ei );
799
800         /* If number of kids in the cache differs from on-disk, load
801          * up all the kids from the database
802          */
803         if ( cx->ei->bei_ckids+1 != cx->ei->bei_dkids ) {
804                 EntryInfo ei;
805                 db_recno_t dkids = cx->ei->bei_dkids;
806                 ei.bei_parent = cx->ei;
807
808                 /* Only one thread should load the cache */
809                 while ( cx->ei->bei_state & CACHE_ENTRY_ONELEVEL ) {
810                         mdb_cache_entryinfo_unlock( cx->ei );
811                         ldap_pvt_thread_yield();
812                         mdb_cache_entryinfo_lock( cx->ei );
813                         if ( cx->ei->bei_ckids+1 == cx->ei->bei_dkids ) {
814                                 goto synced;
815                         }
816                 }
817
818                 cx->ei->bei_state |= CACHE_ENTRY_ONELEVEL;
819
820                 mdb_cache_entryinfo_unlock( cx->ei );
821
822                 cx->rc = cx->db->cursor( cx->db, NULL, &cx->dbc,
823                         cx->mdb->bi_db_opflags );
824                 if ( cx->rc )
825                         goto done_one;
826
827                 cx->data.data = &cx->dbuf;
828                 cx->data.ulen = sizeof(ID);
829                 cx->data.dlen = sizeof(ID);
830                 cx->data.flags = DB_DBT_USERMEM | DB_DBT_PARTIAL;
831
832                 /* The first item holds the parent ID. Ignore it. */
833                 cx->key.data = &cx->nid;
834                 cx->key.size = sizeof(ID);
835                 cx->rc = cx->dbc->c_get( cx->dbc, &cx->key, &cx->data, DB_SET );
836                 if ( cx->rc ) {
837                         cx->dbc->c_close( cx->dbc );
838                         goto done_one;
839                 }
840
841                 /* If the on-disk count is zero we've never checked it.
842                  * Count it now.
843                  */
844                 if ( !dkids ) {
845                         cx->dbc->c_count( cx->dbc, &dkids, 0 );
846                         cx->ei->bei_dkids = dkids;
847                 }
848
849                 cx->data.data = cx->buf;
850                 cx->data.ulen = MDB_IDL_UM_SIZE * sizeof(ID);
851                 cx->data.flags = DB_DBT_USERMEM;
852
853                 if ( dkids > 1 ) {
854                         /* Fetch the rest of the IDs in a loop... */
855                         while ( (cx->rc = cx->dbc->c_get( cx->dbc, &cx->key, &cx->data,
856                                 DB_MULTIPLE | DB_NEXT_DUP )) == 0 ) {
857                                 u_int8_t *j;
858                                 size_t len;
859                                 void *ptr;
860                                 DB_MULTIPLE_INIT( ptr, &cx->data );
861                                 while (ptr) {
862                                         DB_MULTIPLE_NEXT( ptr, &cx->data, j, len );
863                                         if (j) {
864                                                 EntryInfo *ei2;
865                                                 diskNode *d = (diskNode *)j;
866                                                 short nrlen;
867
868                                                 MDB_DISK2ID( j + len - sizeof(ID), &ei.bei_id );
869                                                 nrlen = ((d->nrdnlen[0] ^ 0x80) << 8) | d->nrdnlen[1];
870                                                 ei.bei_nrdn.bv_len = nrlen;
871                                                 /* nrdn/rdn are set in-place.
872                                                  * mdb_cache_load will copy them as needed
873                                                  */
874                                                 ei.bei_nrdn.bv_val = d->nrdn;
875                                                 ei.bei_rdn.bv_len = len - sizeof(diskNode)
876                                                         - ei.bei_nrdn.bv_len;
877                                                 ei.bei_rdn.bv_val = d->nrdn + ei.bei_nrdn.bv_len + 1;
878                                                 mdb_idl_append_one( cx->tmp, ei.bei_id );
879                                                 mdb_cache_load( cx->mdb, &ei, &ei2 );
880                                         }
881                                 }
882                         }
883                 }
884
885                 cx->rc = cx->dbc->c_close( cx->dbc );
886 done_one:
887                 mdb_cache_entryinfo_lock( cx->ei );
888                 cx->ei->bei_state &= ~CACHE_ENTRY_ONELEVEL;
889                 mdb_cache_entryinfo_unlock( cx->ei );
890                 if ( cx->rc )
891                         return cx->rc;
892
893         } else {
894                 /* The in-memory cache is in sync with the on-disk data.
895                  * do we have any kids?
896                  */
897 synced:
898                 cx->rc = 0;
899                 if ( cx->ei->bei_ckids > 0 ) {
900                         /* Walk the kids tree; order is irrelevant since mdb_idl_sort
901                          * will sort it later.
902                          */
903                         avl_apply( cx->ei->bei_kids, apply_func,
904                                 cx->tmp, -1, AVL_POSTORDER );
905                 }
906                 mdb_cache_entryinfo_unlock( cx->ei );
907         }
908
909         if ( !MDB_IDL_IS_RANGE( cx->tmp ) && cx->tmp[0] > 3 )
910                 mdb_idl_sort( cx->tmp, cx->buf );
911         if ( cx->mdb->bi_idl_cache_max_size && !MDB_IDL_IS_ZERO( cx->tmp )) {
912                 char *ptr = ((char *)&cx->id)-1;
913                 cx->key.data = ptr;
914                 cx->key.size = sizeof(ID)+1;
915                 *ptr = DN_ONE_PREFIX;
916                 mdb_idl_cache_put( cx->mdb, cx->db, &cx->key, cx->tmp, cx->rc );
917         }
918
919 gotit:
920         if ( !MDB_IDL_IS_ZERO( cx->tmp )) {
921                 if ( cx->prefix == DN_SUBTREE_PREFIX ) {
922                         mdb_idl_append( cx->ids, cx->tmp );
923                         cx->need_sort = 1;
924                         if ( !(cx->ei->bei_state & CACHE_ENTRY_NO_GRANDKIDS)) {
925                                 ID *save, idcurs;
926                                 EntryInfo *ei = cx->ei;
927                                 int nokids = 1;
928                                 save = cx->op->o_tmpalloc( MDB_IDL_SIZEOF( cx->tmp ),
929                                         cx->op->o_tmpmemctx );
930                                 MDB_IDL_CPY( save, cx->tmp );
931
932                                 idcurs = 0;
933                                 cx->depth++;
934                                 for ( cx->id = mdb_idl_first( save, &idcurs );
935                                         cx->id != NOID;
936                                         cx->id = mdb_idl_next( save, &idcurs )) {
937                                         EntryInfo *ei2;
938                                         cx->ei = NULL;
939                                         if ( mdb_cache_find_id( cx->op, cx->txn, cx->id, &cx->ei,
940                                                 ID_NOENTRY, NULL ))
941                                                 continue;
942                                         if ( cx->ei ) {
943                                                 ei2 = cx->ei;
944                                                 if ( !( ei2->bei_state & CACHE_ENTRY_NO_KIDS )) {
945                                                         MDB_ID2DISK( cx->id, &cx->nid );
946                                                         mdb_dn2idl_internal( cx );
947                                                         if ( !MDB_IDL_IS_ZERO( cx->tmp ))
948                                                                 nokids = 0;
949                                                 }
950                                                 mdb_cache_entryinfo_lock( ei2 );
951                                                 ei2->bei_finders--;
952                                                 mdb_cache_entryinfo_unlock( ei2 );
953                                         }
954                                 }
955                                 cx->depth--;
956                                 cx->op->o_tmpfree( save, cx->op->o_tmpmemctx );
957                                 if ( nokids ) {
958                                         mdb_cache_entryinfo_lock( ei );
959                                         ei->bei_state |= CACHE_ENTRY_NO_GRANDKIDS;
960                                         mdb_cache_entryinfo_unlock( ei );
961                                 }
962                         }
963                         /* Make sure caller knows it had kids! */
964                         cx->tmp[0]=1;
965
966                         cx->rc = 0;
967                 } else {
968                         MDB_IDL_CPY( cx->ids, cx->tmp );
969                 }
970         }
971         return cx->rc;
972 }
973
974 int
975 mdb_dn2idl(
976         Operation       *op,
977         DB_TXN *txn,
978         struct berval *ndn,
979         EntryInfo       *ei,
980         ID *ids,
981         ID *stack )
982 {
983         struct mdb_info *mdb = (struct mdb_info *)op->o_bd->be_private;
984         struct dn2id_cookie cx;
985
986         Debug( LDAP_DEBUG_TRACE, "=> mdb_dn2idl(\"%s\")\n",
987                 ndn->bv_val, 0, 0 );
988
989 #ifndef MDB_MULTIPLE_SUFFIXES
990         if ( op->ors_scope != LDAP_SCOPE_ONELEVEL && 
991                 ( ei->bei_id == 0 ||
992                 ( ei->bei_parent->bei_id == 0 && op->o_bd->be_suffix[0].bv_len )))
993         {
994                 MDB_IDL_ALL( mdb, ids );
995                 return 0;
996         }
997 #endif
998
999         cx.id = ei->bei_id;
1000         MDB_ID2DISK( cx.id, &cx.nid );
1001         cx.ei = ei;
1002         cx.mdb = mdb;
1003         cx.db = cx.mdb->bi_dn2id->bdi_db;
1004         cx.prefix = (op->ors_scope == LDAP_SCOPE_ONELEVEL) ?
1005                 DN_ONE_PREFIX : DN_SUBTREE_PREFIX;
1006         cx.ids = ids;
1007         cx.tmp = stack;
1008         cx.buf = stack + MDB_IDL_UM_SIZE;
1009         cx.op = op;
1010         cx.txn = txn;
1011         cx.need_sort = 0;
1012         cx.depth = 0;
1013
1014         if ( cx.prefix == DN_SUBTREE_PREFIX ) {
1015                 ids[0] = 1;
1016                 ids[1] = cx.id;
1017         } else {
1018                 MDB_IDL_ZERO( ids );
1019         }
1020         if ( cx.ei->bei_state & CACHE_ENTRY_NO_KIDS )
1021                 return LDAP_SUCCESS;
1022
1023         DBTzero(&cx.key);
1024         cx.key.ulen = sizeof(ID);
1025         cx.key.size = sizeof(ID);
1026         cx.key.flags = DB_DBT_USERMEM;
1027
1028         DBTzero(&cx.data);
1029
1030         mdb_dn2idl_internal(&cx);
1031         if ( cx.need_sort ) {
1032                 char *ptr = ((char *)&cx.id)-1;
1033                 if ( !MDB_IDL_IS_RANGE( cx.ids ) && cx.ids[0] > 3 ) 
1034                         mdb_idl_sort( cx.ids, cx.tmp );
1035                 cx.key.data = ptr;
1036                 cx.key.size = sizeof(ID)+1;
1037                 *ptr = cx.prefix;
1038                 cx.id = ei->bei_id;
1039                 if ( cx.mdb->bi_idl_cache_max_size )
1040                         mdb_idl_cache_put( cx.mdb, cx.db, &cx.key, cx.ids, cx.rc );
1041         }
1042
1043         if ( cx.rc == DB_NOTFOUND )
1044                 cx.rc = LDAP_SUCCESS;
1045
1046         return cx.rc;
1047 }
1048 #endif