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