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