]> git.sur5r.net Git - openldap/blob - servers/slapd/back-mdb/dn2id.c
Merge remote branch 'origin/mdb.master' into OPENLDAP_REL_ENG_2_4
[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-2012 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 /* We add two elements to the DN2ID database - a data item under the parent's
75  * entryID containing the child's RDN and entryID, and an item under the
76  * child's entryID containing the parent's entryID.
77  */
78 int
79 mdb_dn2id_add(
80         Operation       *op,
81         MDB_cursor      *mcp,
82         MDB_cursor      *mcd,
83         ID pid,
84         Entry           *e )
85 {
86         struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
87         MDB_val         key, data;
88         ID              nid;
89         int             rc, rlen, nrlen;
90         diskNode *d;
91         char *ptr;
92
93         Debug( LDAP_DEBUG_TRACE, "=> mdb_dn2id_add 0x%lx: \"%s\"\n",
94                 e->e_id, e->e_ndn ? e->e_ndn : "", 0 );
95
96         nrlen = dn_rdnlen( op->o_bd, &e->e_nname );
97         if (nrlen) {
98                 rlen = dn_rdnlen( op->o_bd, &e->e_name );
99         } else {
100                 nrlen = e->e_nname.bv_len;
101                 rlen = e->e_name.bv_len;
102         }
103
104         d = op->o_tmpalloc(sizeof(diskNode) + rlen + nrlen, op->o_tmpmemctx);
105         d->nrdnlen[1] = nrlen & 0xff;
106         d->nrdnlen[0] = (nrlen >> 8) | 0x80;
107         ptr = lutil_strncopy( d->nrdn, e->e_nname.bv_val, nrlen );
108         *ptr++ = '\0';
109         ptr = lutil_strncopy( ptr, e->e_name.bv_val, rlen );
110         *ptr++ = '\0';
111         memcpy( ptr, &e->e_id, sizeof( ID ));
112
113         key.mv_size = sizeof(ID);
114         key.mv_data = &nid;
115
116         nid = pid;
117
118         /* Need to make dummy root node once. Subsequent attempts
119          * will fail harmlessly.
120          */
121         if ( pid == 0 ) {
122                 diskNode dummy = {{0, 0}, "", "", ""};
123                 data.mv_data = &dummy;
124                 data.mv_size = sizeof(diskNode);
125
126                 mdb_cursor_put( mcp, &key, &data, MDB_NODUPDATA );
127         }
128
129         data.mv_data = d;
130         data.mv_size = sizeof(diskNode) + rlen + nrlen;
131
132         rc = mdb_cursor_put( mcp, &key, &data, MDB_NODUPDATA );
133
134         if (rc == 0) {
135                 int flag = MDB_NODUPDATA;
136                 nid = e->e_id;
137                 memcpy( ptr, &pid, sizeof( ID ));
138                 d->nrdnlen[0] ^= 0x80;
139
140                 if ((slapMode & SLAP_TOOL_MODE) || (e->e_id == mdb->mi_nextid))
141                         flag |= MDB_APPEND;
142                 rc = mdb_cursor_put( mcd, &key, &data, flag );
143         }
144
145 fail:
146         op->o_tmpfree( d, op->o_tmpmemctx );
147         Debug( LDAP_DEBUG_TRACE, "<= mdb_dn2id_add 0x%lx: %d\n", e->e_id, rc, 0 );
148
149         return rc;
150 }
151
152 /* mc must have been set by mdb_dn2id */
153 int
154 mdb_dn2id_delete(
155         Operation       *op,
156         MDB_cursor *mc,
157         ID id )
158 {
159         int rc;
160
161         Debug( LDAP_DEBUG_TRACE, "=> mdb_dn2id_delete 0x%lx\n",
162                 id, 0, 0 );
163
164         /* Delete our ID from the parent's list */
165         rc = mdb_cursor_del( mc, 0 );
166
167         /* Delete our ID from the tree. With sorted duplicates, this
168          * will leave any child nodes still hanging around. This is OK
169          * for modrdn, which will add our info back in later.
170          */
171         if ( rc == 0 ) {
172                 MDB_val key, data;
173                 key.mv_size = sizeof(ID);
174                 key.mv_data = &id;
175                 rc = mdb_cursor_get( mc, &key, &data, MDB_SET );
176                 if ( rc == 0 )
177                         rc = mdb_cursor_del( mc, 0 );
178         }
179
180         Debug( LDAP_DEBUG_TRACE, "<= mdb_dn2id_delete 0x%lx: %d\n", id, rc, 0 );
181         return rc;
182 }
183
184 /* return last found ID in *id if no match
185  * If mc is provided, it will be left pointing to the RDN's
186  * record under the parent's ID.
187  */
188 int
189 mdb_dn2id(
190         Operation       *op,
191         MDB_txn *txn,
192         MDB_cursor      *mc,
193         struct berval   *in,
194         ID      *id,
195         struct berval   *matched,
196         struct berval   *nmatched )
197 {
198         struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
199         MDB_cursor *cursor;
200         MDB_dbi dbi = mdb->mi_dn2id;
201         MDB_val         key, data;
202         int             rc = 0, nrlen;
203         diskNode *d;
204         char    *ptr;
205         char dn[SLAP_LDAPDN_MAXLEN];
206         ID pid, nid;
207         struct berval tmp;
208
209         Debug( LDAP_DEBUG_TRACE, "=> mdb_dn2id(\"%s\")\n", in->bv_val ? in->bv_val : "", 0, 0 );
210
211         if ( matched ) {
212                 matched->bv_val = dn + sizeof(dn) - 1;
213                 matched->bv_len = 0;
214                 *matched->bv_val-- = '\0';
215         }
216         if ( nmatched ) {
217                 nmatched->bv_len = 0;
218                 nmatched->bv_val = 0;
219         }
220
221         if ( !in->bv_len ) {
222                 *id = 0;
223                 nid = 0;
224                 goto done;
225         }
226
227         tmp = *in;
228
229         if ( op->o_bd->be_nsuffix[0].bv_len ) {
230                 nrlen = tmp.bv_len - op->o_bd->be_nsuffix[0].bv_len;
231                 tmp.bv_val += nrlen;
232                 tmp.bv_len = op->o_bd->be_nsuffix[0].bv_len;
233         } else {
234                 for ( ptr = tmp.bv_val + tmp.bv_len - 1; ptr >= tmp.bv_val; ptr-- )
235                         if (DN_SEPARATOR(*ptr))
236                                 break;
237                 ptr++;
238                 tmp.bv_len -= ptr - tmp.bv_val;
239                 tmp.bv_val = ptr;
240         }
241         nid = 0;
242         key.mv_size = sizeof(ID);
243
244         if ( mc ) {
245                 cursor = mc;
246         } else {
247                 rc = mdb_cursor_open( txn, dbi, &cursor );
248                 if ( rc ) return rc;
249         }
250
251         for (;;) {
252                 key.mv_data = &pid;
253                 pid = nid;
254
255                 data.mv_size = sizeof(diskNode) + tmp.bv_len;
256                 d = op->o_tmpalloc( data.mv_size, op->o_tmpmemctx );
257                 d->nrdnlen[1] = tmp.bv_len & 0xff;
258                 d->nrdnlen[0] = (tmp.bv_len >> 8) | 0x80;
259                 ptr = lutil_strncopy( d->nrdn, tmp.bv_val, tmp.bv_len );
260                 *ptr = '\0';
261                 data.mv_data = d;
262                 rc = mdb_cursor_get( cursor, &key, &data, MDB_GET_BOTH );
263                 op->o_tmpfree( d, op->o_tmpmemctx );
264                 if ( rc )
265                         break;
266                 ptr = (char *) data.mv_data + data.mv_size - sizeof(ID);
267                 memcpy( &nid, ptr, sizeof(ID));
268
269                 /* grab the non-normalized RDN */
270                 if ( matched ) {
271                         int rlen;
272                         d = data.mv_data;
273                         rlen = data.mv_size - sizeof(diskNode) - tmp.bv_len;
274                         matched->bv_len += rlen;
275                         matched->bv_val -= rlen + 1;
276                         ptr = lutil_strcopy( matched->bv_val, d->rdn + tmp.bv_len );
277                         if ( pid ) {
278                                 *ptr = ',';
279                                 matched->bv_len++;
280                         }
281                 }
282                 if ( nmatched ) {
283                         nmatched->bv_val = tmp.bv_val;
284                 }
285
286                 if ( tmp.bv_val > in->bv_val ) {
287                         for (ptr = tmp.bv_val - 2; ptr > in->bv_val &&
288                                 !DN_SEPARATOR(*ptr); ptr--)     /* empty */;
289                         if ( ptr >= in->bv_val ) {
290                                 if (DN_SEPARATOR(*ptr)) ptr++;
291                                 tmp.bv_len = tmp.bv_val - ptr - 1;
292                                 tmp.bv_val = ptr;
293                         }
294                 } else {
295                         break;
296                 }
297         }
298         *id = nid; 
299         if ( !mc )
300                 mdb_cursor_close( cursor );
301 done:
302         if ( matched ) {
303                 if ( matched->bv_len ) {
304                         ptr = op->o_tmpalloc( matched->bv_len+1, op->o_tmpmemctx );
305                         strcpy( ptr, matched->bv_val );
306                         matched->bv_val = ptr;
307                 } else {
308                         if ( BER_BVISEMPTY( &op->o_bd->be_nsuffix[0] ) && !nid ) {
309                                 ber_dupbv( matched, (struct berval *)&slap_empty_bv );
310                         } else {
311                                 matched->bv_val = NULL;
312                         }
313                 }
314         }
315         if ( nmatched ) {
316                 if ( nmatched->bv_val ) {
317                         nmatched->bv_len = in->bv_len - (nmatched->bv_val - in->bv_val);
318                 } else {
319                         *nmatched = slap_empty_bv;
320                 }
321         }
322
323         if( rc != 0 ) {
324                 Debug( LDAP_DEBUG_TRACE, "<= mdb_dn2id: get failed: %s (%d)\n",
325                         mdb_strerror( rc ), rc, 0 );
326         } else {
327                 Debug( LDAP_DEBUG_TRACE, "<= mdb_dn2id: got id=0x%lx\n",
328                         nid, 0, 0 );
329         }
330
331         return rc;
332 }
333
334 /* return IDs from root to parent of DN */
335 int
336 mdb_dn2sups(
337         Operation       *op,
338         MDB_txn *txn,
339         struct berval   *in,
340         ID      *ids )
341 {
342         struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
343         MDB_cursor *cursor;
344         MDB_dbi dbi = mdb->mi_dn2id;
345         MDB_val         key, data;
346         int             rc = 0, nrlen;
347         diskNode *d;
348         char    *ptr;
349         ID pid, nid;
350         struct berval tmp;
351
352         Debug( LDAP_DEBUG_TRACE, "=> mdb_dn2sups(\"%s\")\n", in->bv_val, 0, 0 );
353
354         if ( !in->bv_len ) {
355                 goto done;
356         }
357
358         tmp = *in;
359
360         nrlen = tmp.bv_len - op->o_bd->be_nsuffix[0].bv_len;
361         tmp.bv_val += nrlen;
362         tmp.bv_len = op->o_bd->be_nsuffix[0].bv_len;
363         nid = 0;
364         key.mv_size = sizeof(ID);
365
366         rc = mdb_cursor_open( txn, dbi, &cursor );
367         if ( rc ) return rc;
368
369         for (;;) {
370                 key.mv_data = &pid;
371                 pid = nid;
372
373                 data.mv_size = sizeof(diskNode) + tmp.bv_len;
374                 d = op->o_tmpalloc( data.mv_size, op->o_tmpmemctx );
375                 d->nrdnlen[1] = tmp.bv_len & 0xff;
376                 d->nrdnlen[0] = (tmp.bv_len >> 8) | 0x80;
377                 ptr = lutil_strncopy( d->nrdn, tmp.bv_val, tmp.bv_len );
378                 *ptr = '\0';
379                 data.mv_data = d;
380                 rc = mdb_cursor_get( cursor, &key, &data, MDB_GET_BOTH );
381                 op->o_tmpfree( d, op->o_tmpmemctx );
382                 if ( rc ) {
383                         mdb_cursor_close( cursor );
384                         break;
385                 }
386                 ptr = (char *) data.mv_data + data.mv_size - sizeof(ID);
387                 memcpy( &nid, ptr, sizeof(ID));
388
389                 if ( pid )
390                         mdb_idl_insert( ids, pid );
391
392                 if ( tmp.bv_val > in->bv_val ) {
393                         for (ptr = tmp.bv_val - 2; ptr > in->bv_val &&
394                                 !DN_SEPARATOR(*ptr); ptr--)     /* empty */;
395                         if ( ptr >= in->bv_val ) {
396                                 if (DN_SEPARATOR(*ptr)) ptr++;
397                                 tmp.bv_len = tmp.bv_val - ptr - 1;
398                                 tmp.bv_val = ptr;
399                         }
400                 } else {
401                         break;
402                 }
403         }
404
405 done:
406         if( rc != 0 ) {
407                 Debug( LDAP_DEBUG_TRACE, "<= mdb_dn2sups: get failed: %s (%d)\n",
408                         mdb_strerror( rc ), rc, 0 );
409         }
410
411         return rc;
412 }
413
414 int
415 mdb_dn2id_children(
416         Operation *op,
417         MDB_txn *txn,
418         Entry *e )
419 {
420         struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
421         MDB_dbi dbi = mdb->mi_dn2id;
422         MDB_val         key, data;
423         MDB_cursor      *cursor;
424         int             rc;
425         ID              id;
426
427         key.mv_size = sizeof(ID);
428         key.mv_data = &id;
429         id = e->e_id;
430
431         rc = mdb_cursor_open( txn, dbi, &cursor );
432         if ( rc ) return rc;
433
434         rc = mdb_cursor_get( cursor, &key, &data, MDB_SET );
435         if ( rc == 0 ) {
436                 size_t dkids;
437                 rc = mdb_cursor_count( cursor, &dkids );
438                 if ( rc == 0 ) {
439                         if ( dkids < 2 ) rc = MDB_NOTFOUND;
440                 }
441         }
442         mdb_cursor_close( cursor );
443         return rc;
444 }
445
446 int
447 mdb_id2name(
448         Operation *op,
449         MDB_txn *txn,
450         MDB_cursor **cursp,
451         ID id,
452         struct berval *name,
453         struct berval *nname )
454 {
455         struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
456         MDB_dbi dbi = mdb->mi_dn2id;
457         MDB_val         key, data;
458         MDB_cursor      *cursor;
459         int             rc, len, nlen;
460         char dn[SLAP_LDAPDN_MAXLEN], ndn[SLAP_LDAPDN_MAXLEN], *ptr;
461         char *dptr, *nptr;
462         diskNode *d;
463
464         key.mv_size = sizeof(ID);
465
466         if ( !*cursp ) {
467                 rc = mdb_cursor_open( txn, dbi, cursp );
468                 if ( rc ) return rc;
469         }
470         cursor = *cursp;
471
472         len = 0;
473         nlen = 0;
474         dptr = dn;
475         nptr = ndn;
476         while (id) {
477                 unsigned int nrlen, rlen;
478                 key.mv_data = &id;
479                 data.mv_size = 0;
480                 data.mv_data = "";
481                 rc = mdb_cursor_get( cursor, &key, &data, MDB_SET );
482                 if ( rc ) break;
483                 ptr = data.mv_data;
484                 ptr += data.mv_size - sizeof(ID);
485                 memcpy( &id, ptr, sizeof(ID) );
486                 d = data.mv_data;
487                 nrlen = (d->nrdnlen[0] << 8) | d->nrdnlen[1];
488                 rlen = data.mv_size - sizeof(diskNode) - nrlen;
489                 assert( nrlen < 1024 && rlen < 1024 );  /* FIXME: Sanity check */
490                 if (nptr > ndn) {
491                         *nptr++ = ',';
492                         *dptr++ = ',';
493                 }
494                 /* copy name and trailing NUL */
495                 memcpy( nptr, d->nrdn, nrlen+1 );
496                 memcpy( dptr, d->nrdn+nrlen+1, rlen+1 );
497                 nptr += nrlen;
498                 dptr += rlen;
499         }
500         if ( rc == 0 ) {
501                 name->bv_len = dptr - dn;
502                 nname->bv_len = nptr - ndn;
503                 name->bv_val = op->o_tmpalloc( name->bv_len + 1, op->o_tmpmemctx );
504                 nname->bv_val = op->o_tmpalloc( nname->bv_len + 1, op->o_tmpmemctx );
505                 memcpy( name->bv_val, dn, name->bv_len );
506                 name->bv_val[name->bv_len] = '\0';
507                 memcpy( nname->bv_val, ndn, nname->bv_len );
508                 nname->bv_val[nname->bv_len] = '\0';
509         }
510         return rc;
511 }
512
513 /* Find each id in ids that is a child of base and move it to res.
514  */
515 int
516 mdb_idscope(
517         Operation *op,
518         MDB_txn *txn,
519         ID base,
520         ID *ids,
521         ID *res )
522 {
523         struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
524         MDB_dbi dbi = mdb->mi_dn2id;
525         MDB_val         key, data;
526         MDB_cursor      *cursor;
527         ID ida, id, cid, ci0, idc = 0;
528         char    *ptr;
529         int             rc;
530
531         key.mv_size = sizeof(ID);
532
533         MDB_IDL_ZERO( res );
534
535         rc = mdb_cursor_open( txn, dbi, &cursor );
536         if ( rc ) return rc;
537
538         ida = mdb_idl_first( ids, &cid );
539
540         /* Don't bother moving out of ids if it's a range */
541         if (!MDB_IDL_IS_RANGE(ids)) {
542                 idc = ids[0];
543                 ci0 = cid;
544         }
545
546         while (ida != NOID) {
547                 id = ida;
548                 while (id) {
549                         key.mv_data = &id;
550                         rc = mdb_cursor_get( cursor, &key, &data, MDB_SET );
551                         if ( rc ) {
552                                 /* not found, move on to next */
553                                 if (idc) {
554                                         if (ci0 != cid)
555                                                 ids[ci0] = ids[cid];
556                                         ci0++;
557                                 }
558                                 break;
559                         }
560                         ptr = data.mv_data;
561                         ptr += data.mv_size - sizeof(ID);
562                         memcpy( &id, ptr, sizeof(ID) );
563                         if ( id == base ) {
564                                 res[0]++;
565                                 res[res[0]] = ida;
566                                 if (idc)
567                                         idc--;
568                                 break;
569                         } else {
570                                 if (idc) {
571                                         if (ci0 != cid)
572                                                 ids[ci0] = ids[cid];
573                                         ci0++;
574                                 }
575                         }
576                         if ( op->ors_scope == LDAP_SCOPE_ONELEVEL )
577                                 break;
578                 }
579                 ida = mdb_idl_next( ids, &cid );
580         }
581         if (!MDB_IDL_IS_RANGE( ids ))
582                 ids[0] = idc;
583
584         mdb_cursor_close( cursor );
585         return rc;
586 }
587
588 /* See if base is a child of any of the scopes
589  */
590 int
591 mdb_idscopes(
592         Operation *op,
593         IdScopes *isc )
594 {
595         struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
596         MDB_dbi dbi = mdb->mi_dn2id;
597         MDB_val         key, data;
598         ID id;
599         ID2 id2;
600         char    *ptr;
601         int             rc = 0;
602         unsigned int x;
603         unsigned int nrlen, rlen;
604         diskNode *d;
605
606         key.mv_size = sizeof(ID);
607
608         if ( !isc->mc ) {
609                 rc = mdb_cursor_open( isc->mt, dbi, &isc->mc );
610                 if ( rc ) return rc;
611         }
612
613         id = isc->id;
614         while (id) {
615                 if ( !rc ) {
616                         key.mv_data = &id;
617                         rc = mdb_cursor_get( isc->mc, &key, &data, MDB_SET );
618                         if ( rc )
619                                 break;
620
621                         /* save RDN info */
622                 }
623                 d = data.mv_data;
624                 nrlen = (d->nrdnlen[0] << 8) | d->nrdnlen[1];
625                 rlen = data.mv_size - sizeof(diskNode) - nrlen;
626                 isc->nrdns[isc->numrdns].bv_len = nrlen;
627                 isc->nrdns[isc->numrdns].bv_val = d->nrdn;
628                 isc->rdns[isc->numrdns].bv_len = rlen;
629                 isc->rdns[isc->numrdns].bv_val = d->nrdn+nrlen+1;
630                 isc->numrdns++;
631
632                 if (!rc && id != isc->id) {
633                         id2.mid = id;
634                         id2.mval = data;
635                         mdb_id2l_insert( isc->scopes, &id2 );
636                 }
637                 ptr = data.mv_data;
638                 ptr += data.mv_size - sizeof(ID);
639                 memcpy( &id, ptr, sizeof(ID) );
640                 x = mdb_id2l_search( isc->scopes, id );
641                 if ( x <= isc->scopes[0].mid && isc->scopes[x].mid == id ) {
642                         if ( !isc->scopes[x].mval.mv_data ) {
643                                 isc->nscope = x;
644                                 return MDB_SUCCESS;
645                         }
646                         data = isc->scopes[x].mval;
647                         rc = 1;
648                 }
649                 if ( op->ors_scope == LDAP_SCOPE_ONELEVEL )
650                         break;
651         }
652         return MDB_NOTFOUND;
653 }