]> git.sur5r.net Git - openldap/blob - servers/slapd/back-mdb/id2entry.c
Keep cursor open for id2entry
[openldap] / servers / slapd / back-mdb / id2entry.c
1 /* id2entry.c - routines to deal with the id2entry database */
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 #include <ac/errno.h>
22
23 #include "back-mdb.h"
24
25 static int mdb_entry_encode(Operation *op, MDB_txn *txn, Entry *e, MDB_val *data);
26 static Entry *mdb_entry_alloc( Operation *op, int nattrs, int nvals );
27
28 static int mdb_id2entry_put(
29         Operation *op,
30         MDB_txn *tid,
31         Entry *e,
32         int flag )
33 {
34         struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
35         MDB_dbi dbi = mdb->mi_id2entry;
36         MDB_val key, data;
37         int rc;
38
39         /* We only store rdns, and they go in the dn2id database. */
40
41         key.mv_data = &e->e_id;
42         key.mv_size = sizeof(ID);
43
44         rc = mdb_entry_encode( op, tid, e, &data );
45         if( rc != LDAP_SUCCESS ) {
46                 return LDAP_OTHER;
47         }
48
49         rc = mdb_put( tid, dbi, &key, &data, flag );
50         op->o_tmpfree( data.mv_data, op->o_tmpmemctx );
51         if (rc) {
52                 Debug( LDAP_DEBUG_ANY,
53                         "mdb_id2entry_put: mdb_put failed: %s(%d) \"%s\"\n",
54                         mdb_strerror(rc), rc,
55                         e->e_nname.bv_val );
56                 rc = LDAP_OTHER;
57         }
58         return rc;
59 }
60
61 /*
62  * This routine adds (or updates) an entry on disk.
63  * The cache should be already be updated.
64  */
65
66
67 int mdb_id2entry_add(
68         Operation *op,
69         MDB_txn *tid,
70         Entry *e )
71 {
72         return mdb_id2entry_put(op, tid, e, MDB_NOOVERWRITE);
73 }
74
75 int mdb_id2entry_update(
76         Operation *op,
77         MDB_txn *tid,
78         Entry *e )
79 {
80         return mdb_id2entry_put(op, tid, e, 0);
81 }
82
83 int mdb_id2entry(
84         Operation *op,
85         MDB_cursor *mc,
86         ID id,
87         Entry **e )
88 {
89         struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
90         MDB_val key, data;
91         int rc = 0;
92
93         *e = NULL;
94
95         key.mv_data = &id;
96         key.mv_size = sizeof(ID);
97
98         /* fetch it */
99         rc = mdb_cursor_get( mc, &key, &data, MDB_SET );
100         if ( rc == MDB_NOTFOUND ) {
101                 /* Looking for root entry on an empty-dn suffix? */
102                 if ( !id && BER_BVISEMPTY( &op->o_bd->be_nsuffix[0] )) {
103                         struct berval gluebv = BER_BVC("glue");
104                         Entry *r = mdb_entry_alloc(op, 2, 4);
105                         Attribute *a = r->e_attrs;
106                         struct berval *bptr;
107
108                         r->e_id = 0;
109                         r->e_ocflags = SLAP_OC_GLUE|SLAP_OC__END;
110                         bptr = a->a_vals;
111                         a->a_flags = SLAP_ATTR_DONT_FREE_DATA | SLAP_ATTR_DONT_FREE_VALS;
112                         a->a_desc = slap_schema.si_ad_objectClass;
113                         a->a_nvals = a->a_vals;
114                         a->a_numvals = 1;
115                         *bptr++ = gluebv;
116                         BER_BVZERO(bptr);
117                         bptr++;
118                         a = a->a_next;
119                         a->a_flags = SLAP_ATTR_DONT_FREE_DATA | SLAP_ATTR_DONT_FREE_VALS;
120                         a->a_desc = slap_schema.si_ad_structuralObjectClass;
121                         a->a_vals = bptr;
122                         a->a_nvals = a->a_vals;
123                         a->a_numvals = 1;
124                         *bptr++ = gluebv;
125                         BER_BVZERO(bptr);
126                         *e = r;
127                         return MDB_SUCCESS;
128                 }
129         }
130         if ( rc ) return rc;
131
132         rc = mdb_entry_decode( op, &data, e );
133         if ( rc ) return rc;
134
135         (*e)->e_id = id;
136         (*e)->e_name.bv_val = NULL;
137         (*e)->e_nname.bv_val = NULL;
138
139         return rc;
140 }
141
142 int mdb_id2entry_delete(
143         BackendDB *be,
144         MDB_txn *tid,
145         Entry *e )
146 {
147         struct mdb_info *mdb = (struct mdb_info *) be->be_private;
148         MDB_dbi dbi = mdb->mi_id2entry;
149         MDB_val key;
150         int rc;
151
152         key.mv_data = &e->e_id;
153         key.mv_size = sizeof(ID);
154
155         /* delete from database */
156         rc = mdb_del( tid, dbi, &key, NULL );
157
158         return rc;
159 }
160
161 static Attribute * mdb_attrs_alloc(
162         Operation *op,
163         int nattrs,
164         int nvals )
165 {
166         Attribute *a, *s;
167
168         if (!nattrs || !nvals) return NULL;
169
170         s = op->o_tmpalloc( nattrs * sizeof(Attribute) +
171                 nvals * sizeof(struct berval), op->o_tmpmemctx );
172
173         for (a=s; nattrs>1; nattrs--) {
174                 a->a_next = a+1;
175                 a++;
176         }
177         a->a_next = NULL;
178         s->a_vals = (struct berval *)(a+1);
179         return s;
180 }
181
182 static Entry * mdb_entry_alloc(
183         Operation *op,
184         int nattrs,
185         int nvals )
186 {
187         Entry *e = op->o_tmpalloc( sizeof(Entry), op->o_tmpmemctx );
188         BER_BVZERO(&e->e_bv);
189         e->e_attrs = mdb_attrs_alloc( op, nattrs, nvals );
190         e->e_private = e;
191         return e;
192 }
193
194 int mdb_entry_return(
195         Operation *op,
196         Entry *e
197 )
198 {
199         if ( e->e_private ) {
200                 op->o_tmpfree( e->e_nname.bv_val, op->o_tmpmemctx );
201                 op->o_tmpfree( e->e_name.bv_val, op->o_tmpmemctx );
202                 op->o_tmpfree( e->e_attrs, op->o_tmpmemctx );
203                 op->o_tmpfree( e, op->o_tmpmemctx );
204         } else {
205                 entry_free( e );
206         }
207         return 0;
208 }
209
210 int mdb_entry_release(
211         Operation *op,
212         Entry *e,
213         int rw )
214 {
215         struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
216         struct mdb_op_info *moi = NULL;
217         int rc;
218  
219         /* slapMode : SLAP_SERVER_MODE, SLAP_TOOL_MODE,
220                         SLAP_TRUNCATE_MODE, SLAP_UNDEFINED_MODE */
221  
222         mdb_entry_return( op, e );
223         if ( slapMode == SLAP_SERVER_MODE ) {
224                 OpExtra *oex;
225                 LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
226                         if ( oex->oe_key == mdb ) {
227                                 moi = (mdb_op_info *)oex;
228                                 /* If it was setup by entry_get we should probably free it */
229                                 if ( moi->moi_flag & MOI_FREEIT ) {
230                                         moi->moi_ref--;
231                                         if ( moi->moi_ref < 1 ) {
232                                                 mdb_txn_reset( moi->moi_txn );
233                                                 moi->moi_ref = 0;
234                                                 LDAP_SLIST_REMOVE( &op->o_extra, &moi->moi_oe, OpExtra, oe_next );
235                                                 op->o_tmpfree( moi, op->o_tmpmemctx );
236                                         }
237                                 }
238                                 break;
239                         }
240                 }
241         }
242  
243         return 0;
244 }
245
246 /* return LDAP_SUCCESS IFF we can retrieve the specified entry.
247  */
248 int mdb_entry_get(
249         Operation *op,
250         struct berval *ndn,
251         ObjectClass *oc,
252         AttributeDescription *at,
253         int rw,
254         Entry **ent )
255 {
256         struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
257         struct mdb_op_info *moi = NULL;
258         MDB_txn *txn = NULL;
259         Entry *e = NULL;
260         int     rc;
261         const char *at_name = at ? at->ad_cname.bv_val : "(null)";
262
263         Debug( LDAP_DEBUG_ARGS,
264                 "=> mdb_entry_get: ndn: \"%s\"\n", ndn->bv_val, 0, 0 ); 
265         Debug( LDAP_DEBUG_ARGS,
266                 "=> mdb_entry_get: oc: \"%s\", at: \"%s\"\n",
267                 oc ? oc->soc_cname.bv_val : "(null)", at_name, 0);
268
269         rc = mdb_opinfo_get( op, mdb, rw == 0, &moi );
270         if ( rc )
271                 return LDAP_OTHER;
272         txn = moi->moi_txn;
273
274         /* can we find entry */
275         rc = mdb_dn2entry( op, txn, ndn, &e, 0 );
276         switch( rc ) {
277         case MDB_NOTFOUND:
278         case 0:
279                 break;
280         default:
281                 return (rc != LDAP_BUSY) ? LDAP_OTHER : LDAP_BUSY;
282         }
283         if (e == NULL) {
284                 Debug( LDAP_DEBUG_ACL,
285                         "=> mdb_entry_get: cannot find entry: \"%s\"\n",
286                                 ndn->bv_val, 0, 0 ); 
287                 rc = LDAP_NO_SUCH_OBJECT;
288                 goto return_results;
289         }
290         
291         Debug( LDAP_DEBUG_ACL,
292                 "=> mdb_entry_get: found entry: \"%s\"\n",
293                 ndn->bv_val, 0, 0 ); 
294
295         if ( oc && !is_entry_objectclass( e, oc, 0 )) {
296                 Debug( LDAP_DEBUG_ACL,
297                         "<= mdb_entry_get: failed to find objectClass %s\n",
298                         oc->soc_cname.bv_val, 0, 0 ); 
299                 rc = LDAP_NO_SUCH_ATTRIBUTE;
300                 goto return_results;
301         }
302
303         /* NOTE: attr_find() or attrs_find()? */
304         if ( at && attr_find( e->e_attrs, at ) == NULL ) {
305                 Debug( LDAP_DEBUG_ACL,
306                         "<= mdb_entry_get: failed to find attribute %s\n",
307                         at->ad_cname.bv_val, 0, 0 ); 
308                 rc = LDAP_NO_SUCH_ATTRIBUTE;
309                 goto return_results;
310         }
311
312 return_results:
313         if( rc != LDAP_SUCCESS ) {
314                 /* free entry */
315                 if ( e )
316                         mdb_entry_return( op, e );
317
318                 if (moi->moi_ref == 1) {
319                         LDAP_SLIST_REMOVE( &op->o_extra, &moi->moi_oe, OpExtra, oe_next );
320                         mdb_txn_reset( txn );
321                         op->o_tmpfree( moi, op->o_tmpmemctx );
322                 }
323         } else {
324                 *ent = e;
325         }
326
327         Debug( LDAP_DEBUG_TRACE,
328                 "mdb_entry_get: rc=%d\n",
329                 rc, 0, 0 ); 
330         return(rc);
331 }
332
333 static void
334 mdb_reader_free( void *key, void *data )
335 {
336         MDB_txn *txn = data;
337
338         if ( txn ) mdb_txn_abort( txn );
339 }
340
341 /* free up any keys used by the main thread */
342 void
343 mdb_reader_flush( MDB_env *env )
344 {
345         void *data;
346         void *ctx = ldap_pvt_thread_pool_context();
347
348         if ( !ldap_pvt_thread_pool_getkey( ctx, env, &data, NULL ) ) {
349                 ldap_pvt_thread_pool_setkey( ctx, env, NULL, 0, NULL, NULL );
350                 mdb_reader_free( env, data );
351         }
352 }
353
354 int
355 mdb_opinfo_get( Operation *op, struct mdb_info *mdb, int rdonly, mdb_op_info **moip )
356 {
357         int rc, renew = 0;
358         void *data;
359         void *ctx;
360         mdb_op_info *moi = NULL;
361         OpExtra *oex;
362
363         assert( op != NULL );
364
365         if ( !mdb || !moip ) return -1;
366
367         /* If no op was provided, try to find the ctx anyway... */
368         if ( op ) {
369                 ctx = op->o_threadctx;
370         } else {
371                 ctx = ldap_pvt_thread_pool_context();
372         }
373
374         if ( op ) {
375                 LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
376                         if ( oex->oe_key == mdb ) break;
377                 }
378                 moi = (mdb_op_info *)oex;
379         }
380
381         if ( !moi ) {
382                 moi = *moip;
383
384                 if ( !moi ) {
385                         if ( op ) {
386                                 moi = op->o_tmpalloc(sizeof(struct mdb_op_info),op->o_tmpmemctx);
387                         } else {
388                                 moi = ch_malloc(sizeof(mdb_op_info));
389                         }
390                         moi->moi_flag = MOI_FREEIT;
391                         *moip = moi;
392                 }
393                 LDAP_SLIST_INSERT_HEAD( &op->o_extra, &moi->moi_oe, oe_next );
394                 moi->moi_oe.oe_key = mdb;
395                 moi->moi_ref = 0;
396                 moi->moi_txn = NULL;
397         }
398
399         if ( !rdonly ) {
400                 /* This op started as a reader, but now wants to write. */
401                 if ( moi->moi_flag & MOI_READER ) {
402                         moi = *moip;
403                         LDAP_SLIST_INSERT_HEAD( &op->o_extra, &moi->moi_oe, oe_next );
404                 } else {
405                 /* This op is continuing an existing write txn */
406                         *moip = moi;
407                 }
408                 moi->moi_ref++;
409                 if ( !moi->moi_txn ) {
410                         rc = mdb_txn_begin( mdb->mi_dbenv, 0, &moi->moi_txn );
411                         if (rc) {
412                                 Debug( LDAP_DEBUG_ANY, "mdb_opinfo_get: err %s(%d)\n",
413                                         mdb_strerror(rc), rc, 0 );
414                         }
415                         return rc;
416                 }
417                 return 0;
418         }
419
420         /* OK, this is a reader */
421         if ( !moi->moi_txn ) {
422                 if ( !ctx ) {
423                         /* Shouldn't happen unless we're single-threaded */
424                         rc = mdb_txn_begin( mdb->mi_dbenv, MDB_RDONLY, &moi->moi_txn );
425                         if (rc) {
426                                 Debug( LDAP_DEBUG_ANY, "mdb_opinfo_get: err %s(%d)\n",
427                                         mdb_strerror(rc), rc, 0 );
428                         }
429                         return rc;
430                 }
431                 if ( ldap_pvt_thread_pool_getkey( ctx, mdb->mi_dbenv, &data, NULL ) ) {
432                         rc = mdb_txn_begin( mdb->mi_dbenv, MDB_RDONLY, &moi->moi_txn );
433                         if (rc) {
434                                 Debug( LDAP_DEBUG_ANY, "mdb_opinfo_get: err %s(%d)\n",
435                                         mdb_strerror(rc), rc, 0 );
436                                 return rc;
437                         }
438                         data = moi->moi_txn;
439                         if ( ( rc = ldap_pvt_thread_pool_setkey( ctx, mdb->mi_dbenv,
440                                 data, mdb_reader_free, NULL, NULL ) ) ) {
441                                 mdb_txn_abort( moi->moi_txn );
442                                 moi->moi_txn = NULL;
443                                 Debug( LDAP_DEBUG_ANY, "mdb_opinfo_get: thread_pool_setkey failed err (%d)\n",
444                                         rc, 0, 0 );
445                                 return rc;
446                         }
447                 } else {
448                         moi->moi_txn = data;
449                         renew = 1;
450                 }
451                 moi->moi_flag |= MOI_READER;
452         }
453         if ( moi->moi_ref < 1 ) {
454                 moi->moi_ref = 0;
455         }
456         if ( renew ) {
457                 mdb_txn_renew( moi->moi_txn );
458         }
459         moi->moi_ref++;
460         if ( *moip != moi )
461                 *moip = moi;
462
463         return 0;
464 }
465
466 /* This is like a ber_len */
467 #define entry_lenlen(l) (((l) < 0x80) ? 1 : ((l) < 0x100) ? 2 : \
468         ((l) < 0x10000) ? 3 : ((l) < 0x1000000) ? 4 : 5)
469
470 static void
471 mdb_entry_putlen(unsigned char **buf, ber_len_t len)
472 {
473         ber_len_t lenlen = entry_lenlen(len);
474
475         if (lenlen == 1) {
476                 **buf = (unsigned char) len;
477         } else {
478                 int i;
479                 **buf = 0x80 | ((unsigned char) lenlen - 1);
480                 for (i=lenlen-1; i>0; i--) {
481                         (*buf)[i] = (unsigned char) len;
482                         len >>= 8;
483                 }
484         }
485         *buf += lenlen;
486 }
487
488 static ber_len_t
489 mdb_entry_getlen(unsigned char **buf)
490 {
491         ber_len_t len;
492         int i;
493
494         len = *(*buf)++;
495         if (len <= 0x7f)
496                 return len;
497         i = len & 0x7f;
498         len = 0;
499         for (;i > 0; i--) {
500                 len <<= 8;
501                 len |= *(*buf)++;
502         }
503         return len;
504 }
505
506 /* Count up the sizes of the components of an entry */
507 static int mdb_entry_partsize(struct mdb_info *mdb, MDB_txn *txn, Entry *e,
508         EntryHeader *eh)
509 {
510         ber_len_t len = 0;
511         int i, nat = 0, nval = 0;
512         Attribute *a;
513
514         for (a=e->e_attrs; a; a=a->a_next) {
515                 /* For AttributeDesc, we only store the attr index */
516                 nat++;
517                 if (!mdb->mi_adxs[a->a_desc->ad_index]) {
518                         int rc = mdb_ad_get(mdb, txn, a->a_desc);
519                         if (rc)
520                                 return rc;
521                 }
522                 len += entry_lenlen(mdb->mi_adxs[a->a_desc->ad_index]);
523                 for (i=0; a->a_vals[i].bv_val; i++) {
524                         nval++;
525                         len += a->a_vals[i].bv_len + 1;
526                         len += entry_lenlen(a->a_vals[i].bv_len);
527                 }
528                 len += entry_lenlen(i);
529                 nval++; /* empty berval at end */
530                 if (a->a_nvals != a->a_vals) {
531                         for (i=0; a->a_nvals[i].bv_val; i++) {
532                                 nval++;
533                                 len += a->a_nvals[i].bv_len + 1;
534                                 len += entry_lenlen(a->a_nvals[i].bv_len);
535                         }
536                         len += entry_lenlen(i); /* i nvals */
537                         nval++;
538                 } else {
539                         len += entry_lenlen(0); /* 0 nvals */
540                 }
541         }
542         len += entry_lenlen(e->e_ocflags);
543         len += entry_lenlen(nat);
544         len += entry_lenlen(nval);
545         eh->bv.bv_len = len;
546         eh->nattrs = nat;
547         eh->nvals = nval;
548         return 0;
549 }
550
551 /* Flatten an Entry into a buffer. The buffer is filled with just the
552  * strings/bervals of all the entry components. Each field is preceded
553  * by its length, encoded the way ber_put_len works. Every field is NUL
554  * terminated.  The entire buffer size is precomputed so that a single
555  * malloc can be performed. The entry size is also recorded,
556  * to aid in entry_decode.
557  */
558 static int mdb_entry_encode(Operation *op, MDB_txn *txn, Entry *e, MDB_val *data)
559 {
560         struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
561         ber_len_t len, dnlen, ndnlen, i;
562         EntryHeader eh;
563         int rc;
564         Attribute *a;
565         unsigned char *ptr;
566
567         Debug( LDAP_DEBUG_TRACE, "=> mdb_entry_encode(0x%08lx): %s\n",
568                 (long) e->e_id, e->e_dn, 0 );
569
570         if (is_entry_referral(e))
571                 ;       /* empty */
572
573         rc = mdb_entry_partsize( mdb, txn, e, &eh );
574         if (rc) return rc;
575
576         data->mv_size = eh.bv.bv_len;
577         data->mv_data = op->o_tmpalloc(data->mv_size, op->o_tmpmemctx);
578         ptr = (unsigned char *)data->mv_data;
579         mdb_entry_putlen(&ptr, eh.nattrs);
580         mdb_entry_putlen(&ptr, eh.nvals);
581         mdb_entry_putlen(&ptr, e->e_ocflags);
582
583         for (a=e->e_attrs; a; a=a->a_next) {
584                 mdb_entry_putlen(&ptr, mdb->mi_adxs[a->a_desc->ad_index]);
585                 if (a->a_vals) {
586                         for (i=0; a->a_vals[i].bv_val; i++);
587                         assert( i == a->a_numvals );
588                         mdb_entry_putlen(&ptr, i);
589                         for (i=0; a->a_vals[i].bv_val; i++) {
590                                 mdb_entry_putlen(&ptr, a->a_vals[i].bv_len);
591                                 AC_MEMCPY(ptr, a->a_vals[i].bv_val,
592                                         a->a_vals[i].bv_len);
593                                 ptr += a->a_vals[i].bv_len;
594                                 *ptr++ = '\0';
595                         }
596                         if (a->a_nvals != a->a_vals) {
597                                 mdb_entry_putlen(&ptr, i);
598                                 for (i=0; a->a_nvals[i].bv_val; i++) {
599                                         mdb_entry_putlen(&ptr, a->a_nvals[i].bv_len);
600                                         AC_MEMCPY(ptr, a->a_nvals[i].bv_val,
601                                         a->a_nvals[i].bv_len);
602                                         ptr += a->a_nvals[i].bv_len;
603                                         *ptr++ = '\0';
604                                 }
605                         } else {
606                                 mdb_entry_putlen(&ptr, 0);
607                         }
608                 }
609         }
610
611         Debug( LDAP_DEBUG_TRACE, "<= mdb_entry_encode(0x%08lx): %s\n",
612                 (long) e->e_id, e->e_dn, 0 );
613
614         return 0;
615 }
616
617 /* Retrieve an Entry that was stored using entry_encode above.
618  *
619  * Note: everything is stored in a single contiguous block, so
620  * you can not free individual attributes or names from this
621  * structure. Attempting to do so will likely corrupt memory.
622  */
623
624 int mdb_entry_decode(Operation *op, MDB_val *data, Entry **e)
625 {
626         struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
627         int i, j, nattrs, nvals;
628         int rc;
629         Attribute *a;
630         Entry *x;
631         const char *text;
632         AttributeDescription *ad;
633         unsigned char *ptr = (unsigned char *)data->mv_data;
634         BerVarray bptr;
635
636         Debug( LDAP_DEBUG_TRACE,
637                 "=> mdb_entry_decode:\n",
638                 0, 0, 0 );
639
640         nattrs = mdb_entry_getlen(&ptr);
641         nvals = mdb_entry_getlen(&ptr);
642         x = mdb_entry_alloc(op, nattrs, nvals);
643         x->e_ocflags = mdb_entry_getlen(&ptr);
644         if (!nvals) {
645                 goto done;
646         }
647         a = x->e_attrs;
648         bptr = a->a_vals;
649
650         while ((i = mdb_entry_getlen(&ptr))) {
651                 a->a_desc = mdb->mi_ads[i];
652                 a->a_flags = SLAP_ATTR_DONT_FREE_DATA | SLAP_ATTR_DONT_FREE_VALS;
653                 j = mdb_entry_getlen(&ptr);
654                 a->a_numvals = j;
655                 a->a_vals = bptr;
656
657                 while (j) {
658                         i = mdb_entry_getlen(&ptr);
659                         bptr->bv_len = i;
660                         bptr->bv_val = (char *)ptr;
661                         ptr += i+1;
662                         bptr++;
663                         j--;
664                 }
665                 bptr->bv_val = NULL;
666                 bptr->bv_len = 0;
667                 bptr++;
668
669                 j = mdb_entry_getlen(&ptr);
670                 if (j) {
671                         a->a_nvals = bptr;
672                         while (j) {
673                                 i = mdb_entry_getlen(&ptr);
674                                 bptr->bv_len = i;
675                                 bptr->bv_val = (char *)ptr;
676                                 ptr += i+1;
677                                 bptr++;
678                                 j--;
679                         }
680                         bptr->bv_val = NULL;
681                         bptr->bv_len = 0;
682                         bptr++;
683                 } else {
684                         a->a_nvals = a->a_vals;
685                 }
686                 /* FIXME: This is redundant once a sorted entry is saved into the DB */
687                 if ( a->a_desc->ad_type->sat_flags & SLAP_AT_SORTED_VAL ) {
688                         rc = slap_sort_vals( (Modifications *)a, &text, &j, NULL );
689                         if ( rc == LDAP_SUCCESS ) {
690                                 a->a_flags |= SLAP_ATTR_SORTED_VALS;
691                         } else if ( rc == LDAP_TYPE_OR_VALUE_EXISTS ) {
692                                 /* should never happen */
693                                 Debug( LDAP_DEBUG_ANY,
694                                         "mdb_entry_decode: attributeType %s value #%d provided more than once\n",
695                                         a->a_desc->ad_cname.bv_val, j, 0 );
696                                 return rc;
697                         }
698                 }
699                 a = a->a_next;
700                 nattrs--;
701                 if ( !nattrs )
702                         break;
703         }
704 done:
705
706         Debug(LDAP_DEBUG_TRACE, "<= mdb_entry_decode\n",
707                 0, 0, 0 );
708         *e = x;
709         return 0;
710 }