1/*	$NetBSD: attr.c,v 1.3 2021/08/14 16:15:00 christos Exp $	*/
2
3/* attr.c - backend routines for dealing with attributes */
4/* $OpenLDAP$ */
5/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6 *
7 * Copyright 2000-2021 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: attr.c,v 1.3 2021/08/14 16:15:00 christos Exp $");
21
22#include "portable.h"
23
24#include <stdio.h>
25
26#include <ac/socket.h>
27#include <ac/string.h>
28
29#include "slap.h"
30#include "back-mdb.h"
31#include "slap-config.h"
32#include "lutil.h"
33
34/* Find the ad, return -1 if not found,
35 * set point for insertion if ins is non-NULL
36 */
37int
38mdb_attr_slot( struct mdb_info *mdb, AttributeDescription *ad, int *ins )
39{
40	unsigned base = 0, cursor = 0;
41	unsigned n = mdb->mi_nattrs;
42	int val = 0;
43
44	while ( 0 < n ) {
45		unsigned pivot = n >> 1;
46		cursor = base + pivot;
47
48		val = SLAP_PTRCMP( ad, mdb->mi_attrs[cursor]->ai_desc );
49		if ( val < 0 ) {
50			n = pivot;
51		} else if ( val > 0 ) {
52			base = cursor + 1;
53			n -= pivot + 1;
54		} else {
55			return cursor;
56		}
57	}
58	if ( ins ) {
59		if ( val > 0 )
60			++cursor;
61		*ins = cursor;
62	}
63	return -1;
64}
65
66static int
67ainfo_insert( struct mdb_info *mdb, AttrInfo *a )
68{
69	int x;
70	int i = mdb_attr_slot( mdb, a->ai_desc, &x );
71
72	/* Is it a dup? */
73	if ( i >= 0 )
74		return -1;
75
76	mdb->mi_attrs = ch_realloc( mdb->mi_attrs, ( mdb->mi_nattrs+1 ) *
77		sizeof( AttrInfo * ));
78	if ( x < mdb->mi_nattrs )
79		AC_MEMCPY( &mdb->mi_attrs[x+1], &mdb->mi_attrs[x],
80			( mdb->mi_nattrs - x ) * sizeof( AttrInfo *));
81	mdb->mi_attrs[x] = a;
82	mdb->mi_nattrs++;
83	return 0;
84}
85
86AttrInfo *
87mdb_attr_mask(
88	struct mdb_info	*mdb,
89	AttributeDescription *desc )
90{
91	int i = mdb_attr_slot( mdb, desc, NULL );
92	return i < 0 ? NULL : mdb->mi_attrs[i];
93}
94
95/* Open all un-opened index DB handles */
96int
97mdb_attr_dbs_open(
98	BackendDB *be, MDB_txn *tx0, ConfigReply *cr )
99{
100	struct mdb_info *mdb = (struct mdb_info *) be->be_private;
101	MDB_txn *txn;
102	MDB_dbi *dbis = NULL;
103	int i, flags;
104	int rc;
105
106	txn = tx0;
107	if ( txn == NULL ) {
108		rc = mdb_txn_begin( mdb->mi_dbenv, NULL, 0, &txn );
109		if ( rc ) {
110			snprintf( cr->msg, sizeof(cr->msg), "database \"%s\": "
111				"txn_begin failed: %s (%d).",
112				be->be_suffix[0].bv_val, mdb_strerror(rc), rc );
113			Debug( LDAP_DEBUG_ANY,
114				LDAP_XSTRING(mdb_attr_dbs) ": %s\n",
115				cr->msg );
116			return rc;
117		}
118		dbis = ch_calloc( 1, mdb->mi_nattrs * sizeof(MDB_dbi) );
119	} else {
120		rc = 0;
121	}
122
123	flags = MDB_DUPSORT|MDB_DUPFIXED|MDB_INTEGERDUP;
124	if ( !(slapMode & SLAP_TOOL_READONLY) )
125		flags |= MDB_CREATE;
126
127	for ( i=0; i<mdb->mi_nattrs; i++ ) {
128		if ( mdb->mi_attrs[i]->ai_dbi )	/* already open */
129			continue;
130		if ( !( mdb->mi_attrs[i]->ai_indexmask || mdb->mi_attrs[i]->ai_newmask ))	/* not an index record */
131			continue;
132		rc = mdb_dbi_open( txn, mdb->mi_attrs[i]->ai_desc->ad_type->sat_cname.bv_val,
133			flags, &mdb->mi_attrs[i]->ai_dbi );
134		if ( rc ) {
135			snprintf( cr->msg, sizeof(cr->msg), "database \"%s\": "
136				"mdb_dbi_open(%s) failed: %s (%d).",
137				be->be_suffix[0].bv_val,
138				mdb->mi_attrs[i]->ai_desc->ad_type->sat_cname.bv_val,
139				mdb_strerror(rc), rc );
140			Debug( LDAP_DEBUG_ANY,
141				LDAP_XSTRING(mdb_attr_dbs) ": %s\n",
142				cr->msg );
143			break;
144		}
145		/* Remember newly opened DBI handles */
146		if ( dbis )
147			dbis[i] = mdb->mi_attrs[i]->ai_dbi;
148	}
149
150	/* Only commit if this is our txn */
151	if ( tx0 == NULL ) {
152		if ( !rc ) {
153			rc = mdb_txn_commit( txn );
154			if ( rc ) {
155				snprintf( cr->msg, sizeof(cr->msg), "database \"%s\": "
156					"txn_commit failed: %s (%d).",
157					be->be_suffix[0].bv_val, mdb_strerror(rc), rc );
158				Debug( LDAP_DEBUG_ANY,
159					LDAP_XSTRING(mdb_attr_dbs) ": %s\n",
160					cr->msg );
161			}
162		} else {
163			mdb_txn_abort( txn );
164		}
165		/* Something failed, forget anything we just opened */
166		if ( rc ) {
167			for ( i=0; i<mdb->mi_nattrs; i++ ) {
168				if ( dbis[i] ) {
169					mdb->mi_attrs[i]->ai_dbi = 0;
170					mdb->mi_attrs[i]->ai_indexmask |= MDB_INDEX_DELETING;
171				}
172			}
173			mdb_attr_flush( mdb );
174		}
175		ch_free( dbis );
176	}
177
178	return rc;
179}
180
181void
182mdb_attr_dbs_close(
183	struct mdb_info *mdb
184)
185{
186	int i;
187	for ( i=0; i<mdb->mi_nattrs; i++ )
188		if ( mdb->mi_attrs[i]->ai_dbi ) {
189			mdb_dbi_close( mdb->mi_dbenv, mdb->mi_attrs[i]->ai_dbi );
190			mdb->mi_attrs[i]->ai_dbi = 0;
191		}
192}
193
194int
195mdb_attr_index_config(
196	struct mdb_info	*mdb,
197	const char		*fname,
198	int			lineno,
199	int			argc,
200	char		**argv,
201	struct		config_reply_s *c_reply)
202{
203	int rc = 0;
204	int	i;
205	slap_mask_t mask;
206	char **attrs;
207	char **indexes = NULL;
208
209	attrs = ldap_str2charray( argv[0], "," );
210
211	if( attrs == NULL ) {
212		fprintf( stderr, "%s: line %d: "
213			"no attributes specified: %s\n",
214			fname, lineno, argv[0] );
215		return LDAP_PARAM_ERROR;
216	}
217
218	if ( argc > 1 ) {
219		indexes = ldap_str2charray( argv[1], "," );
220
221		if( indexes == NULL ) {
222			fprintf( stderr, "%s: line %d: "
223				"no indexes specified: %s\n",
224				fname, lineno, argv[1] );
225			rc = LDAP_PARAM_ERROR;
226			goto done;
227		}
228	}
229
230	if( indexes == NULL ) {
231		mask = mdb->mi_defaultmask;
232
233	} else {
234		mask = 0;
235
236		for ( i = 0; indexes[i] != NULL; i++ ) {
237			slap_mask_t index;
238			rc = slap_str2index( indexes[i], &index );
239
240			if( rc != LDAP_SUCCESS ) {
241				if ( c_reply )
242				{
243					snprintf(c_reply->msg, sizeof(c_reply->msg),
244						"index type \"%s\" undefined", indexes[i] );
245
246					fprintf( stderr, "%s: line %d: %s\n",
247						fname, lineno, c_reply->msg );
248				}
249				rc = LDAP_PARAM_ERROR;
250				goto done;
251			}
252
253			mask |= index;
254		}
255	}
256
257	if( !mask ) {
258		if ( c_reply )
259		{
260			snprintf(c_reply->msg, sizeof(c_reply->msg),
261				"no indexes selected" );
262			fprintf( stderr, "%s: line %d: %s\n",
263				fname, lineno, c_reply->msg );
264		}
265		rc = LDAP_PARAM_ERROR;
266		goto done;
267	}
268
269	for ( i = 0; attrs[i] != NULL; i++ ) {
270		AttrInfo	*a;
271		AttributeDescription *ad;
272		const char *text;
273#ifdef LDAP_COMP_MATCH
274		ComponentReference* cr = NULL;
275		AttrInfo *a_cr = NULL;
276#endif
277
278		if( strcasecmp( attrs[i], "default" ) == 0 ) {
279			mdb->mi_defaultmask |= mask;
280			continue;
281		}
282
283#ifdef LDAP_COMP_MATCH
284		if ( is_component_reference( attrs[i] ) ) {
285			rc = extract_component_reference( attrs[i], &cr );
286			if ( rc != LDAP_SUCCESS ) {
287				if ( c_reply )
288				{
289					snprintf(c_reply->msg, sizeof(c_reply->msg),
290						"index component reference\"%s\" undefined",
291						attrs[i] );
292					fprintf( stderr, "%s: line %d: %s\n",
293						fname, lineno, c_reply->msg );
294				}
295				goto done;
296			}
297			cr->cr_indexmask = mask;
298			/*
299			 * After extracting a component reference
300			 * only the name of a attribute will be remaining
301			 */
302		} else {
303			cr = NULL;
304		}
305#endif
306		ad = NULL;
307		rc = slap_str2ad( attrs[i], &ad, &text );
308
309		if( rc != LDAP_SUCCESS ) {
310			if ( c_reply )
311			{
312				snprintf(c_reply->msg, sizeof(c_reply->msg),
313					"index attribute \"%s\" undefined",
314					attrs[i] );
315
316				fprintf( stderr, "%s: line %d: %s\n",
317					fname, lineno, c_reply->msg );
318			}
319fail:
320#ifdef LDAP_COMP_MATCH
321			ch_free( cr );
322#endif
323			goto done;
324		}
325
326		if( ad == slap_schema.si_ad_entryDN || slap_ad_is_binary( ad ) ) {
327			if (c_reply) {
328				snprintf(c_reply->msg, sizeof(c_reply->msg),
329					"index of attribute \"%s\" disallowed", attrs[i] );
330				fprintf( stderr, "%s: line %d: %s\n",
331					fname, lineno, c_reply->msg );
332			}
333			rc = LDAP_UNWILLING_TO_PERFORM;
334			goto fail;
335		}
336
337		if( IS_SLAP_INDEX( mask, SLAP_INDEX_APPROX ) && !(
338			ad->ad_type->sat_approx
339				&& ad->ad_type->sat_approx->smr_indexer
340				&& ad->ad_type->sat_approx->smr_filter ) )
341		{
342			if (c_reply) {
343				snprintf(c_reply->msg, sizeof(c_reply->msg),
344					"approx index of attribute \"%s\" disallowed", attrs[i] );
345				fprintf( stderr, "%s: line %d: %s\n",
346					fname, lineno, c_reply->msg );
347			}
348			rc = LDAP_INAPPROPRIATE_MATCHING;
349			goto fail;
350		}
351
352		if( IS_SLAP_INDEX( mask, SLAP_INDEX_EQUALITY ) && !(
353			ad->ad_type->sat_equality
354				&& ad->ad_type->sat_equality->smr_indexer
355				&& ad->ad_type->sat_equality->smr_filter ) )
356		{
357			if (c_reply) {
358				snprintf(c_reply->msg, sizeof(c_reply->msg),
359					"equality index of attribute \"%s\" disallowed", attrs[i] );
360				fprintf( stderr, "%s: line %d: %s\n",
361					fname, lineno, c_reply->msg );
362			}
363			rc = LDAP_INAPPROPRIATE_MATCHING;
364			goto fail;
365		}
366
367		if( IS_SLAP_INDEX( mask, SLAP_INDEX_SUBSTR ) && !(
368			ad->ad_type->sat_substr
369				&& ad->ad_type->sat_substr->smr_indexer
370				&& ad->ad_type->sat_substr->smr_filter ) )
371		{
372			if (c_reply) {
373				snprintf(c_reply->msg, sizeof(c_reply->msg),
374					"substr index of attribute \"%s\" disallowed", attrs[i] );
375				fprintf( stderr, "%s: line %d: %s\n",
376					fname, lineno, c_reply->msg );
377			}
378			rc = LDAP_INAPPROPRIATE_MATCHING;
379			goto fail;
380		}
381
382		Debug( LDAP_DEBUG_CONFIG, "index %s 0x%04lx\n",
383			ad->ad_cname.bv_val, mask );
384
385		a = (AttrInfo *) ch_malloc( sizeof(AttrInfo) );
386
387#ifdef LDAP_COMP_MATCH
388		a->ai_cr = NULL;
389#endif
390		a->ai_cursor = NULL;
391		a->ai_root = NULL;
392		a->ai_desc = ad;
393		a->ai_dbi = 0;
394		a->ai_multi_hi = UINT_MAX;
395		a->ai_multi_lo = UINT_MAX;
396
397		if ( mdb->mi_flags & MDB_IS_OPEN ) {
398			a->ai_indexmask = 0;
399			a->ai_newmask = mask;
400		} else {
401			a->ai_indexmask = mask;
402			a->ai_newmask = 0;
403		}
404
405#ifdef LDAP_COMP_MATCH
406		if ( cr ) {
407			a_cr = mdb_attr_mask( mdb, ad );
408			if ( a_cr ) {
409				/*
410				 * AttrInfo is already in AVL
411				 * just add the extracted component reference
412				 * in the AttrInfo
413				 */
414				ch_free( a );
415				rc = insert_component_reference( cr, &a_cr->ai_cr );
416				if ( rc != LDAP_SUCCESS) {
417					fprintf( stderr, " error during inserting component reference in %s ", attrs[i]);
418					rc = LDAP_PARAM_ERROR;
419					goto fail;
420				}
421				continue;
422			} else {
423				rc = insert_component_reference( cr, &a->ai_cr );
424				if ( rc != LDAP_SUCCESS) {
425					fprintf( stderr, " error during inserting component reference in %s ", attrs[i]);
426					rc = LDAP_PARAM_ERROR;
427					ch_free( a );
428					goto fail;
429				}
430			}
431		}
432#endif
433		rc = ainfo_insert( mdb, a );
434		if( rc ) {
435			AttrInfo *b = mdb_attr_mask( mdb, ad );
436			/* If this is just a multival record, reuse it for index info */
437			if ( !( b->ai_indexmask || b->ai_newmask ) && b->ai_multi_lo < UINT_MAX ) {
438				b->ai_indexmask = a->ai_indexmask;
439				b->ai_newmask = a->ai_newmask;
440				ch_free( a );
441				rc = 0;
442				continue;
443			}
444			if ( mdb->mi_flags & MDB_IS_OPEN ) {
445				/* If there is already an index defined for this attribute
446				 * it must be replaced. Otherwise we end up with multiple
447				 * olcIndex values for the same attribute */
448				if ( b->ai_indexmask & MDB_INDEX_DELETING ) {
449					/* If we were editing this attr, reset it */
450					b->ai_indexmask &= ~MDB_INDEX_DELETING;
451					/* If this is leftover from a previous add, commit it */
452					if ( b->ai_newmask )
453						b->ai_indexmask = b->ai_newmask;
454					b->ai_newmask = a->ai_newmask;
455					ch_free( a );
456					rc = 0;
457					continue;
458				}
459			}
460			if (c_reply) {
461				snprintf(c_reply->msg, sizeof(c_reply->msg),
462					"duplicate index definition for attr \"%s\"",
463					attrs[i] );
464				fprintf( stderr, "%s: line %d: %s\n",
465					fname, lineno, c_reply->msg );
466			}
467
468			rc = LDAP_PARAM_ERROR;
469			goto done;
470		}
471	}
472
473done:
474	ldap_charray_free( attrs );
475	if ( indexes != NULL ) ldap_charray_free( indexes );
476
477	return rc;
478}
479
480static int
481mdb_attr_index_unparser( void *v1, void *v2 )
482{
483	AttrInfo *ai = v1;
484	BerVarray *bva = v2;
485	struct berval bv;
486	char *ptr;
487
488	slap_index2bvlen( ai->ai_indexmask, &bv );
489	if ( bv.bv_len ) {
490		bv.bv_len += ai->ai_desc->ad_cname.bv_len + 1;
491		ptr = ch_malloc( bv.bv_len+1 );
492		bv.bv_val = lutil_strcopy( ptr, ai->ai_desc->ad_cname.bv_val );
493		*bv.bv_val++ = ' ';
494		slap_index2bv( ai->ai_indexmask, &bv );
495		bv.bv_val = ptr;
496		ber_bvarray_add( bva, &bv );
497	}
498	return 0;
499}
500
501static AttributeDescription addef = { NULL, NULL, BER_BVC("default") };
502static AttrInfo aidef = { &addef };
503
504void
505mdb_attr_index_unparse( struct mdb_info *mdb, BerVarray *bva )
506{
507	int i;
508
509	if ( mdb->mi_defaultmask ) {
510		aidef.ai_indexmask = mdb->mi_defaultmask;
511		mdb_attr_index_unparser( &aidef, bva );
512	}
513	for ( i=0; i<mdb->mi_nattrs; i++ )
514		if ( mdb->mi_attrs[i]->ai_indexmask )
515			mdb_attr_index_unparser( mdb->mi_attrs[i], bva );
516}
517
518int
519mdb_attr_multi_config(
520	struct mdb_info	*mdb,
521	const char		*fname,
522	int			lineno,
523	int			argc,
524	char		**argv,
525	struct		config_reply_s *c_reply)
526{
527	int rc = 0;
528	int	i;
529	unsigned hi,lo;
530	char **attrs, *next, *s;
531
532	attrs = ldap_str2charray( argv[0], "," );
533
534	if( attrs == NULL ) {
535		fprintf( stderr, "%s: line %d: "
536			"no attributes specified: %s\n",
537			fname, lineno, argv[0] );
538		return LDAP_PARAM_ERROR;
539	}
540
541	hi = strtoul( argv[1], &next, 10 );
542	if ( next == argv[1] || next[0] != ',' )
543		goto badval;
544	s = next+1;
545	lo = strtoul( s, &next, 10 );
546	if ( next == s || next[0] != '\0' )
547		goto badval;
548
549	if ( lo > hi ) {
550badval:
551		snprintf(c_reply->msg, sizeof(c_reply->msg),
552			"invalid hi/lo thresholds" );
553		fprintf( stderr, "%s: line %d: %s\n",
554			fname, lineno, c_reply->msg );
555		return LDAP_PARAM_ERROR;
556	}
557
558	for ( i = 0; attrs[i] != NULL; i++ ) {
559		AttrInfo	*a;
560		AttributeDescription *ad;
561		const char *text;
562
563		if( strcasecmp( attrs[i], "default" ) == 0 ) {
564			mdb->mi_multi_hi = hi;
565			mdb->mi_multi_lo = lo;
566			continue;
567		}
568
569		ad = NULL;
570		rc = slap_str2ad( attrs[i], &ad, &text );
571
572		if( rc != LDAP_SUCCESS ) {
573			if ( c_reply )
574			{
575				snprintf(c_reply->msg, sizeof(c_reply->msg),
576					"multival attribute \"%s\" undefined",
577					attrs[i] );
578
579				fprintf( stderr, "%s: line %d: %s\n",
580					fname, lineno, c_reply->msg );
581			}
582fail:
583			goto done;
584		}
585
586		a = (AttrInfo *) ch_calloc( 1, sizeof(AttrInfo) );
587
588		a->ai_desc = ad;
589		a->ai_multi_hi = hi;
590		a->ai_multi_lo = lo;
591
592		rc = ainfo_insert( mdb, a );
593		if( rc ) {
594			AttrInfo *b = mdb_attr_mask( mdb, ad );
595			/* If this is just an index record, reuse it for multival info */
596			if ( b->ai_multi_lo == UINT_MAX ) {
597				b->ai_multi_hi = a->ai_multi_hi;
598				b->ai_multi_lo = a->ai_multi_lo;
599				ch_free( a );
600				rc = 0;
601				continue;
602			}
603			if (c_reply) {
604				snprintf(c_reply->msg, sizeof(c_reply->msg),
605					"duplicate multival definition for attr \"%s\"",
606					attrs[i] );
607				fprintf( stderr, "%s: line %d: %s\n",
608					fname, lineno, c_reply->msg );
609			}
610
611			rc = LDAP_PARAM_ERROR;
612			goto done;
613		}
614	}
615
616done:
617	ldap_charray_free( attrs );
618
619	return rc;
620}
621
622static int
623mdb_attr_multi_unparser( void *v1, void *v2 )
624{
625	AttrInfo *ai = v1;
626	BerVarray *bva = v2;
627	struct berval bv;
628	char digbuf[sizeof("4294967296,4294967296")];
629	char *ptr;
630
631	bv.bv_len = snprintf( digbuf, sizeof(digbuf), "%u,%u",
632		ai->ai_multi_hi, ai->ai_multi_lo );
633	if ( bv.bv_len ) {
634		bv.bv_len += ai->ai_desc->ad_cname.bv_len + 1;
635		ptr = ch_malloc( bv.bv_len+1 );
636		bv.bv_val = lutil_strcopy( ptr, ai->ai_desc->ad_cname.bv_val );
637		*bv.bv_val++ = ' ';
638		strcpy(bv.bv_val, digbuf);
639		bv.bv_val = ptr;
640		ber_bvarray_add( bva, &bv );
641	}
642	return 0;
643}
644
645void
646mdb_attr_multi_unparse( struct mdb_info *mdb, BerVarray *bva )
647{
648	int i;
649
650	if ( mdb->mi_multi_hi < UINT_MAX ) {
651		aidef.ai_multi_hi = mdb->mi_multi_hi;
652		aidef.ai_multi_lo = mdb->mi_multi_lo;
653		mdb_attr_multi_unparser( &aidef, bva );
654	}
655	for ( i=0; i<mdb->mi_nattrs; i++ )
656		if ( mdb->mi_attrs[i]->ai_multi_hi < UINT_MAX )
657			mdb_attr_multi_unparser( mdb->mi_attrs[i], bva );
658}
659
660void
661mdb_attr_multi_thresh( struct mdb_info *mdb, AttributeDescription *ad, unsigned *hi, unsigned *lo )
662{
663	AttrInfo *ai = mdb_attr_mask( mdb, ad );
664	if ( ai && ai->ai_multi_hi < UINT_MAX )
665	{
666		if ( hi )
667			*hi = ai->ai_multi_hi;
668		if ( lo )
669			*lo = ai->ai_multi_lo;
670	} else
671	{
672		if ( hi )
673			*hi = mdb->mi_multi_hi;
674		if ( lo )
675			*lo = mdb->mi_multi_lo;
676	}
677}
678
679void
680mdb_attr_info_free( AttrInfo *ai )
681{
682#ifdef LDAP_COMP_MATCH
683	free( ai->ai_cr );
684#endif
685	free( ai );
686}
687
688void
689mdb_attr_index_destroy( struct mdb_info *mdb )
690{
691	int i;
692
693	for ( i=0; i<mdb->mi_nattrs; i++ )
694		mdb_attr_info_free( mdb->mi_attrs[i] );
695
696	free( mdb->mi_attrs );
697}
698
699void mdb_attr_index_free( struct mdb_info *mdb, AttributeDescription *ad )
700{
701	int i;
702
703	i = mdb_attr_slot( mdb, ad, NULL );
704	if ( i >= 0 ) {
705		mdb_attr_info_free( mdb->mi_attrs[i] );
706		mdb->mi_nattrs--;
707		for (; i<mdb->mi_nattrs; i++)
708			mdb->mi_attrs[i] = mdb->mi_attrs[i+1];
709	}
710}
711
712void mdb_attr_flush( struct mdb_info *mdb )
713{
714	int i;
715
716	for ( i=0; i<mdb->mi_nattrs; i++ ) {
717		if ( mdb->mi_attrs[i]->ai_indexmask & MDB_INDEX_DELETING ) {
718			/* if this is also a multival rec, just clear index */
719			if ( mdb->mi_attrs[i]->ai_multi_lo < UINT_MAX ) {
720				mdb->mi_attrs[i]->ai_indexmask = 0;
721				mdb->mi_attrs[i]->ai_newmask = 0;
722			} else {
723				int j;
724				mdb_attr_info_free( mdb->mi_attrs[i] );
725				mdb->mi_nattrs--;
726				for (j=i; j<mdb->mi_nattrs; j++)
727					mdb->mi_attrs[j] = mdb->mi_attrs[j+1];
728				i--;
729			}
730		}
731	}
732}
733
734int mdb_ad_read( struct mdb_info *mdb, MDB_txn *txn )
735{
736	int i, rc;
737	MDB_cursor *mc;
738	MDB_val key, data;
739	struct berval bdata;
740	const char *text;
741	AttributeDescription *ad;
742
743	rc = mdb_cursor_open( txn, mdb->mi_ad2id, &mc );
744	if ( rc ) {
745		Debug( LDAP_DEBUG_ANY,
746			"mdb_ad_read: cursor_open failed %s(%d)\n",
747			mdb_strerror(rc), rc );
748		return rc;
749	}
750
751	/* our array is 1-based, an index of 0 means no data */
752	i = mdb->mi_numads+1;
753	key.mv_size = sizeof(int);
754	key.mv_data = &i;
755
756	rc = mdb_cursor_get( mc, &key, &data, MDB_SET );
757
758	while ( rc == MDB_SUCCESS ) {
759		bdata.bv_len = data.mv_size;
760		bdata.bv_val = data.mv_data;
761		ad = NULL;
762		rc = slap_bv2ad( &bdata, &ad, &text );
763		if ( rc ) {
764			rc = slap_bv2undef_ad( &bdata, &mdb->mi_ads[i], &text, 0 );
765		} else {
766			if ( ad->ad_index >= MDB_MAXADS ) {
767				Debug( LDAP_DEBUG_ANY,
768					"mdb_adb_read: too many AttributeDescriptions in use\n" );
769				return LDAP_OTHER;
770			}
771			mdb->mi_adxs[ad->ad_index] = i;
772			mdb->mi_ads[i] = ad;
773		}
774		i++;
775		rc = mdb_cursor_get( mc, &key, &data, MDB_NEXT );
776	}
777	mdb->mi_numads = i-1;
778
779done:
780	if ( rc == MDB_NOTFOUND )
781		rc = 0;
782
783	mdb_cursor_close( mc );
784
785	return rc;
786}
787
788int mdb_ad_get( struct mdb_info *mdb, MDB_txn *txn, AttributeDescription *ad )
789{
790	int i, rc;
791	MDB_val key, val;
792
793	rc = mdb_ad_read( mdb, txn );
794	if (rc)
795		return rc;
796
797	if ( mdb->mi_adxs[ad->ad_index] )
798		return 0;
799
800	i = mdb->mi_numads+1;
801	key.mv_size = sizeof(int);
802	key.mv_data = &i;
803	val.mv_size = ad->ad_cname.bv_len;
804	val.mv_data = ad->ad_cname.bv_val;
805
806	rc = mdb_put( txn, mdb->mi_ad2id, &key, &val, 0 );
807	if ( rc == MDB_SUCCESS ) {
808		mdb->mi_adxs[ad->ad_index] = i;
809		mdb->mi_ads[i] = ad;
810		mdb->mi_numads = i;
811	} else {
812		Debug( LDAP_DEBUG_ANY,
813			"mdb_ad_get: mdb_put failed %s(%d)\n",
814			mdb_strerror(rc), rc );
815	}
816
817	return rc;
818}
819
820void mdb_ad_unwind( struct mdb_info *mdb, int prev_ads )
821{
822	int i;
823
824	for (i=mdb->mi_numads; i>prev_ads; i--) {
825		mdb->mi_adxs[mdb->mi_ads[i]->ad_index] = 0;
826		mdb->mi_ads[i] = NULL;
827	}
828	mdb->mi_numads = i;
829}
830