xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/back-mdb/id2entry.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /*	$NetBSD: id2entry.c,v 1.1.1.3 2018/02/06 01:53:17 christos Exp $	*/
2 
3 /* id2entry.c - routines to deal with the id2entry database */
4 /* $OpenLDAP$ */
5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6  *
7  * Copyright 2000-2017 The OpenLDAP Foundation.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted only as authorized by the OpenLDAP
12  * Public License.
13  *
14  * A copy of this license is available in the file LICENSE in the
15  * top-level directory of the distribution or, alternatively, at
16  * <http://www.OpenLDAP.org/license.html>.
17  */
18 
19 #include <sys/cdefs.h>
20 __RCSID("$NetBSD: id2entry.c,v 1.1.1.3 2018/02/06 01:53:17 christos Exp $");
21 
22 #include "portable.h"
23 
24 #include <stdio.h>
25 #include <ac/string.h>
26 #include <ac/errno.h>
27 
28 #include "back-mdb.h"
29 
30 typedef struct Ecount {
31 	ber_len_t len;
32 	int nattrs;
33 	int nvals;
34 	int offset;
35 } Ecount;
36 
37 static int mdb_entry_partsize(struct mdb_info *mdb, MDB_txn *txn, Entry *e,
38 	Ecount *eh);
39 static int mdb_entry_encode(Operation *op, Entry *e, MDB_val *data,
40 	Ecount *ec);
41 static Entry *mdb_entry_alloc( Operation *op, int nattrs, int nvals );
42 
43 #define ADD_FLAGS	(MDB_NOOVERWRITE|MDB_APPEND)
44 
45 static int mdb_id2entry_put(
46 	Operation *op,
47 	MDB_txn *txn,
48 	MDB_cursor *mc,
49 	Entry *e,
50 	int flag )
51 {
52 	struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
53 	Ecount ec;
54 	MDB_val key, data;
55 	int rc;
56 
57 	/* We only store rdns, and they go in the dn2id database. */
58 
59 	key.mv_data = &e->e_id;
60 	key.mv_size = sizeof(ID);
61 
62 	rc = mdb_entry_partsize( mdb, txn, e, &ec );
63 	if (rc)
64 		return LDAP_OTHER;
65 
66 	flag |= MDB_RESERVE;
67 
68 	if (e->e_id < mdb->mi_nextid)
69 		flag &= ~MDB_APPEND;
70 
71 again:
72 	data.mv_size = ec.len;
73 	if ( mc )
74 		rc = mdb_cursor_put( mc, &key, &data, flag );
75 	else
76 		rc = mdb_put( txn, mdb->mi_id2entry, &key, &data, flag );
77 	if (rc == MDB_SUCCESS) {
78 		rc = mdb_entry_encode( op, e, &data, &ec );
79 		if( rc != LDAP_SUCCESS )
80 			return rc;
81 	}
82 	if (rc) {
83 		/* Was there a hole from slapadd? */
84 		if ( (flag & MDB_NOOVERWRITE) && data.mv_size == 0 ) {
85 			flag &= ~ADD_FLAGS;
86 			goto again;
87 		}
88 		Debug( LDAP_DEBUG_ANY,
89 			"mdb_id2entry_put: mdb_put failed: %s(%d) \"%s\"\n",
90 			mdb_strerror(rc), rc,
91 			e->e_nname.bv_val );
92 		if ( rc != MDB_KEYEXIST )
93 			rc = LDAP_OTHER;
94 	}
95 	return rc;
96 }
97 
98 /*
99  * This routine adds (or updates) an entry on disk.
100  * The cache should be already be updated.
101  */
102 
103 
104 int mdb_id2entry_add(
105 	Operation *op,
106 	MDB_txn *txn,
107 	MDB_cursor *mc,
108 	Entry *e )
109 {
110 	return mdb_id2entry_put(op, txn, mc, e, ADD_FLAGS);
111 }
112 
113 int mdb_id2entry_update(
114 	Operation *op,
115 	MDB_txn *txn,
116 	MDB_cursor *mc,
117 	Entry *e )
118 {
119 	return mdb_id2entry_put(op, txn, mc, e, 0);
120 }
121 
122 int mdb_id2edata(
123 	Operation *op,
124 	MDB_cursor *mc,
125 	ID id,
126 	MDB_val *data )
127 {
128 	MDB_val key;
129 	int rc;
130 
131 	key.mv_data = &id;
132 	key.mv_size = sizeof(ID);
133 
134 	/* fetch it */
135 	rc = mdb_cursor_get( mc, &key, data, MDB_SET );
136 	/* stubs from missing parents - DB is actually invalid */
137 	if ( rc == MDB_SUCCESS && !data->mv_size )
138 		rc = MDB_NOTFOUND;
139 	return rc;
140 }
141 
142 int mdb_id2entry(
143 	Operation *op,
144 	MDB_cursor *mc,
145 	ID id,
146 	Entry **e )
147 {
148 	struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
149 	MDB_val key, data;
150 	int rc = 0;
151 
152 	*e = NULL;
153 
154 	key.mv_data = &id;
155 	key.mv_size = sizeof(ID);
156 
157 	/* fetch it */
158 	rc = mdb_cursor_get( mc, &key, &data, MDB_SET );
159 	if ( rc == MDB_NOTFOUND ) {
160 		/* Looking for root entry on an empty-dn suffix? */
161 		if ( !id && BER_BVISEMPTY( &op->o_bd->be_nsuffix[0] )) {
162 			struct berval gluebv = BER_BVC("glue");
163 			Entry *r = mdb_entry_alloc(op, 2, 4);
164 			Attribute *a = r->e_attrs;
165 			struct berval *bptr;
166 
167 			r->e_id = 0;
168 			r->e_ocflags = SLAP_OC_GLUE|SLAP_OC__END;
169 			bptr = a->a_vals;
170 			a->a_flags = SLAP_ATTR_DONT_FREE_DATA | SLAP_ATTR_DONT_FREE_VALS;
171 			a->a_desc = slap_schema.si_ad_objectClass;
172 			a->a_nvals = a->a_vals;
173 			a->a_numvals = 1;
174 			*bptr++ = gluebv;
175 			BER_BVZERO(bptr);
176 			bptr++;
177 			a->a_next = a+1;
178 			a = a->a_next;
179 			a->a_flags = SLAP_ATTR_DONT_FREE_DATA | SLAP_ATTR_DONT_FREE_VALS;
180 			a->a_desc = slap_schema.si_ad_structuralObjectClass;
181 			a->a_vals = bptr;
182 			a->a_nvals = a->a_vals;
183 			a->a_numvals = 1;
184 			*bptr++ = gluebv;
185 			BER_BVZERO(bptr);
186 			a->a_next = NULL;
187 			*e = r;
188 			return MDB_SUCCESS;
189 		}
190 	}
191 	/* stubs from missing parents - DB is actually invalid */
192 	if ( rc == MDB_SUCCESS && !data.mv_size )
193 		rc = MDB_NOTFOUND;
194 	if ( rc ) return rc;
195 
196 	rc = mdb_entry_decode( op, mdb_cursor_txn( mc ), &data, e );
197 	if ( rc ) return rc;
198 
199 	(*e)->e_id = id;
200 	(*e)->e_name.bv_val = NULL;
201 	(*e)->e_nname.bv_val = NULL;
202 
203 	return rc;
204 }
205 
206 int mdb_id2entry_delete(
207 	BackendDB *be,
208 	MDB_txn *tid,
209 	Entry *e )
210 {
211 	struct mdb_info *mdb = (struct mdb_info *) be->be_private;
212 	MDB_dbi dbi = mdb->mi_id2entry;
213 	MDB_val key;
214 	int rc;
215 
216 	key.mv_data = &e->e_id;
217 	key.mv_size = sizeof(ID);
218 
219 	/* delete from database */
220 	rc = mdb_del( tid, dbi, &key, NULL );
221 
222 	return rc;
223 }
224 
225 static Entry * mdb_entry_alloc(
226 	Operation *op,
227 	int nattrs,
228 	int nvals )
229 {
230 	Entry *e = op->o_tmpalloc( sizeof(Entry) +
231 		nattrs * sizeof(Attribute) +
232 		nvals * sizeof(struct berval), op->o_tmpmemctx );
233 	BER_BVZERO(&e->e_bv);
234 	e->e_private = e;
235 	if (nattrs) {
236 		e->e_attrs = (Attribute *)(e+1);
237 		e->e_attrs->a_vals = (struct berval *)(e->e_attrs+nattrs);
238 	} else {
239 		e->e_attrs = NULL;
240 	}
241 
242 	return e;
243 }
244 
245 int mdb_entry_return(
246 	Operation *op,
247 	Entry *e
248 )
249 {
250 	if ( !e )
251 		return 0;
252 	if ( e->e_private ) {
253 		if ( op->o_hdr ) {
254 			op->o_tmpfree( e->e_nname.bv_val, op->o_tmpmemctx );
255 			op->o_tmpfree( e->e_name.bv_val, op->o_tmpmemctx );
256 			op->o_tmpfree( e, op->o_tmpmemctx );
257 		} else {
258 			ch_free( e->e_nname.bv_val );
259 			ch_free( e->e_name.bv_val );
260 			ch_free( e );
261 		}
262 	} else {
263 		entry_free( e );
264 	}
265 	return 0;
266 }
267 
268 int mdb_entry_release(
269 	Operation *op,
270 	Entry *e,
271 	int rw )
272 {
273 	struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
274 	struct mdb_op_info *moi = NULL;
275 	int rc;
276 
277 	/* slapMode : SLAP_SERVER_MODE, SLAP_TOOL_MODE,
278 			SLAP_TRUNCATE_MODE, SLAP_UNDEFINED_MODE */
279 
280 	int release = 1;
281 	if ( slapMode & SLAP_SERVER_MODE ) {
282 		OpExtra *oex;
283 		LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
284 			release = 0;
285 			if ( oex->oe_key == mdb ) {
286 				mdb_entry_return( op, e );
287 				moi = (mdb_op_info *)oex;
288 				/* If it was setup by entry_get we should probably free it */
289 				if ( moi->moi_flag & MOI_FREEIT ) {
290 					moi->moi_ref--;
291 					if ( moi->moi_ref < 1 ) {
292 						mdb_txn_reset( moi->moi_txn );
293 						moi->moi_ref = 0;
294 						LDAP_SLIST_REMOVE( &op->o_extra, &moi->moi_oe, OpExtra, oe_next );
295 						op->o_tmpfree( moi, op->o_tmpmemctx );
296 					}
297 				}
298 				break;
299 			}
300 		}
301 	}
302 
303 	if (release)
304 		mdb_entry_return( op, e );
305 
306 	return 0;
307 }
308 
309 /* return LDAP_SUCCESS IFF we can retrieve the specified entry.
310  */
311 int mdb_entry_get(
312 	Operation *op,
313 	struct berval *ndn,
314 	ObjectClass *oc,
315 	AttributeDescription *at,
316 	int rw,
317 	Entry **ent )
318 {
319 	struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
320 	struct mdb_op_info *moi = NULL;
321 	MDB_txn *txn = NULL;
322 	Entry *e = NULL;
323 	int	rc;
324 	const char *at_name = at ? at->ad_cname.bv_val : "(null)";
325 
326 	Debug( LDAP_DEBUG_ARGS,
327 		"=> mdb_entry_get: ndn: \"%s\"\n", ndn->bv_val, 0, 0 );
328 	Debug( LDAP_DEBUG_ARGS,
329 		"=> mdb_entry_get: oc: \"%s\", at: \"%s\"\n",
330 		oc ? oc->soc_cname.bv_val : "(null)", at_name, 0);
331 
332 	rc = mdb_opinfo_get( op, mdb, rw == 0, &moi );
333 	if ( rc )
334 		return LDAP_OTHER;
335 	txn = moi->moi_txn;
336 
337 	/* can we find entry */
338 	rc = mdb_dn2entry( op, txn, NULL, ndn, &e, NULL, 0 );
339 	switch( rc ) {
340 	case MDB_NOTFOUND:
341 	case 0:
342 		break;
343 	default:
344 		return (rc != LDAP_BUSY) ? LDAP_OTHER : LDAP_BUSY;
345 	}
346 	if (e == NULL) {
347 		Debug( LDAP_DEBUG_ACL,
348 			"=> mdb_entry_get: cannot find entry: \"%s\"\n",
349 				ndn->bv_val, 0, 0 );
350 		rc = LDAP_NO_SUCH_OBJECT;
351 		goto return_results;
352 	}
353 
354 	Debug( LDAP_DEBUG_ACL,
355 		"=> mdb_entry_get: found entry: \"%s\"\n",
356 		ndn->bv_val, 0, 0 );
357 
358 	if ( oc && !is_entry_objectclass( e, oc, 0 )) {
359 		Debug( LDAP_DEBUG_ACL,
360 			"<= mdb_entry_get: failed to find objectClass %s\n",
361 			oc->soc_cname.bv_val, 0, 0 );
362 		rc = LDAP_NO_SUCH_ATTRIBUTE;
363 		goto return_results;
364 	}
365 
366 	/* NOTE: attr_find() or attrs_find()? */
367 	if ( at && attr_find( e->e_attrs, at ) == NULL ) {
368 		Debug( LDAP_DEBUG_ACL,
369 			"<= mdb_entry_get: failed to find attribute %s\n",
370 			at->ad_cname.bv_val, 0, 0 );
371 		rc = LDAP_NO_SUCH_ATTRIBUTE;
372 		goto return_results;
373 	}
374 
375 return_results:
376 	if( rc != LDAP_SUCCESS ) {
377 		/* free entry */
378 		mdb_entry_release( op, e, rw );
379 	} else {
380 		*ent = e;
381 	}
382 
383 	Debug( LDAP_DEBUG_TRACE,
384 		"mdb_entry_get: rc=%d\n",
385 		rc, 0, 0 );
386 	return(rc);
387 }
388 
389 static void
390 mdb_reader_free( void *key, void *data )
391 {
392 	MDB_txn *txn = data;
393 
394 	if ( txn ) mdb_txn_abort( txn );
395 }
396 
397 /* free up any keys used by the main thread */
398 void
399 mdb_reader_flush( MDB_env *env )
400 {
401 	void *data;
402 	void *ctx = ldap_pvt_thread_pool_context();
403 
404 	if ( !ldap_pvt_thread_pool_getkey( ctx, env, &data, NULL ) ) {
405 		ldap_pvt_thread_pool_setkey( ctx, env, NULL, 0, NULL, NULL );
406 		mdb_reader_free( env, data );
407 	}
408 }
409 
410 extern MDB_txn *mdb_tool_txn;
411 
412 int
413 mdb_opinfo_get( Operation *op, struct mdb_info *mdb, int rdonly, mdb_op_info **moip )
414 {
415 	int rc, renew = 0;
416 	void *data;
417 	void *ctx;
418 	mdb_op_info *moi = NULL;
419 	OpExtra *oex;
420 
421 	assert( op != NULL );
422 
423 	if ( !mdb || !moip ) return -1;
424 
425 	/* If no op was provided, try to find the ctx anyway... */
426 	if ( op ) {
427 		ctx = op->o_threadctx;
428 	} else {
429 		ctx = ldap_pvt_thread_pool_context();
430 	}
431 
432 	if ( op ) {
433 		LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
434 			if ( oex->oe_key == mdb ) break;
435 		}
436 		moi = (mdb_op_info *)oex;
437 	}
438 
439 	if ( !moi ) {
440 		moi = *moip;
441 
442 		if ( !moi ) {
443 			if ( op ) {
444 				moi = op->o_tmpalloc(sizeof(struct mdb_op_info),op->o_tmpmemctx);
445 			} else {
446 				moi = ch_malloc(sizeof(mdb_op_info));
447 			}
448 			moi->moi_flag = MOI_FREEIT;
449 			*moip = moi;
450 		}
451 		LDAP_SLIST_INSERT_HEAD( &op->o_extra, &moi->moi_oe, oe_next );
452 		moi->moi_oe.oe_key = mdb;
453 		moi->moi_ref = 0;
454 		moi->moi_txn = NULL;
455 	}
456 
457 	if ( !rdonly ) {
458 		/* This op started as a reader, but now wants to write. */
459 		if ( moi->moi_flag & MOI_READER ) {
460 			moi = *moip;
461 			LDAP_SLIST_INSERT_HEAD( &op->o_extra, &moi->moi_oe, oe_next );
462 		} else {
463 		/* This op is continuing an existing write txn */
464 			*moip = moi;
465 		}
466 		moi->moi_ref++;
467 		if ( !moi->moi_txn ) {
468 			if (( slapMode & SLAP_TOOL_MODE ) && mdb_tool_txn ) {
469 				moi->moi_txn = mdb_tool_txn;
470 			} else {
471 				rc = mdb_txn_begin( mdb->mi_dbenv, NULL, 0, &moi->moi_txn );
472 				if (rc) {
473 					Debug( LDAP_DEBUG_ANY, "mdb_opinfo_get: err %s(%d)\n",
474 						mdb_strerror(rc), rc, 0 );
475 				}
476 				return rc;
477 			}
478 		}
479 		return 0;
480 	}
481 
482 	/* OK, this is a reader */
483 	if ( !moi->moi_txn ) {
484 		if (( slapMode & SLAP_TOOL_MODE ) && mdb_tool_txn ) {
485 			moi->moi_txn = mdb_tool_txn;
486 			goto ok;
487 		}
488 		if ( !ctx ) {
489 			/* Shouldn't happen unless we're single-threaded */
490 			rc = mdb_txn_begin( mdb->mi_dbenv, NULL, MDB_RDONLY, &moi->moi_txn );
491 			if (rc) {
492 				Debug( LDAP_DEBUG_ANY, "mdb_opinfo_get: err %s(%d)\n",
493 					mdb_strerror(rc), rc, 0 );
494 			}
495 			return rc;
496 		}
497 		if ( ldap_pvt_thread_pool_getkey( ctx, mdb->mi_dbenv, &data, NULL ) ) {
498 			rc = mdb_txn_begin( mdb->mi_dbenv, NULL, MDB_RDONLY, &moi->moi_txn );
499 			if (rc) {
500 				Debug( LDAP_DEBUG_ANY, "mdb_opinfo_get: err %s(%d)\n",
501 					mdb_strerror(rc), rc, 0 );
502 				return rc;
503 			}
504 			data = moi->moi_txn;
505 			if ( ( rc = ldap_pvt_thread_pool_setkey( ctx, mdb->mi_dbenv,
506 				data, mdb_reader_free, NULL, NULL ) ) ) {
507 				mdb_txn_abort( moi->moi_txn );
508 				moi->moi_txn = NULL;
509 				Debug( LDAP_DEBUG_ANY, "mdb_opinfo_get: thread_pool_setkey failed err (%d)\n",
510 					rc, 0, 0 );
511 				return rc;
512 			}
513 		} else {
514 			moi->moi_txn = data;
515 			renew = 1;
516 		}
517 		moi->moi_flag |= MOI_READER;
518 	}
519 ok:
520 	if ( moi->moi_ref < 1 ) {
521 		moi->moi_ref = 0;
522 	}
523 	if ( renew ) {
524 		rc = mdb_txn_renew( moi->moi_txn );
525 		assert(!rc);
526 	}
527 	moi->moi_ref++;
528 	if ( *moip != moi )
529 		*moip = moi;
530 
531 	return 0;
532 }
533 
534 /* Count up the sizes of the components of an entry */
535 static int mdb_entry_partsize(struct mdb_info *mdb, MDB_txn *txn, Entry *e,
536 	Ecount *eh)
537 {
538 	ber_len_t len;
539 	int i, nat = 0, nval = 0, nnval = 0;
540 	Attribute *a;
541 
542 	len = 4*sizeof(int);	/* nattrs, nvals, ocflags, offset */
543 	for (a=e->e_attrs; a; a=a->a_next) {
544 		/* For AttributeDesc, we only store the attr index */
545 		nat++;
546 		if (a->a_desc->ad_index >= MDB_MAXADS) {
547 			Debug( LDAP_DEBUG_ANY, "mdb_entry_partsize: too many AttributeDescriptions used\n",
548 				0, 0, 0 );
549 			return LDAP_OTHER;
550 		}
551 		if (!mdb->mi_adxs[a->a_desc->ad_index]) {
552 			int rc = mdb_ad_get(mdb, txn, a->a_desc);
553 			if (rc)
554 				return rc;
555 		}
556 		len += 2*sizeof(int);	/* AD index, numvals */
557 		nval += a->a_numvals + 1;	/* empty berval at end */
558 		for (i=0; i<a->a_numvals; i++) {
559 			len += a->a_vals[i].bv_len + 1 + sizeof(int);	/* len */
560 		}
561 		if (a->a_nvals != a->a_vals) {
562 			nval += a->a_numvals + 1;
563 			nnval++;
564 			for (i=0; i<a->a_numvals; i++) {
565 				len += a->a_nvals[i].bv_len + 1 + sizeof(int);;
566 			}
567 		}
568 	}
569 	/* padding */
570 	len = (len + sizeof(ID)-1) & ~(sizeof(ID)-1);
571 	eh->len = len;
572 	eh->nattrs = nat;
573 	eh->nvals = nval;
574 	eh->offset = nat + nval - nnval;
575 	return 0;
576 }
577 
578 #define HIGH_BIT (1<<(sizeof(unsigned int)*CHAR_BIT-1))
579 
580 /* Flatten an Entry into a buffer. The buffer starts with the count of the
581  * number of attributes in the entry, the total number of values in the
582  * entry, and the e_ocflags. It then contains a list of integers for each
583  * attribute. For each attribute the first integer gives the index of the
584  * matching AttributeDescription, followed by the number of values in the
585  * attribute. If the high bit of the attr index is set, the attribute's
586  * values are already sorted.
587  * If the high bit of numvals is set, the attribute also has normalized
588  * values present. (Note - a_numvals is an unsigned int, so this means
589  * it's possible to receive an attribute that we can't encode due to size
590  * overflow. In practice, this should not be an issue.) Then the length
591  * of each value is listed. If there are normalized values, their lengths
592  * come next. This continues for each attribute. After all of the lengths
593  * for the last attribute, the actual values are copied, with a NUL
594  * terminator after each value. The buffer is padded to the sizeof(ID).
595  * The entire buffer size is precomputed so that a single malloc can be
596  * performed.
597  */
598 static int mdb_entry_encode(Operation *op, Entry *e, MDB_val *data, Ecount *eh)
599 {
600 	struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
601 	ber_len_t len, i;
602 	int rc;
603 	Attribute *a;
604 	unsigned char *ptr;
605 	unsigned int *lp, l;
606 
607 	Debug( LDAP_DEBUG_TRACE, "=> mdb_entry_encode(0x%08lx): %s\n",
608 		(long) e->e_id, e->e_dn, 0 );
609 
610 	/* make sure e->e_ocflags is set */
611 	if (is_entry_referral(e))
612 		;	/* empty */
613 
614 	lp = (unsigned int *)data->mv_data;
615 	*lp++ = eh->nattrs;
616 	*lp++ = eh->nvals;
617 	*lp++ = (unsigned int)e->e_ocflags;
618 	*lp++ = eh->offset;
619 	ptr = (unsigned char *)(lp + eh->offset);
620 
621 	for (a=e->e_attrs; a; a=a->a_next) {
622 		if (!a->a_desc->ad_index)
623 			return LDAP_UNDEFINED_TYPE;
624 		l = mdb->mi_adxs[a->a_desc->ad_index];
625 		if (a->a_flags & SLAP_ATTR_SORTED_VALS)
626 			l |= HIGH_BIT;
627 		*lp++ = l;
628 		l = a->a_numvals;
629 		if (a->a_nvals != a->a_vals)
630 			l |= HIGH_BIT;
631 		*lp++ = l;
632 		if (a->a_vals) {
633 			for (i=0; a->a_vals[i].bv_val; i++);
634 			assert( i == a->a_numvals );
635 			for (i=0; i<a->a_numvals; i++) {
636 				*lp++ = a->a_vals[i].bv_len;
637 				memcpy(ptr, a->a_vals[i].bv_val,
638 					a->a_vals[i].bv_len);
639 				ptr += a->a_vals[i].bv_len;
640 				*ptr++ = '\0';
641 			}
642 			if (a->a_nvals != a->a_vals) {
643 				for (i=0; i<a->a_numvals; i++) {
644 					*lp++ = a->a_nvals[i].bv_len;
645 					memcpy(ptr, a->a_nvals[i].bv_val,
646 						a->a_nvals[i].bv_len);
647 					ptr += a->a_nvals[i].bv_len;
648 					*ptr++ = '\0';
649 				}
650 			}
651 		}
652 	}
653 
654 	Debug( LDAP_DEBUG_TRACE, "<= mdb_entry_encode(0x%08lx): %s\n",
655 		(long) e->e_id, e->e_dn, 0 );
656 
657 	return 0;
658 }
659 
660 /* Retrieve an Entry that was stored using entry_encode above.
661  *
662  * Note: everything is stored in a single contiguous block, so
663  * you can not free individual attributes or names from this
664  * structure. Attempting to do so will likely corrupt memory.
665  */
666 
667 int mdb_entry_decode(Operation *op, MDB_txn *txn, MDB_val *data, Entry **e)
668 {
669 	struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
670 	int i, j, nattrs, nvals;
671 	int rc;
672 	Attribute *a;
673 	Entry *x;
674 	const char *text;
675 	AttributeDescription *ad;
676 	unsigned int *lp = (unsigned int *)data->mv_data;
677 	unsigned char *ptr;
678 	BerVarray bptr;
679 
680 	Debug( LDAP_DEBUG_TRACE,
681 		"=> mdb_entry_decode:\n",
682 		0, 0, 0 );
683 
684 	nattrs = *lp++;
685 	nvals = *lp++;
686 	x = mdb_entry_alloc(op, nattrs, nvals);
687 	x->e_ocflags = *lp++;
688 	if (!nvals) {
689 		goto done;
690 	}
691 	a = x->e_attrs;
692 	bptr = a->a_vals;
693 	i = *lp++;
694 	ptr = (unsigned char *)(lp + i);
695 
696 	for (;nattrs>0; nattrs--) {
697 		int have_nval = 0;
698 		a->a_flags = SLAP_ATTR_DONT_FREE_DATA | SLAP_ATTR_DONT_FREE_VALS;
699 		i = *lp++;
700 		if (i & HIGH_BIT) {
701 			i ^= HIGH_BIT;
702 			a->a_flags |= SLAP_ATTR_SORTED_VALS;
703 		}
704 		if (i > mdb->mi_numads) {
705 			rc = mdb_ad_read(mdb, txn);
706 			if (rc)
707 				return rc;
708 			if (i > mdb->mi_numads) {
709 				Debug( LDAP_DEBUG_ANY,
710 					"mdb_entry_decode: attribute index %d not recognized\n",
711 					i, 0, 0 );
712 				return LDAP_OTHER;
713 			}
714 		}
715 		a->a_desc = mdb->mi_ads[i];
716 		a->a_numvals = *lp++;
717 		if (a->a_numvals & HIGH_BIT) {
718 			a->a_numvals ^= HIGH_BIT;
719 			have_nval = 1;
720 		}
721 		a->a_vals = bptr;
722 		for (i=0; i<a->a_numvals; i++) {
723 			bptr->bv_len = *lp++;;
724 			bptr->bv_val = (char *)ptr;
725 			ptr += bptr->bv_len+1;
726 			bptr++;
727 		}
728 		bptr->bv_val = NULL;
729 		bptr->bv_len = 0;
730 		bptr++;
731 
732 		if (have_nval) {
733 			a->a_nvals = bptr;
734 			for (i=0; i<a->a_numvals; i++) {
735 				bptr->bv_len = *lp++;
736 				bptr->bv_val = (char *)ptr;
737 				ptr += bptr->bv_len+1;
738 				bptr++;
739 			}
740 			bptr->bv_val = NULL;
741 			bptr->bv_len = 0;
742 			bptr++;
743 		} else {
744 			a->a_nvals = a->a_vals;
745 		}
746 		/* FIXME: This is redundant once a sorted entry is saved into the DB */
747 		if (( a->a_desc->ad_type->sat_flags & SLAP_AT_SORTED_VAL )
748 			&& !(a->a_flags & SLAP_ATTR_SORTED_VALS)) {
749 			rc = slap_sort_vals( (Modifications *)a, &text, &j, NULL );
750 			if ( rc == LDAP_SUCCESS ) {
751 				a->a_flags |= SLAP_ATTR_SORTED_VALS;
752 			} else if ( rc == LDAP_TYPE_OR_VALUE_EXISTS ) {
753 				/* should never happen */
754 				Debug( LDAP_DEBUG_ANY,
755 					"mdb_entry_decode: attributeType %s value #%d provided more than once\n",
756 					a->a_desc->ad_cname.bv_val, j, 0 );
757 				return rc;
758 			}
759 		}
760 		a->a_next = a+1;
761 		a = a->a_next;
762 	}
763 	a[-1].a_next = NULL;
764 done:
765 
766 	Debug(LDAP_DEBUG_TRACE, "<= mdb_entry_decode\n",
767 		0, 0, 0 );
768 	*e = x;
769 	return 0;
770 }
771