1 /* OpenLDAP WiredTiger backend */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2002-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 /* ACKNOWLEDGEMENTS:
17  * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
18  * based on back-bdb for inclusion in OpenLDAP Software.
19  * WiredTiger is a product of MongoDB Inc.
20  */
21 
22 #include "back-wt.h"
23 #include "slap-config.h"
24 #include "lutil.h"
25 
26 /* Find the ad, return -1 if not found,
27  * set point for insertion if ins is non-NULL
28  */
29 int
wt_attr_slot(struct wt_info * wi,AttributeDescription * ad,int * ins)30 wt_attr_slot( struct wt_info *wi, AttributeDescription *ad, int *ins )
31 {
32 	unsigned base = 0, cursor = 0;
33 	unsigned n = wi->wi_nattrs;
34 	int val = 0;
35 
36 	while ( 0 < n ) {
37 		unsigned pivot = n >> 1;
38 		cursor = base + pivot;
39 
40 		val = SLAP_PTRCMP( ad, wi->wi_attrs[cursor]->ai_desc );
41 		if ( val < 0 ) {
42 			n = pivot;
43 		} else if ( val > 0 ) {
44 			base = cursor + 1;
45 			n -= pivot + 1;
46 		} else {
47 			return cursor;
48 		}
49 	}
50 	if ( ins ) {
51 		if ( val > 0 )
52 			++cursor;
53 		*ins = cursor;
54 	}
55 	return -1;
56 }
57 
58 static int
ainfo_insert(struct wt_info * wi,AttrInfo * a)59 ainfo_insert( struct wt_info *wi, AttrInfo *a )
60 {
61 	int x = INT_MAX;
62 	int i = wt_attr_slot( wi, a->ai_desc, &x );
63 
64 	/* Is it a dup? */
65 	if ( i >= 0 )
66 		return -1;
67 
68 	wi->wi_attrs = ch_realloc( wi->wi_attrs, ( wi->wi_nattrs+1 ) *
69 							   sizeof( AttrInfo * ));
70 	if ( x < wi->wi_nattrs )
71 		AC_MEMCPY( &wi->wi_attrs[x+1], &wi->wi_attrs[x],
72 				   ( wi->wi_nattrs - x ) * sizeof( AttrInfo *));
73 	wi->wi_attrs[x] = a;
74 	wi->wi_nattrs++;
75 	return 0;
76 }
77 
78 AttrInfo *
wt_attr_mask(struct wt_info * wi,AttributeDescription * desc)79 wt_attr_mask(
80 	struct wt_info	*wi,
81 	AttributeDescription *desc )
82 {
83 	int i = wt_attr_slot( wi, desc, NULL );
84 	return i < 0 ? NULL : wi->wi_attrs[i];
85 }
86 
87 int
wt_attr_index_config(struct wt_info * wi,const char * fname,int lineno,int argc,char ** argv,struct config_reply_s * c_reply)88 wt_attr_index_config(
89 	struct wt_info	*wi,
90 	const char		*fname,
91 	int			lineno,
92 	int			argc,
93 	char		**argv,
94 	struct		config_reply_s *c_reply)
95 {
96 	int rc = 0;
97 	int	i;
98 	slap_mask_t mask;
99 	char **attrs;
100 	char **indexes = NULL;
101 
102 	attrs = ldap_str2charray( argv[0], "," );
103 
104 	if( attrs == NULL ) {
105 		fprintf( stderr, "%s: line %d: "
106 			"no attributes specified: %s\n",
107 			fname, lineno, argv[0] );
108 		return LDAP_PARAM_ERROR;
109 	}
110 
111 	if ( argc > 1 ) {
112 		indexes = ldap_str2charray( argv[1], "," );
113 
114 		if( indexes == NULL ) {
115 			fprintf( stderr, "%s: line %d: "
116 				"no indexes specified: %s\n",
117 				fname, lineno, argv[1] );
118 			rc = LDAP_PARAM_ERROR;
119 			goto done;
120 		}
121 	}
122 
123 	if( indexes == NULL ) {
124 		mask = wi->wi_defaultmask;
125 
126 	} else {
127 		mask = 0;
128 
129 		for ( i = 0; indexes[i] != NULL; i++ ) {
130 			slap_mask_t index;
131 
132 			rc = slap_str2index( indexes[i], &index );
133 
134 			if( rc != LDAP_SUCCESS ) {
135 				if ( c_reply )
136 				{
137 					snprintf(c_reply->msg, sizeof(c_reply->msg),
138 						"index type \"%s\" undefined", indexes[i] );
139 
140 					fprintf( stderr, "%s: line %d: %s\n",
141 						fname, lineno, c_reply->msg );
142 				}
143 				rc = LDAP_PARAM_ERROR;
144 				goto done;
145 			}
146 
147 			mask |= index;
148 		}
149 	}
150 
151 	if( !mask ) {
152 		if ( c_reply )
153 		{
154 			snprintf(c_reply->msg, sizeof(c_reply->msg),
155 				"no indexes selected" );
156 			fprintf( stderr, "%s: line %d: %s\n",
157 				fname, lineno, c_reply->msg );
158 		}
159 		rc = LDAP_PARAM_ERROR;
160 		goto done;
161 	}
162 
163 	for ( i = 0; attrs[i] != NULL; i++ ) {
164 		AttrInfo	*a;
165 		AttributeDescription *ad;
166 		const char *text;
167 #ifdef LDAP_COMP_MATCH
168 		ComponentReference* cr = NULL;
169 		AttrInfo *a_cr = NULL;
170 #endif
171 
172 		if( strcasecmp( attrs[i], "default" ) == 0 ) {
173 			wi->wi_defaultmask |= mask;
174 			continue;
175 		}
176 
177 #ifdef LDAP_COMP_MATCH
178 		if ( is_component_reference( attrs[i] ) ) {
179 			rc = extract_component_reference( attrs[i], &cr );
180 			if ( rc != LDAP_SUCCESS ) {
181 				if ( c_reply )
182 				{
183 					snprintf(c_reply->msg, sizeof(c_reply->msg),
184 						"index component reference\"%s\" undefined",
185 						attrs[i] );
186 					fprintf( stderr, "%s: line %d: %s\n",
187 						fname, lineno, c_reply->msg );
188 				}
189 				goto done;
190 			}
191 			cr->cr_indexmask = mask;
192 			/*
193 			 * After extracting a component reference
194 			 * only the name of a attribute will be remaining
195 			 */
196 		} else {
197 			cr = NULL;
198 		}
199 #endif
200 		ad = NULL;
201 		rc = slap_str2ad( attrs[i], &ad, &text );
202 
203 		if( rc != LDAP_SUCCESS ) {
204 			if ( c_reply )
205 			{
206 				snprintf(c_reply->msg, sizeof(c_reply->msg),
207 					"index attribute \"%s\" undefined",
208 					attrs[i] );
209 
210 				fprintf( stderr, "%s: line %d: %s\n",
211 					fname, lineno, c_reply->msg );
212 			}
213 fail:
214 #ifdef LDAP_COMP_MATCH
215 			ch_free( cr );
216 #endif
217 			goto done;
218 		}
219 
220 		if( ad == slap_schema.si_ad_entryDN || slap_ad_is_binary( ad ) ) {
221 			if (c_reply) {
222 				snprintf(c_reply->msg, sizeof(c_reply->msg),
223 					"index of attribute \"%s\" disallowed", attrs[i] );
224 				fprintf( stderr, "%s: line %d: %s\n",
225 					fname, lineno, c_reply->msg );
226 			}
227 			rc = LDAP_UNWILLING_TO_PERFORM;
228 			goto fail;
229 		}
230 
231 		if( IS_SLAP_INDEX( mask, SLAP_INDEX_APPROX ) && !(
232 			ad->ad_type->sat_approx
233 				&& ad->ad_type->sat_approx->smr_indexer
234 				&& ad->ad_type->sat_approx->smr_filter ) )
235 		{
236 			if (c_reply) {
237 				snprintf(c_reply->msg, sizeof(c_reply->msg),
238 					"approx index of attribute \"%s\" disallowed", attrs[i] );
239 				fprintf( stderr, "%s: line %d: %s\n",
240 					fname, lineno, c_reply->msg );
241 			}
242 			rc = LDAP_INAPPROPRIATE_MATCHING;
243 			goto fail;
244 		}
245 
246 		if( IS_SLAP_INDEX( mask, SLAP_INDEX_EQUALITY ) && !(
247 			ad->ad_type->sat_equality
248 				&& ad->ad_type->sat_equality->smr_indexer
249 				&& ad->ad_type->sat_equality->smr_filter ) )
250 		{
251 			if (c_reply) {
252 				snprintf(c_reply->msg, sizeof(c_reply->msg),
253 					"equality index of attribute \"%s\" disallowed", attrs[i] );
254 				fprintf( stderr, "%s: line %d: %s\n",
255 					fname, lineno, c_reply->msg );
256 			}
257 			rc = LDAP_INAPPROPRIATE_MATCHING;
258 			goto fail;
259 		}
260 
261 		if( IS_SLAP_INDEX( mask, SLAP_INDEX_SUBSTR ) && !(
262 			ad->ad_type->sat_substr
263 				&& ad->ad_type->sat_substr->smr_indexer
264 				&& ad->ad_type->sat_substr->smr_filter ) )
265 		{
266 			if (c_reply) {
267 				snprintf(c_reply->msg, sizeof(c_reply->msg),
268 					"substr index of attribute \"%s\" disallowed", attrs[i] );
269 				fprintf( stderr, "%s: line %d: %s\n",
270 					fname, lineno, c_reply->msg );
271 			}
272 			rc = LDAP_INAPPROPRIATE_MATCHING;
273 			goto fail;
274 		}
275 
276 		Debug( LDAP_DEBUG_CONFIG, "index %s 0x%04lx\n",
277 			ad->ad_cname.bv_val, mask );
278 
279 		a = (AttrInfo *) ch_malloc( sizeof(AttrInfo) );
280 
281 #ifdef LDAP_COMP_MATCH
282 		a->ai_cr = NULL;
283 #endif
284 		a->ai_desc = ad;
285 
286 		if ( wi->wi_flags & WT_IS_OPEN ) {
287 			a->ai_indexmask = 0;
288 			a->ai_newmask = mask;
289 		} else {
290 			a->ai_indexmask = mask;
291 			a->ai_newmask = 0;
292 		}
293 
294 #ifdef LDAP_COMP_MATCH
295 		if ( cr ) {
296 			a_cr = wt_attr_mask( wi, ad );
297 			if ( a_cr ) {
298 				/*
299 				 * AttrInfo is already in AVL
300 				 * just add the extracted component reference
301 				 * in the AttrInfo
302 				 */
303 				ch_free( a );
304 				rc = insert_component_reference( cr, &a_cr->ai_cr );
305 				if ( rc != LDAP_SUCCESS) {
306 					fprintf( stderr, " error during inserting component reference in %s ", attrs[i]);
307 					rc = LDAP_PARAM_ERROR;
308 					goto fail;
309 				}
310 				continue;
311 			} else {
312 				rc = insert_component_reference( cr, &a->ai_cr );
313 				if ( rc != LDAP_SUCCESS) {
314 					fprintf( stderr, " error during inserting component reference in %s ", attrs[i]);
315 					rc = LDAP_PARAM_ERROR;
316 					ch_free( a );
317 					goto fail;
318 				}
319 			}
320 		}
321 #endif
322 		rc = ainfo_insert( wi, a );
323 		if( rc ) {
324 			if ( wi->wi_flags & WT_IS_OPEN ) {
325 				AttrInfo *b = wt_attr_mask( wi, ad );
326 				/* If there is already an index defined for this attribute
327 				 * it must be replaced. Otherwise we end up with multiple
328 				 * olcIndex values for the same attribute */
329 				if ( b->ai_indexmask & WT_INDEX_DELETING ) {
330 					/* If we were editing this attr, reset it */
331 					b->ai_indexmask &= ~WT_INDEX_DELETING;
332 					/* If this is leftover from a previous add, commit it */
333 					if ( b->ai_newmask )
334 						b->ai_indexmask = b->ai_newmask;
335 					b->ai_newmask = a->ai_newmask;
336 					ch_free( a );
337 					rc = 0;
338 					continue;
339 				}
340 			}
341 			if (c_reply) {
342 				snprintf(c_reply->msg, sizeof(c_reply->msg),
343 					"duplicate index definition for attr \"%s\"",
344 					attrs[i] );
345 				fprintf( stderr, "%s: line %d: %s\n",
346 					fname, lineno, c_reply->msg );
347 			}
348 
349 			rc = LDAP_PARAM_ERROR;
350 			goto done;
351 		}
352 	}
353 
354 done:
355 	ldap_charray_free( attrs );
356 	if ( indexes != NULL ) ldap_charray_free( indexes );
357 
358 	return rc;
359 }
360 
361 static int
wt_attr_index_unparser(void * v1,void * v2)362 wt_attr_index_unparser( void *v1, void *v2 )
363 {
364 	AttrInfo *ai = v1;
365 	BerVarray *bva = v2;
366 	struct berval bv;
367 	char *ptr;
368 
369 	slap_index2bvlen( ai->ai_indexmask, &bv );
370 	if ( bv.bv_len ) {
371 		bv.bv_len += ai->ai_desc->ad_cname.bv_len + 1;
372 		ptr = ch_malloc( bv.bv_len+1 );
373 		bv.bv_val = lutil_strcopy(ptr,
374 								  (const char*)ai->ai_desc->ad_cname.bv_val );
375 		*bv.bv_val++ = ' ';
376 		slap_index2bv( ai->ai_indexmask, &bv );
377 		bv.bv_val = ptr;
378 		ber_bvarray_add( bva, &bv );
379 	}
380 	return 0;
381 }
382 
383 static AttributeDescription addef = { NULL, NULL, BER_BVC("default") };
384 static AttrInfo aidef = { &addef };
385 
386 void
wt_attr_index_unparse(struct wt_info * wi,BerVarray * bva)387 wt_attr_index_unparse( struct wt_info *wi, BerVarray *bva )
388 {
389 	int i;
390 
391 	if ( wi->wi_defaultmask ) {
392 		aidef.ai_indexmask = wi->wi_defaultmask;
393 		wt_attr_index_unparser( &aidef, bva );
394 	}
395 	for ( i=0; i<wi->wi_nattrs; i++ )
396 		wt_attr_index_unparser( wi->wi_attrs[i], bva );
397 }
398 
399 void
wt_attr_info_free(AttrInfo * ai)400 wt_attr_info_free( AttrInfo *ai )
401 {
402 #ifdef LDAP_COMP_MATCH
403 	free( ai->ai_cr );
404 #endif
405 	free( ai );
406 }
407 
408 void
wt_attr_index_destroy(struct wt_info * wi)409 wt_attr_index_destroy( struct wt_info *wi )
410 {
411 	int i;
412 
413 	for ( i=0; i<wi->wi_nattrs; i++ )
414 		wt_attr_info_free( wi->wi_attrs[i] );
415 
416 	free( wi->wi_attrs );
417 }
418 
419 /*
420  * Local variables:
421  * indent-tabs-mode: t
422  * tab-width: 4
423  * c-basic-offset: 4
424  * End:
425  */
426