]> git.sur5r.net Git - openldap/blob - servers/slapd/back-mdb/id2entry.c
Fix entry_alloc with no attrs
[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_next = a+1;
126                         a = a->a_next;
127                         a->a_flags = SLAP_ATTR_DONT_FREE_DATA | SLAP_ATTR_DONT_FREE_VALS;
128                         a->a_desc = slap_schema.si_ad_structuralObjectClass;
129                         a->a_vals = bptr;
130                         a->a_nvals = a->a_vals;
131                         a->a_numvals = 1;
132                         *bptr++ = gluebv;
133                         BER_BVZERO(bptr);
134                         a->a_next = NULL;
135                         *e = r;
136                         return MDB_SUCCESS;
137                 }
138         }
139         if ( rc ) return rc;
140
141         rc = mdb_entry_decode( op, &data, e );
142         if ( rc ) return rc;
143
144         (*e)->e_id = id;
145         (*e)->e_name.bv_val = NULL;
146         (*e)->e_nname.bv_val = NULL;
147
148         return rc;
149 }
150
151 int mdb_id2entry_delete(
152         BackendDB *be,
153         MDB_txn *tid,
154         Entry *e )
155 {
156         struct mdb_info *mdb = (struct mdb_info *) be->be_private;
157         MDB_dbi dbi = mdb->mi_id2entry;
158         MDB_val key;
159         int rc;
160
161         key.mv_data = &e->e_id;
162         key.mv_size = sizeof(ID);
163
164         /* delete from database */
165         rc = mdb_del( tid, dbi, &key, NULL );
166
167         return rc;
168 }
169
170 static Entry * mdb_entry_alloc(
171         Operation *op,
172         int nattrs,
173         int nvals )
174 {
175         Entry *e = op->o_tmpalloc( sizeof(Entry) +
176                 nattrs * sizeof(Attribute) +
177                 nvals * sizeof(struct berval), op->o_tmpmemctx );
178         BER_BVZERO(&e->e_bv);
179         e->e_private = e;
180         if (nattrs) {
181                 e->e_attrs = (Attribute *)(e+1);
182                 e->e_attrs->a_vals = (struct berval *)(e->e_attrs+nattrs);
183         } else {
184                 e->e_attrs = NULL;
185         }
186
187         return e;
188 }
189
190 int mdb_entry_return(
191         Operation *op,
192         Entry *e
193 )
194 {
195         if ( e->e_private ) {
196                 if ( slapMode & SLAP_TOOL_MODE ) {
197                         ch_free( e->e_nname.bv_val );
198                         ch_free( e->e_name.bv_val );
199                         ch_free( e );
200                 } else {
201                         op->o_tmpfree( e->e_nname.bv_val, op->o_tmpmemctx );
202                         op->o_tmpfree( e->e_name.bv_val, op->o_tmpmemctx );
203                         op->o_tmpfree( e, op->o_tmpmemctx );
204                 }
205         } else {
206                 entry_free( e );
207         }
208         return 0;
209 }
210
211 int mdb_entry_release(
212         Operation *op,
213         Entry *e,
214         int rw )
215 {
216         struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
217         struct mdb_op_info *moi = NULL;
218         int rc;
219  
220         /* slapMode : SLAP_SERVER_MODE, SLAP_TOOL_MODE,
221                         SLAP_TRUNCATE_MODE, SLAP_UNDEFINED_MODE */
222  
223         mdb_entry_return( op, e );
224         if ( slapMode == SLAP_SERVER_MODE ) {
225                 OpExtra *oex;
226                 LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
227                         if ( oex->oe_key == mdb ) {
228                                 moi = (mdb_op_info *)oex;
229                                 /* If it was setup by entry_get we should probably free it */
230                                 if ( moi->moi_flag & MOI_FREEIT ) {
231                                         moi->moi_ref--;
232                                         if ( moi->moi_ref < 1 ) {
233                                                 mdb_txn_reset( moi->moi_txn );
234                                                 moi->moi_ref = 0;
235                                                 LDAP_SLIST_REMOVE( &op->o_extra, &moi->moi_oe, OpExtra, oe_next );
236                                                 op->o_tmpfree( moi, op->o_tmpmemctx );
237                                         }
238                                 }
239                                 break;
240                         }
241                 }
242         }
243  
244         return 0;
245 }
246
247 /* return LDAP_SUCCESS IFF we can retrieve the specified entry.
248  */
249 int mdb_entry_get(
250         Operation *op,
251         struct berval *ndn,
252         ObjectClass *oc,
253         AttributeDescription *at,
254         int rw,
255         Entry **ent )
256 {
257         struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
258         struct mdb_op_info *moi = NULL;
259         MDB_txn *txn = NULL;
260         Entry *e = NULL;
261         int     rc;
262         const char *at_name = at ? at->ad_cname.bv_val : "(null)";
263
264         Debug( LDAP_DEBUG_ARGS,
265                 "=> mdb_entry_get: ndn: \"%s\"\n", ndn->bv_val, 0, 0 ); 
266         Debug( LDAP_DEBUG_ARGS,
267                 "=> mdb_entry_get: oc: \"%s\", at: \"%s\"\n",
268                 oc ? oc->soc_cname.bv_val : "(null)", at_name, 0);
269
270         rc = mdb_opinfo_get( op, mdb, rw == 0, &moi );
271         if ( rc )
272                 return LDAP_OTHER;
273         txn = moi->moi_txn;
274
275         /* can we find entry */
276         rc = mdb_dn2entry( op, txn, ndn, &e, 0 );
277         switch( rc ) {
278         case MDB_NOTFOUND:
279         case 0:
280                 break;
281         default:
282                 return (rc != LDAP_BUSY) ? LDAP_OTHER : LDAP_BUSY;
283         }
284         if (e == NULL) {
285                 Debug( LDAP_DEBUG_ACL,
286                         "=> mdb_entry_get: cannot find entry: \"%s\"\n",
287                                 ndn->bv_val, 0, 0 ); 
288                 rc = LDAP_NO_SUCH_OBJECT;
289                 goto return_results;
290         }
291         
292         Debug( LDAP_DEBUG_ACL,
293                 "=> mdb_entry_get: found entry: \"%s\"\n",
294                 ndn->bv_val, 0, 0 ); 
295
296         if ( oc && !is_entry_objectclass( e, oc, 0 )) {
297                 Debug( LDAP_DEBUG_ACL,
298                         "<= mdb_entry_get: failed to find objectClass %s\n",
299                         oc->soc_cname.bv_val, 0, 0 ); 
300                 rc = LDAP_NO_SUCH_ATTRIBUTE;
301                 goto return_results;
302         }
303
304         /* NOTE: attr_find() or attrs_find()? */
305         if ( at && attr_find( e->e_attrs, at ) == NULL ) {
306                 Debug( LDAP_DEBUG_ACL,
307                         "<= mdb_entry_get: failed to find attribute %s\n",
308                         at->ad_cname.bv_val, 0, 0 ); 
309                 rc = LDAP_NO_SUCH_ATTRIBUTE;
310                 goto return_results;
311         }
312
313 return_results:
314         if( rc != LDAP_SUCCESS ) {
315                 /* free entry */
316                 if ( e )
317                         mdb_entry_return( op, e );
318
319                 if (moi->moi_ref == 1) {
320                         LDAP_SLIST_REMOVE( &op->o_extra, &moi->moi_oe, OpExtra, oe_next );
321                         mdb_txn_reset( txn );
322                         op->o_tmpfree( moi, op->o_tmpmemctx );
323                 }
324         } else {
325                 *ent = e;
326         }
327
328         Debug( LDAP_DEBUG_TRACE,
329                 "mdb_entry_get: rc=%d\n",
330                 rc, 0, 0 ); 
331         return(rc);
332 }
333
334 static void
335 mdb_reader_free( void *key, void *data )
336 {
337         MDB_txn *txn = data;
338
339         if ( txn ) mdb_txn_abort( txn );
340 }
341
342 /* free up any keys used by the main thread */
343 void
344 mdb_reader_flush( MDB_env *env )
345 {
346         void *data;
347         void *ctx = ldap_pvt_thread_pool_context();
348
349         if ( !ldap_pvt_thread_pool_getkey( ctx, env, &data, NULL ) ) {
350                 ldap_pvt_thread_pool_setkey( ctx, env, NULL, 0, NULL, NULL );
351                 mdb_reader_free( env, data );
352         }
353 }
354
355 int
356 mdb_opinfo_get( Operation *op, struct mdb_info *mdb, int rdonly, mdb_op_info **moip )
357 {
358         int rc, renew = 0;
359         void *data;
360         void *ctx;
361         mdb_op_info *moi = NULL;
362         OpExtra *oex;
363
364         assert( op != NULL );
365
366         if ( !mdb || !moip ) return -1;
367
368         /* If no op was provided, try to find the ctx anyway... */
369         if ( op ) {
370                 ctx = op->o_threadctx;
371         } else {
372                 ctx = ldap_pvt_thread_pool_context();
373         }
374
375         if ( op ) {
376                 LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
377                         if ( oex->oe_key == mdb ) break;
378                 }
379                 moi = (mdb_op_info *)oex;
380         }
381
382         if ( !moi ) {
383                 moi = *moip;
384
385                 if ( !moi ) {
386                         if ( op ) {
387                                 moi = op->o_tmpalloc(sizeof(struct mdb_op_info),op->o_tmpmemctx);
388                         } else {
389                                 moi = ch_malloc(sizeof(mdb_op_info));
390                         }
391                         moi->moi_flag = MOI_FREEIT;
392                         *moip = moi;
393                 }
394                 LDAP_SLIST_INSERT_HEAD( &op->o_extra, &moi->moi_oe, oe_next );
395                 moi->moi_oe.oe_key = mdb;
396                 moi->moi_ref = 0;
397                 moi->moi_txn = NULL;
398         }
399
400         if ( !rdonly ) {
401                 /* This op started as a reader, but now wants to write. */
402                 if ( moi->moi_flag & MOI_READER ) {
403                         moi = *moip;
404                         LDAP_SLIST_INSERT_HEAD( &op->o_extra, &moi->moi_oe, oe_next );
405                 } else {
406                 /* This op is continuing an existing write txn */
407                         *moip = moi;
408                 }
409                 moi->moi_ref++;
410                 if ( !moi->moi_txn ) {
411                         rc = mdb_txn_begin( mdb->mi_dbenv, 0, &moi->moi_txn );
412                         if (rc) {
413                                 Debug( LDAP_DEBUG_ANY, "mdb_opinfo_get: err %s(%d)\n",
414                                         mdb_strerror(rc), rc, 0 );
415                         }
416                         return rc;
417                 }
418                 return 0;
419         }
420
421         /* OK, this is a reader */
422         if ( !moi->moi_txn ) {
423                 if ( !ctx ) {
424                         /* Shouldn't happen unless we're single-threaded */
425                         rc = mdb_txn_begin( mdb->mi_dbenv, MDB_RDONLY, &moi->moi_txn );
426                         if (rc) {
427                                 Debug( LDAP_DEBUG_ANY, "mdb_opinfo_get: err %s(%d)\n",
428                                         mdb_strerror(rc), rc, 0 );
429                         }
430                         return rc;
431                 }
432                 if ( ldap_pvt_thread_pool_getkey( ctx, mdb->mi_dbenv, &data, NULL ) ) {
433                         rc = mdb_txn_begin( mdb->mi_dbenv, MDB_RDONLY, &moi->moi_txn );
434                         if (rc) {
435                                 Debug( LDAP_DEBUG_ANY, "mdb_opinfo_get: err %s(%d)\n",
436                                         mdb_strerror(rc), rc, 0 );
437                                 return rc;
438                         }
439                         data = moi->moi_txn;
440                         if ( ( rc = ldap_pvt_thread_pool_setkey( ctx, mdb->mi_dbenv,
441                                 data, mdb_reader_free, NULL, NULL ) ) ) {
442                                 mdb_txn_abort( moi->moi_txn );
443                                 moi->moi_txn = NULL;
444                                 Debug( LDAP_DEBUG_ANY, "mdb_opinfo_get: thread_pool_setkey failed err (%d)\n",
445                                         rc, 0, 0 );
446                                 return rc;
447                         }
448                 } else {
449                         moi->moi_txn = data;
450                         renew = 1;
451                 }
452                 moi->moi_flag |= MOI_READER;
453         }
454         if ( moi->moi_ref < 1 ) {
455                 moi->moi_ref = 0;
456         }
457         if ( renew ) {
458                 mdb_txn_renew( moi->moi_txn );
459         }
460         moi->moi_ref++;
461         if ( *moip != moi )
462                 *moip = moi;
463
464         return 0;
465 }
466
467 typedef struct Ecount {
468         ber_len_t len;
469         int nattrs;
470         int nvals;
471         int offset;
472 } Ecount;
473
474 /* Count up the sizes of the components of an entry */
475 static int mdb_entry_partsize(struct mdb_info *mdb, MDB_txn *txn, Entry *e,
476         Ecount *eh)
477 {
478         ber_len_t len;
479         int i, nat = 0, nval = 0, nnval = 0;
480         Attribute *a;
481
482         len = 4*sizeof(int);    /* nattrs, nvals, ocflags, offset */
483         for (a=e->e_attrs; a; a=a->a_next) {
484                 /* For AttributeDesc, we only store the attr index */
485                 nat++;
486                 if (!mdb->mi_adxs[a->a_desc->ad_index]) {
487                         int rc = mdb_ad_get(mdb, txn, a->a_desc);
488                         if (rc)
489                                 return rc;
490                 }
491                 len += 2*sizeof(int);   /* AD index, numvals */
492                 nval += a->a_numvals + 1;       /* empty berval at end */
493                 for (i=0; i<a->a_numvals; i++) {
494                         len += a->a_vals[i].bv_len + 1 + sizeof(int);   /* len */
495                 }
496                 if (a->a_nvals != a->a_vals) {
497                         nval += a->a_numvals + 1;
498                         nnval++;
499                         for (i=0; i<a->a_numvals; i++) {
500                                 len += a->a_nvals[i].bv_len + 1 + sizeof(int);;
501                         }
502                 }
503         }
504         /* padding */
505         len = (len + sizeof(ID)-1) & ~(sizeof(ID)-1);
506         eh->len = len;
507         eh->nattrs = nat;
508         eh->nvals = nval;
509         eh->offset = nat + nval - nnval;
510         return 0;
511 }
512
513 #define HIGH_BIT (1<<(sizeof(unsigned int)*CHAR_BIT-1))
514
515 /* Flatten an Entry into a buffer. The buffer starts with the count of the
516  * number of attributes in the entry, the total number of values in the
517  * entry, and the e_ocflags. It then contains a list of integers for each
518  * attribute. For each attribute the first integer gives the index of the
519  * matching AttributeDescription, followed by the number of values in the
520  * attribute. If the high bit is set, the attribute also has normalized
521  * values present. (Note - a_numvals is an unsigned int, so this means
522  * it's possible to receive an attribute that we can't encode due to size
523  * overflow. In practice, this should not be an issue.) Then the length
524  * of each value is listed. If there are normalized values, their lengths
525  * come next. This continues for each attribute. After all of the lengths
526  * for the last attribute, the actual values are copied, with a NUL
527  * terminator after each value. The buffer is padded to the sizeof(ID).
528  * The entire buffer size is precomputed so that a single malloc can be
529  * performed.
530  */
531 static int mdb_entry_encode(Operation *op, MDB_txn *txn, Entry *e, MDB_val *data)
532 {
533         struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
534         ber_len_t len, i;
535         Ecount eh;
536         int rc;
537         Attribute *a;
538         unsigned char *ptr;
539         unsigned int *lp, l;
540
541         Debug( LDAP_DEBUG_TRACE, "=> mdb_entry_encode(0x%08lx): %s\n",
542                 (long) e->e_id, e->e_dn, 0 );
543
544         /* make sure e->e_ocflags is set */
545         if (is_entry_referral(e))
546                 ;       /* empty */
547
548         rc = mdb_entry_partsize( mdb, txn, e, &eh );
549         if (rc) return rc;
550
551         data->mv_size = eh.len;
552         data->mv_data = op->o_tmpalloc(data->mv_size, op->o_tmpmemctx);
553         lp = (unsigned int *)data->mv_data;
554         *lp++ = eh.nattrs;
555         *lp++ = eh.nvals;
556         *lp++ = (unsigned int)e->e_ocflags;
557         *lp++ = eh.offset;
558         ptr = (unsigned char *)(lp + eh.offset);
559
560         for (a=e->e_attrs; a; a=a->a_next) {
561                 *lp++ = mdb->mi_adxs[a->a_desc->ad_index];
562                 l = a->a_numvals;
563                 if (a->a_nvals != a->a_vals)
564                         l |= HIGH_BIT;
565                 *lp++ = l;
566                 if (a->a_vals) {
567                         for (i=0; a->a_vals[i].bv_val; i++);
568                         assert( i == a->a_numvals );
569                         for (i=0; i<a->a_numvals; i++) {
570                                 *lp++ = a->a_vals[i].bv_len;
571                                 memcpy(ptr, a->a_vals[i].bv_val,
572                                         a->a_vals[i].bv_len);
573                                 ptr += a->a_vals[i].bv_len;
574                                 *ptr++ = '\0';
575                         }
576                         if (a->a_nvals != a->a_vals) {
577                                 for (i=0; i<a->a_numvals; i++) {
578                                         *lp++ = a->a_nvals[i].bv_len;
579                                         memcpy(ptr, a->a_nvals[i].bv_val,
580                                                 a->a_nvals[i].bv_len);
581                                         ptr += a->a_nvals[i].bv_len;
582                                         *ptr++ = '\0';
583                                 }
584                         }
585                 }
586         }
587
588         Debug( LDAP_DEBUG_TRACE, "<= mdb_entry_encode(0x%08lx): %s\n",
589                 (long) e->e_id, e->e_dn, 0 );
590
591         return 0;
592 }
593
594 /* Retrieve an Entry that was stored using entry_encode above.
595  *
596  * Note: everything is stored in a single contiguous block, so
597  * you can not free individual attributes or names from this
598  * structure. Attempting to do so will likely corrupt memory.
599  */
600
601 int mdb_entry_decode(Operation *op, MDB_val *data, Entry **e)
602 {
603         struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
604         int i, j, nattrs, nvals;
605         int rc;
606         Attribute *a;
607         Entry *x;
608         const char *text;
609         AttributeDescription *ad;
610         unsigned int *lp = (unsigned int *)data->mv_data;
611         unsigned char *ptr;
612         BerVarray bptr;
613
614         Debug( LDAP_DEBUG_TRACE,
615                 "=> mdb_entry_decode:\n",
616                 0, 0, 0 );
617
618         nattrs = *lp++;
619         nvals = *lp++;
620         x = mdb_entry_alloc(op, nattrs, nvals);
621         x->e_ocflags = *lp++;
622         if (!nvals) {
623                 goto done;
624         }
625         a = x->e_attrs;
626         bptr = a->a_vals;
627         i = *lp++;
628         ptr = (unsigned char *)(lp + i);
629
630         for (;nattrs>0; nattrs--) {
631                 int have_nval = 0;
632                 a->a_desc = mdb->mi_ads[*lp++];
633                 a->a_flags = SLAP_ATTR_DONT_FREE_DATA | SLAP_ATTR_DONT_FREE_VALS;
634                 a->a_numvals = *lp++;
635                 if (a->a_numvals & HIGH_BIT) {
636                         a->a_numvals ^= HIGH_BIT;
637                         have_nval = 1;
638                 }
639                 a->a_vals = bptr;
640                 for (i=0; i<a->a_numvals; i++) {
641                         bptr->bv_len = *lp++;;
642                         bptr->bv_val = (char *)ptr;
643                         ptr += bptr->bv_len+1;
644                         bptr++;
645                 }
646                 bptr->bv_val = NULL;
647                 bptr->bv_len = 0;
648                 bptr++;
649
650                 if (have_nval) {
651                         a->a_nvals = bptr;
652                         for (i=0; i<a->a_numvals; i++) {
653                                 bptr->bv_len = *lp++;
654                                 bptr->bv_val = (char *)ptr;
655                                 ptr += bptr->bv_len+1;
656                                 bptr++;
657                         }
658                         bptr->bv_val = NULL;
659                         bptr->bv_len = 0;
660                         bptr++;
661                 } else {
662                         a->a_nvals = a->a_vals;
663                 }
664                 /* FIXME: This is redundant once a sorted entry is saved into the DB */
665                 if ( a->a_desc->ad_type->sat_flags & SLAP_AT_SORTED_VAL ) {
666                         rc = slap_sort_vals( (Modifications *)a, &text, &j, NULL );
667                         if ( rc == LDAP_SUCCESS ) {
668                                 a->a_flags |= SLAP_ATTR_SORTED_VALS;
669                         } else if ( rc == LDAP_TYPE_OR_VALUE_EXISTS ) {
670                                 /* should never happen */
671                                 Debug( LDAP_DEBUG_ANY,
672                                         "mdb_entry_decode: attributeType %s value #%d provided more than once\n",
673                                         a->a_desc->ad_cname.bv_val, j, 0 );
674                                 return rc;
675                         }
676                 }
677                 a->a_next = a+1;
678                 a = a->a_next;
679         }
680         a[-1].a_next = NULL;
681 done:
682
683         Debug(LDAP_DEBUG_TRACE, "<= mdb_entry_decode\n",
684                 0, 0, 0 );
685         *e = x;
686         return 0;
687 }