attr.c revision 1.1.1.1 1 /* $NetBSD: attr.c,v 1.1.1.1 2014/05/28 09:58:49 tron 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-2014 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 "portable.h"
20
21 #include <stdio.h>
22
23 #include <ac/socket.h>
24 #include <ac/string.h>
25
26 #include "slap.h"
27 #include "back-mdb.h"
28 #include "config.h"
29 #include "lutil.h"
30
31 /* Find the ad, return -1 if not found,
32 * set point for insertion if ins is non-NULL
33 */
34 int
35 mdb_attr_slot( struct mdb_info *mdb, AttributeDescription *ad, int *ins )
36 {
37 unsigned base = 0, cursor = 0;
38 unsigned n = mdb->mi_nattrs;
39 int val = 0;
40
41 while ( 0 < n ) {
42 unsigned pivot = n >> 1;
43 cursor = base + pivot;
44
45 val = SLAP_PTRCMP( ad, mdb->mi_attrs[cursor]->ai_desc );
46 if ( val < 0 ) {
47 n = pivot;
48 } else if ( val > 0 ) {
49 base = cursor + 1;
50 n -= pivot + 1;
51 } else {
52 return cursor;
53 }
54 }
55 if ( ins ) {
56 if ( val > 0 )
57 ++cursor;
58 *ins = cursor;
59 }
60 return -1;
61 }
62
63 static int
64 ainfo_insert( struct mdb_info *mdb, AttrInfo *a )
65 {
66 int x;
67 int i = mdb_attr_slot( mdb, a->ai_desc, &x );
68
69 /* Is it a dup? */
70 if ( i >= 0 )
71 return -1;
72
73 mdb->mi_attrs = ch_realloc( mdb->mi_attrs, ( mdb->mi_nattrs+1 ) *
74 sizeof( AttrInfo * ));
75 if ( x < mdb->mi_nattrs )
76 AC_MEMCPY( &mdb->mi_attrs[x+1], &mdb->mi_attrs[x],
77 ( mdb->mi_nattrs - x ) * sizeof( AttrInfo *));
78 mdb->mi_attrs[x] = a;
79 mdb->mi_nattrs++;
80 return 0;
81 }
82
83 AttrInfo *
84 mdb_attr_mask(
85 struct mdb_info *mdb,
86 AttributeDescription *desc )
87 {
88 int i = mdb_attr_slot( mdb, desc, NULL );
89 return i < 0 ? NULL : mdb->mi_attrs[i];
90 }
91
92 /* Open all un-opened index DB handles */
93 int
94 mdb_attr_dbs_open(
95 BackendDB *be, MDB_txn *tx0, ConfigReply *cr )
96 {
97 struct mdb_info *mdb = (struct mdb_info *) be->be_private;
98 MDB_txn *txn;
99 MDB_dbi *dbis = NULL;
100 int i, flags;
101 int rc;
102
103 txn = tx0;
104 if ( txn == NULL ) {
105 rc = mdb_txn_begin( mdb->mi_dbenv, NULL, 0, &txn );
106 if ( rc ) {
107 snprintf( cr->msg, sizeof(cr->msg), "database \"%s\": "
108 "txn_begin failed: %s (%d).",
109 be->be_suffix[0].bv_val, mdb_strerror(rc), rc );
110 Debug( LDAP_DEBUG_ANY,
111 LDAP_XSTRING(mdb_attr_dbs) ": %s\n",
112 cr->msg, 0, 0 );
113 return rc;
114 }
115 dbis = ch_calloc( 1, mdb->mi_nattrs * sizeof(MDB_dbi) );
116 } else {
117 rc = 0;
118 }
119
120 flags = MDB_DUPSORT|MDB_DUPFIXED|MDB_INTEGERDUP;
121 if ( !(slapMode & SLAP_TOOL_READONLY) )
122 flags |= MDB_CREATE;
123
124 for ( i=0; i<mdb->mi_nattrs; i++ ) {
125 if ( mdb->mi_attrs[i]->ai_dbi ) /* already open */
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, 0, 0 );
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, 0, 0 );
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
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
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 goto done;
315 }
316
317 if( ad == slap_schema.si_ad_entryDN || slap_ad_is_binary( ad ) ) {
318 if (c_reply) {
319 snprintf(c_reply->msg, sizeof(c_reply->msg),
320 "index of attribute \"%s\" disallowed", attrs[i] );
321 fprintf( stderr, "%s: line %d: %s\n",
322 fname, lineno, c_reply->msg );
323 }
324 rc = LDAP_UNWILLING_TO_PERFORM;
325 goto done;
326 }
327
328 if( IS_SLAP_INDEX( mask, SLAP_INDEX_APPROX ) && !(
329 ad->ad_type->sat_approx
330 && ad->ad_type->sat_approx->smr_indexer
331 && ad->ad_type->sat_approx->smr_filter ) )
332 {
333 if (c_reply) {
334 snprintf(c_reply->msg, sizeof(c_reply->msg),
335 "approx index of attribute \"%s\" disallowed", attrs[i] );
336 fprintf( stderr, "%s: line %d: %s\n",
337 fname, lineno, c_reply->msg );
338 }
339 rc = LDAP_INAPPROPRIATE_MATCHING;
340 goto done;
341 }
342
343 if( IS_SLAP_INDEX( mask, SLAP_INDEX_EQUALITY ) && !(
344 ad->ad_type->sat_equality
345 && ad->ad_type->sat_equality->smr_indexer
346 && ad->ad_type->sat_equality->smr_filter ) )
347 {
348 if (c_reply) {
349 snprintf(c_reply->msg, sizeof(c_reply->msg),
350 "equality index of attribute \"%s\" disallowed", attrs[i] );
351 fprintf( stderr, "%s: line %d: %s\n",
352 fname, lineno, c_reply->msg );
353 }
354 rc = LDAP_INAPPROPRIATE_MATCHING;
355 goto done;
356 }
357
358 if( IS_SLAP_INDEX( mask, SLAP_INDEX_SUBSTR ) && !(
359 ad->ad_type->sat_substr
360 && ad->ad_type->sat_substr->smr_indexer
361 && ad->ad_type->sat_substr->smr_filter ) )
362 {
363 if (c_reply) {
364 snprintf(c_reply->msg, sizeof(c_reply->msg),
365 "substr index of attribute \"%s\" disallowed", attrs[i] );
366 fprintf( stderr, "%s: line %d: %s\n",
367 fname, lineno, c_reply->msg );
368 }
369 rc = LDAP_INAPPROPRIATE_MATCHING;
370 goto done;
371 }
372
373 Debug( LDAP_DEBUG_CONFIG, "index %s 0x%04lx\n",
374 ad->ad_cname.bv_val, mask, 0 );
375
376 a = (AttrInfo *) ch_malloc( sizeof(AttrInfo) );
377
378 #ifdef LDAP_COMP_MATCH
379 a->ai_cr = NULL;
380 #endif
381 a->ai_cursor = NULL;
382 a->ai_flist = NULL;
383 a->ai_clist = NULL;
384 a->ai_root = NULL;
385 a->ai_desc = ad;
386 a->ai_dbi = 0;
387
388 if ( mdb->mi_flags & MDB_IS_OPEN ) {
389 a->ai_indexmask = 0;
390 a->ai_newmask = mask;
391 } else {
392 a->ai_indexmask = mask;
393 a->ai_newmask = 0;
394 }
395
396 #ifdef LDAP_COMP_MATCH
397 if ( cr ) {
398 a_cr = mdb_attr_mask( mdb, ad );
399 if ( a_cr ) {
400 /*
401 * AttrInfo is already in AVL
402 * just add the extracted component reference
403 * in the AttrInfo
404 */
405 rc = insert_component_reference( cr, &a_cr->ai_cr );
406 if ( rc != LDAP_SUCCESS) {
407 fprintf( stderr, " error during inserting component reference in %s ", attrs[i]);
408 rc = LDAP_PARAM_ERROR;
409 goto done;
410 }
411 continue;
412 } else {
413 rc = insert_component_reference( cr, &a->ai_cr );
414 if ( rc != LDAP_SUCCESS) {
415 fprintf( stderr, " error during inserting component reference in %s ", attrs[i]);
416 rc = LDAP_PARAM_ERROR;
417 goto done;
418 }
419 }
420 }
421 #endif
422 rc = ainfo_insert( mdb, a );
423 if( rc ) {
424 if ( mdb->mi_flags & MDB_IS_OPEN ) {
425 AttrInfo *b = mdb_attr_mask( mdb, ad );
426 /* If there is already an index defined for this attribute
427 * it must be replaced. Otherwise we end up with multiple
428 * olcIndex values for the same attribute */
429 if ( b->ai_indexmask & MDB_INDEX_DELETING ) {
430 /* If we were editing this attr, reset it */
431 b->ai_indexmask &= ~MDB_INDEX_DELETING;
432 /* If this is leftover from a previous add, commit it */
433 if ( b->ai_newmask )
434 b->ai_indexmask = b->ai_newmask;
435 b->ai_newmask = a->ai_newmask;
436 ch_free( a );
437 rc = 0;
438 continue;
439 }
440 }
441 if (c_reply) {
442 snprintf(c_reply->msg, sizeof(c_reply->msg),
443 "duplicate index definition for attr \"%s\"",
444 attrs[i] );
445 fprintf( stderr, "%s: line %d: %s\n",
446 fname, lineno, c_reply->msg );
447 }
448
449 rc = LDAP_PARAM_ERROR;
450 goto done;
451 }
452 }
453
454 done:
455 ldap_charray_free( attrs );
456 if ( indexes != NULL ) ldap_charray_free( indexes );
457
458 return rc;
459 }
460
461 static int
462 mdb_attr_index_unparser( void *v1, void *v2 )
463 {
464 AttrInfo *ai = v1;
465 BerVarray *bva = v2;
466 struct berval bv;
467 char *ptr;
468
469 slap_index2bvlen( ai->ai_indexmask, &bv );
470 if ( bv.bv_len ) {
471 bv.bv_len += ai->ai_desc->ad_cname.bv_len + 1;
472 ptr = ch_malloc( bv.bv_len+1 );
473 bv.bv_val = lutil_strcopy( ptr, ai->ai_desc->ad_cname.bv_val );
474 *bv.bv_val++ = ' ';
475 slap_index2bv( ai->ai_indexmask, &bv );
476 bv.bv_val = ptr;
477 ber_bvarray_add( bva, &bv );
478 }
479 return 0;
480 }
481
482 static AttributeDescription addef = { NULL, NULL, BER_BVC("default") };
483 static AttrInfo aidef = { &addef };
484
485 void
486 mdb_attr_index_unparse( struct mdb_info *mdb, BerVarray *bva )
487 {
488 int i;
489
490 if ( mdb->mi_defaultmask ) {
491 aidef.ai_indexmask = mdb->mi_defaultmask;
492 mdb_attr_index_unparser( &aidef, bva );
493 }
494 for ( i=0; i<mdb->mi_nattrs; i++ )
495 mdb_attr_index_unparser( mdb->mi_attrs[i], bva );
496 }
497
498 void
499 mdb_attr_info_free( AttrInfo *ai )
500 {
501 #ifdef LDAP_COMP_MATCH
502 free( ai->ai_cr );
503 #endif
504 free( ai );
505 }
506
507 void
508 mdb_attr_index_destroy( struct mdb_info *mdb )
509 {
510 int i;
511
512 for ( i=0; i<mdb->mi_nattrs; i++ )
513 mdb_attr_info_free( mdb->mi_attrs[i] );
514
515 free( mdb->mi_attrs );
516 }
517
518 void mdb_attr_index_free( struct mdb_info *mdb, AttributeDescription *ad )
519 {
520 int i;
521
522 i = mdb_attr_slot( mdb, ad, NULL );
523 if ( i >= 0 ) {
524 mdb_attr_info_free( mdb->mi_attrs[i] );
525 mdb->mi_nattrs--;
526 for (; i<mdb->mi_nattrs; i++)
527 mdb->mi_attrs[i] = mdb->mi_attrs[i+1];
528 }
529 }
530
531 void mdb_attr_flush( struct mdb_info *mdb )
532 {
533 int i;
534
535 for ( i=0; i<mdb->mi_nattrs; i++ ) {
536 if ( mdb->mi_attrs[i]->ai_indexmask & MDB_INDEX_DELETING ) {
537 int j;
538 mdb_attr_info_free( mdb->mi_attrs[i] );
539 mdb->mi_nattrs--;
540 for (j=i; j<mdb->mi_nattrs; j++)
541 mdb->mi_attrs[j] = mdb->mi_attrs[j+1];
542 i--;
543 }
544 }
545 }
546
547 int mdb_ad_read( struct mdb_info *mdb, MDB_txn *txn )
548 {
549 int i, rc;
550 MDB_cursor *mc;
551 MDB_val key, data;
552 struct berval bdata;
553 const char *text;
554 AttributeDescription *ad;
555
556 rc = mdb_cursor_open( txn, mdb->mi_ad2id, &mc );
557 if ( rc ) {
558 Debug( LDAP_DEBUG_ANY,
559 "mdb_ad_read: cursor_open failed %s(%d)\n",
560 mdb_strerror(rc), rc, 0);
561 return rc;
562 }
563
564 /* our array is 1-based, an index of 0 means no data */
565 i = mdb->mi_numads+1;
566 key.mv_size = sizeof(int);
567 key.mv_data = &i;
568
569 rc = mdb_cursor_get( mc, &key, &data, MDB_SET );
570
571 while ( rc == MDB_SUCCESS ) {
572 bdata.bv_len = data.mv_size;
573 bdata.bv_val = data.mv_data;
574 ad = NULL;
575 rc = slap_bv2ad( &bdata, &ad, &text );
576 if ( rc ) {
577 rc = slap_bv2undef_ad( &bdata, &mdb->mi_ads[i], &text, 0 );
578 } else {
579 if ( ad->ad_index >= MDB_MAXADS ) {
580 Debug( LDAP_DEBUG_ANY,
581 "mdb_adb_read: too many AttributeDescriptions in use\n",
582 0, 0, 0 );
583 return LDAP_OTHER;
584 }
585 mdb->mi_adxs[ad->ad_index] = i;
586 mdb->mi_ads[i] = ad;
587 }
588 i++;
589 rc = mdb_cursor_get( mc, &key, &data, MDB_NEXT );
590 }
591 mdb->mi_numads = i-1;
592
593 done:
594 if ( rc == MDB_NOTFOUND )
595 rc = 0;
596
597 mdb_cursor_close( mc );
598
599 return rc;
600 }
601
602 int mdb_ad_get( struct mdb_info *mdb, MDB_txn *txn, AttributeDescription *ad )
603 {
604 int i, rc;
605 MDB_val key, val;
606
607 rc = mdb_ad_read( mdb, txn );
608 if (rc)
609 return rc;
610
611 if ( mdb->mi_adxs[ad->ad_index] )
612 return 0;
613
614 i = mdb->mi_numads+1;
615 key.mv_size = sizeof(int);
616 key.mv_data = &i;
617 val.mv_size = ad->ad_cname.bv_len;
618 val.mv_data = ad->ad_cname.bv_val;
619
620 rc = mdb_put( txn, mdb->mi_ad2id, &key, &val, 0 );
621 if ( rc == MDB_SUCCESS ) {
622 mdb->mi_adxs[ad->ad_index] = i;
623 mdb->mi_ads[i] = ad;
624 mdb->mi_numads++;
625 } else {
626 Debug( LDAP_DEBUG_ANY,
627 "mdb_ad_get: mdb_put failed %s(%d)\n",
628 mdb_strerror(rc), rc, 0);
629 }
630
631 return rc;
632 }
633