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  */
37 int
mdb_attr_slot(struct mdb_info * mdb,AttributeDescription * ad,int * ins)38 mdb_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 
66 static int
ainfo_insert(struct mdb_info * mdb,AttrInfo * a)67 ainfo_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 
86 AttrInfo *
mdb_attr_mask(struct mdb_info * mdb,AttributeDescription * desc)87 mdb_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 */
96 int
mdb_attr_dbs_open(BackendDB * be,MDB_txn * tx0,ConfigReply * cr)97 mdb_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 
181 void
mdb_attr_dbs_close(struct mdb_info * mdb)182 mdb_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 
194 int
mdb_attr_index_config(struct mdb_info * mdb,const char * fname,int lineno,int argc,char ** argv,struct config_reply_s * c_reply)195 mdb_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 			}
319 fail:
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 
473 done:
474 	ldap_charray_free( attrs );
475 	if ( indexes != NULL ) ldap_charray_free( indexes );
476 
477 	return rc;
478 }
479 
480 static int
mdb_attr_index_unparser(void * v1,void * v2)481 mdb_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 
501 static AttributeDescription addef = { NULL, NULL, BER_BVC("default") };
502 static AttrInfo aidef = { &addef };
503 
504 void
mdb_attr_index_unparse(struct mdb_info * mdb,BerVarray * bva)505 mdb_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 
518 int
mdb_attr_multi_config(struct mdb_info * mdb,const char * fname,int lineno,int argc,char ** argv,struct config_reply_s * c_reply)519 mdb_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 ) {
550 badval:
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 			}
582 fail:
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 
616 done:
617 	ldap_charray_free( attrs );
618 
619 	return rc;
620 }
621 
622 static int
mdb_attr_multi_unparser(void * v1,void * v2)623 mdb_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 
645 void
mdb_attr_multi_unparse(struct mdb_info * mdb,BerVarray * bva)646 mdb_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 
660 void
mdb_attr_multi_thresh(struct mdb_info * mdb,AttributeDescription * ad,unsigned * hi,unsigned * lo)661 mdb_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 
679 void
mdb_attr_info_free(AttrInfo * ai)680 mdb_attr_info_free( AttrInfo *ai )
681 {
682 #ifdef LDAP_COMP_MATCH
683 	free( ai->ai_cr );
684 #endif
685 	free( ai );
686 }
687 
688 void
mdb_attr_index_destroy(struct mdb_info * mdb)689 mdb_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 
mdb_attr_index_free(struct mdb_info * mdb,AttributeDescription * ad)699 void 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 
mdb_attr_flush(struct mdb_info * mdb)712 void 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 
mdb_ad_read(struct mdb_info * mdb,MDB_txn * txn)734 int 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 
779 done:
780 	if ( rc == MDB_NOTFOUND )
781 		rc = 0;
782 
783 	mdb_cursor_close( mc );
784 
785 	return rc;
786 }
787 
mdb_ad_get(struct mdb_info * mdb,MDB_txn * txn,AttributeDescription * ad)788 int 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 
mdb_ad_unwind(struct mdb_info * mdb,int prev_ads)820 void 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