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