]> git.sur5r.net Git - openldap/blob - servers/slapd/back-mdb/attr.c
Merge remote-tracking branch 'origin/mdb.master'
[openldap] / servers / slapd / back-mdb / attr.c
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-2013 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 "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
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
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 *
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
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         int i, flags;
98         int rc;
99
100         txn = tx0;
101         if ( txn == NULL ) {
102                 rc = mdb_txn_begin( mdb->mi_dbenv, NULL, 0, &txn );
103                 if ( rc ) {
104                         snprintf( cr->msg, sizeof(cr->msg), "database \"%s\": "
105                                 "txn_begin failed: %s (%d).",
106                                 be->be_suffix[0].bv_val, mdb_strerror(rc), rc );
107                         Debug( LDAP_DEBUG_ANY,
108                                 LDAP_XSTRING(mdb_attr_dbs) ": %s\n",
109                                 cr->msg, 0, 0 );
110                         return rc;
111                 }
112         } else {
113                 rc = 0;
114         }
115
116         flags = MDB_DUPSORT|MDB_DUPFIXED|MDB_INTEGERDUP;
117         if ( !(slapMode & SLAP_TOOL_READONLY) )
118                 flags |= MDB_CREATE;
119
120         for ( i=0; i<mdb->mi_nattrs; i++ ) {
121                 if ( mdb->mi_attrs[i]->ai_dbi ) /* already open */
122                         continue;
123                 rc = mdb_dbi_open( txn, mdb->mi_attrs[i]->ai_desc->ad_type->sat_cname.bv_val,
124                         flags, &mdb->mi_attrs[i]->ai_dbi );
125                 if ( rc ) {
126                         snprintf( cr->msg, sizeof(cr->msg), "database \"%s\": "
127                                 "mdb_dbi_open(%s) failed: %s (%d).",
128                                 be->be_suffix[0].bv_val,
129                                 mdb->mi_attrs[i]->ai_desc->ad_type->sat_cname.bv_val,
130                                 mdb_strerror(rc), rc );
131                         Debug( LDAP_DEBUG_ANY,
132                                 LDAP_XSTRING(mdb_attr_dbs) ": %s\n",
133                                 cr->msg, 0, 0 );
134                         break;
135                 }
136         }
137
138         /* Only commit if this is our txn */
139         if ( tx0 == NULL ) {
140                 if ( !rc ) {
141                         rc = mdb_txn_commit( txn );
142                         if ( rc ) {
143                                 snprintf( cr->msg, sizeof(cr->msg), "database \"%s\": "
144                                         "txn_commit failed: %s (%d).",
145                                         be->be_suffix[0].bv_val, mdb_strerror(rc), rc );
146                                 Debug( LDAP_DEBUG_ANY,
147                                         LDAP_XSTRING(mdb_attr_dbs) ": %s\n",
148                                         cr->msg, 0, 0 );
149                         }
150                 } else {
151                         mdb_txn_abort( txn );
152                 }
153         }
154
155         return rc;
156 }
157
158 void
159 mdb_attr_dbs_close(
160         struct mdb_info *mdb
161 )
162 {
163         int i;
164         for ( i=0; i<mdb->mi_nattrs; i++ )
165                 if ( mdb->mi_attrs[i]->ai_dbi ) {
166                         mdb_dbi_close( mdb->mi_dbenv, mdb->mi_attrs[i]->ai_dbi );
167                         mdb->mi_attrs[i]->ai_dbi = 0;
168                 }
169 }
170
171 int
172 mdb_attr_index_config(
173         struct mdb_info *mdb,
174         const char              *fname,
175         int                     lineno,
176         int                     argc,
177         char            **argv,
178         struct          config_reply_s *c_reply)
179 {
180         int rc = 0;
181         int     i;
182         slap_mask_t mask;
183         char **attrs;
184         char **indexes = NULL;
185
186         attrs = ldap_str2charray( argv[0], "," );
187
188         if( attrs == NULL ) {
189                 fprintf( stderr, "%s: line %d: "
190                         "no attributes specified: %s\n",
191                         fname, lineno, argv[0] );
192                 return LDAP_PARAM_ERROR;
193         }
194
195         if ( argc > 1 ) {
196                 indexes = ldap_str2charray( argv[1], "," );
197
198                 if( indexes == NULL ) {
199                         fprintf( stderr, "%s: line %d: "
200                                 "no indexes specified: %s\n",
201                                 fname, lineno, argv[1] );
202                         rc = LDAP_PARAM_ERROR;
203                         goto done;
204                 }
205         }
206
207         if( indexes == NULL ) {
208                 mask = mdb->mi_defaultmask;
209
210         } else {
211                 mask = 0;
212
213                 for ( i = 0; indexes[i] != NULL; i++ ) {
214                         slap_mask_t index;
215                         rc = slap_str2index( indexes[i], &index );
216
217                         if( rc != LDAP_SUCCESS ) {
218                                 if ( c_reply )
219                                 {
220                                         snprintf(c_reply->msg, sizeof(c_reply->msg),
221                                                 "index type \"%s\" undefined", indexes[i] );
222
223                                         fprintf( stderr, "%s: line %d: %s\n",
224                                                 fname, lineno, c_reply->msg );
225                                 }
226                                 rc = LDAP_PARAM_ERROR;
227                                 goto done;
228                         }
229
230                         mask |= index;
231                 }
232         }
233
234         if( !mask ) {
235                 if ( c_reply )
236                 {
237                         snprintf(c_reply->msg, sizeof(c_reply->msg),
238                                 "no indexes selected" );
239                         fprintf( stderr, "%s: line %d: %s\n",
240                                 fname, lineno, c_reply->msg );
241                 }
242                 rc = LDAP_PARAM_ERROR;
243                 goto done;
244         }
245
246         for ( i = 0; attrs[i] != NULL; i++ ) {
247                 AttrInfo        *a;
248                 AttributeDescription *ad;
249                 const char *text;
250 #ifdef LDAP_COMP_MATCH
251                 ComponentReference* cr = NULL;
252                 AttrInfo *a_cr = NULL;
253 #endif
254
255                 if( strcasecmp( attrs[i], "default" ) == 0 ) {
256                         mdb->mi_defaultmask |= mask;
257                         continue;
258                 }
259
260 #ifdef LDAP_COMP_MATCH
261                 if ( is_component_reference( attrs[i] ) ) {
262                         rc = extract_component_reference( attrs[i], &cr );
263                         if ( rc != LDAP_SUCCESS ) {
264                                 if ( c_reply )
265                                 {
266                                         snprintf(c_reply->msg, sizeof(c_reply->msg),
267                                                 "index component reference\"%s\" undefined",
268                                                 attrs[i] );
269                                         fprintf( stderr, "%s: line %d: %s\n",
270                                                 fname, lineno, c_reply->msg );
271                                 }
272                                 goto done;
273                         }
274                         cr->cr_indexmask = mask;
275                         /*
276                          * After extracting a component reference
277                          * only the name of a attribute will be remaining
278                          */
279                 } else {
280                         cr = NULL;
281                 }
282 #endif
283                 ad = NULL;
284                 rc = slap_str2ad( attrs[i], &ad, &text );
285
286                 if( rc != LDAP_SUCCESS ) {
287                         if ( c_reply )
288                         {
289                                 snprintf(c_reply->msg, sizeof(c_reply->msg),
290                                         "index attribute \"%s\" undefined",
291                                         attrs[i] );
292
293                                 fprintf( stderr, "%s: line %d: %s\n",
294                                         fname, lineno, c_reply->msg );
295                         }
296                         goto done;
297                 }
298
299                 if( ad == slap_schema.si_ad_entryDN || slap_ad_is_binary( ad ) ) {
300                         if (c_reply) {
301                                 snprintf(c_reply->msg, sizeof(c_reply->msg),
302                                         "index of attribute \"%s\" disallowed", attrs[i] );
303                                 fprintf( stderr, "%s: line %d: %s\n",
304                                         fname, lineno, c_reply->msg );
305                         }
306                         rc = LDAP_UNWILLING_TO_PERFORM;
307                         goto done;
308                 }
309
310                 if( IS_SLAP_INDEX( mask, SLAP_INDEX_APPROX ) && !(
311                         ad->ad_type->sat_approx
312                                 && ad->ad_type->sat_approx->smr_indexer
313                                 && ad->ad_type->sat_approx->smr_filter ) )
314                 {
315                         if (c_reply) {
316                                 snprintf(c_reply->msg, sizeof(c_reply->msg),
317                                         "approx index of attribute \"%s\" disallowed", attrs[i] );
318                                 fprintf( stderr, "%s: line %d: %s\n",
319                                         fname, lineno, c_reply->msg );
320                         }
321                         rc = LDAP_INAPPROPRIATE_MATCHING;
322                         goto done;
323                 }
324
325                 if( IS_SLAP_INDEX( mask, SLAP_INDEX_EQUALITY ) && !(
326                         ad->ad_type->sat_equality
327                                 && ad->ad_type->sat_equality->smr_indexer
328                                 && ad->ad_type->sat_equality->smr_filter ) )
329                 {
330                         if (c_reply) {
331                                 snprintf(c_reply->msg, sizeof(c_reply->msg),
332                                         "equality index of attribute \"%s\" disallowed", attrs[i] );
333                                 fprintf( stderr, "%s: line %d: %s\n",
334                                         fname, lineno, c_reply->msg );
335                         }
336                         rc = LDAP_INAPPROPRIATE_MATCHING;
337                         goto done;
338                 }
339
340                 if( IS_SLAP_INDEX( mask, SLAP_INDEX_SUBSTR ) && !(
341                         ad->ad_type->sat_substr
342                                 && ad->ad_type->sat_substr->smr_indexer
343                                 && ad->ad_type->sat_substr->smr_filter ) )
344                 {
345                         if (c_reply) {
346                                 snprintf(c_reply->msg, sizeof(c_reply->msg),
347                                         "substr index of attribute \"%s\" disallowed", attrs[i] );
348                                 fprintf( stderr, "%s: line %d: %s\n",
349                                         fname, lineno, c_reply->msg );
350                         }
351                         rc = LDAP_INAPPROPRIATE_MATCHING;
352                         goto done;
353                 }
354
355                 Debug( LDAP_DEBUG_CONFIG, "index %s 0x%04lx\n",
356                         ad->ad_cname.bv_val, mask, 0 ); 
357
358                 a = (AttrInfo *) ch_malloc( sizeof(AttrInfo) );
359
360 #ifdef LDAP_COMP_MATCH
361                 a->ai_cr = NULL;
362 #endif
363                 a->ai_cursor = NULL;
364                 a->ai_flist = NULL;
365                 a->ai_clist = NULL;
366                 a->ai_root = NULL;
367                 a->ai_desc = ad;
368                 a->ai_dbi = 0;
369
370                 if ( mdb->mi_flags & MDB_IS_OPEN ) {
371                         a->ai_indexmask = 0;
372                         a->ai_newmask = mask;
373                 } else {
374                         a->ai_indexmask = mask;
375                         a->ai_newmask = 0;
376                 }
377
378 #ifdef LDAP_COMP_MATCH
379                 if ( cr ) {
380                         a_cr = mdb_attr_mask( mdb, ad );
381                         if ( a_cr ) {
382                                 /*
383                                  * AttrInfo is already in AVL
384                                  * just add the extracted component reference
385                                  * in the AttrInfo
386                                  */
387                                 rc = insert_component_reference( cr, &a_cr->ai_cr );
388                                 if ( rc != LDAP_SUCCESS) {
389                                         fprintf( stderr, " error during inserting component reference in %s ", attrs[i]);
390                                         rc = LDAP_PARAM_ERROR;
391                                         goto done;
392                                 }
393                                 continue;
394                         } else {
395                                 rc = insert_component_reference( cr, &a->ai_cr );
396                                 if ( rc != LDAP_SUCCESS) {
397                                         fprintf( stderr, " error during inserting component reference in %s ", attrs[i]);
398                                         rc = LDAP_PARAM_ERROR;
399                                         goto done;
400                                 }
401                         }
402                 }
403 #endif
404                 rc = ainfo_insert( mdb, a );
405                 if( rc ) {
406                         if ( mdb->mi_flags & MDB_IS_OPEN ) {
407                                 AttrInfo *b = mdb_attr_mask( mdb, ad );
408                                 /* If there is already an index defined for this attribute
409                                  * it must be replaced. Otherwise we end up with multiple 
410                                  * olcIndex values for the same attribute */
411                                 if ( b->ai_indexmask & MDB_INDEX_DELETING ) {
412                                         /* If we were editing this attr, reset it */
413                                         b->ai_indexmask &= ~MDB_INDEX_DELETING;
414                                         /* If this is leftover from a previous add, commit it */
415                                         if ( b->ai_newmask )
416                                                 b->ai_indexmask = b->ai_newmask;
417                                         b->ai_newmask = a->ai_newmask;
418                                         ch_free( a );
419                                         rc = 0;
420                                         continue;
421                                 }
422                         }
423                         if (c_reply) {
424                                 snprintf(c_reply->msg, sizeof(c_reply->msg),
425                                         "duplicate index definition for attr \"%s\"",
426                                         attrs[i] );
427                                 fprintf( stderr, "%s: line %d: %s\n",
428                                         fname, lineno, c_reply->msg );
429                         }
430
431                         rc = LDAP_PARAM_ERROR;
432                         goto done;
433                 }
434         }
435
436 done:
437         ldap_charray_free( attrs );
438         if ( indexes != NULL ) ldap_charray_free( indexes );
439
440         return rc;
441 }
442
443 static int
444 mdb_attr_index_unparser( void *v1, void *v2 )
445 {
446         AttrInfo *ai = v1;
447         BerVarray *bva = v2;
448         struct berval bv;
449         char *ptr;
450
451         slap_index2bvlen( ai->ai_indexmask, &bv );
452         if ( bv.bv_len ) {
453                 bv.bv_len += ai->ai_desc->ad_cname.bv_len + 1;
454                 ptr = ch_malloc( bv.bv_len+1 );
455                 bv.bv_val = lutil_strcopy( ptr, ai->ai_desc->ad_cname.bv_val );
456                 *bv.bv_val++ = ' ';
457                 slap_index2bv( ai->ai_indexmask, &bv );
458                 bv.bv_val = ptr;
459                 ber_bvarray_add( bva, &bv );
460         }
461         return 0;
462 }
463
464 static AttributeDescription addef = { NULL, NULL, BER_BVC("default") };
465 static AttrInfo aidef = { &addef };
466
467 void
468 mdb_attr_index_unparse( struct mdb_info *mdb, BerVarray *bva )
469 {
470         int i;
471
472         if ( mdb->mi_defaultmask ) {
473                 aidef.ai_indexmask = mdb->mi_defaultmask;
474                 mdb_attr_index_unparser( &aidef, bva );
475         }
476         for ( i=0; i<mdb->mi_nattrs; i++ )
477                 mdb_attr_index_unparser( mdb->mi_attrs[i], bva );
478 }
479
480 void
481 mdb_attr_info_free( AttrInfo *ai )
482 {
483 #ifdef LDAP_COMP_MATCH
484         free( ai->ai_cr );
485 #endif
486         free( ai );
487 }
488
489 void
490 mdb_attr_index_destroy( struct mdb_info *mdb )
491 {
492         int i;
493
494         for ( i=0; i<mdb->mi_nattrs; i++ ) 
495                 mdb_attr_info_free( mdb->mi_attrs[i] );
496
497         free( mdb->mi_attrs );
498 }
499
500 void mdb_attr_index_free( struct mdb_info *mdb, AttributeDescription *ad )
501 {
502         int i;
503
504         i = mdb_attr_slot( mdb, ad, NULL );
505         if ( i >= 0 ) {
506                 mdb_attr_info_free( mdb->mi_attrs[i] );
507                 mdb->mi_nattrs--;
508                 for (; i<mdb->mi_nattrs; i++)
509                         mdb->mi_attrs[i] = mdb->mi_attrs[i+1];
510         }
511 }
512
513 void mdb_attr_flush( struct mdb_info *mdb )
514 {
515         int i;
516
517         for ( i=0; i<mdb->mi_nattrs; i++ ) {
518                 if ( mdb->mi_attrs[i]->ai_indexmask & MDB_INDEX_DELETING ) {
519                         int j;
520                         mdb_attr_info_free( mdb->mi_attrs[i] );
521                         mdb->mi_nattrs--;
522                         for (j=i; j<mdb->mi_nattrs; j++)
523                                 mdb->mi_attrs[j] = mdb->mi_attrs[j+1];
524                         i--;
525                 }
526         }
527 }
528
529 int mdb_ad_read( struct mdb_info *mdb, MDB_txn *txn )
530 {
531         int i, rc;
532         MDB_cursor *mc;
533         MDB_val key, data;
534         struct berval bdata;
535         const char *text;
536         AttributeDescription *ad;
537
538         rc = mdb_cursor_open( txn, mdb->mi_ad2id, &mc );
539         if ( rc ) {
540                 Debug( LDAP_DEBUG_ANY,
541                         "mdb_ad_read: cursor_open failed %s(%d)\n",
542                         mdb_strerror(rc), rc, 0);
543                 return rc;
544         }
545
546         /* our array is 1-based, an index of 0 means no data */
547         i = mdb->mi_numads+1;
548         key.mv_size = sizeof(int);
549         key.mv_data = &i;
550
551         rc = mdb_cursor_get( mc, &key, &data, MDB_SET );
552
553         while ( rc == MDB_SUCCESS ) {
554                 bdata.bv_len = data.mv_size;
555                 bdata.bv_val = data.mv_data;
556                 ad = NULL;
557                 rc = slap_bv2ad( &bdata, &ad, &text );
558                 if ( rc ) {
559                         rc = slap_bv2undef_ad( &bdata, &mdb->mi_ads[i], &text, 0 );
560                 } else {
561                         if ( ad->ad_index >= MDB_MAXADS ) {
562                                 Debug( LDAP_DEBUG_ANY,
563                                         "mdb_adb_read: too many AttributeDescriptions in use\n",
564                                         0, 0, 0 );
565                                 return LDAP_OTHER;
566                         }
567                         mdb->mi_adxs[ad->ad_index] = i;
568                         mdb->mi_ads[i] = ad;
569                 }
570                 i++;
571                 rc = mdb_cursor_get( mc, &key, &data, MDB_NEXT );
572         }
573         mdb->mi_numads = i-1;
574
575 done:
576         if ( rc == MDB_NOTFOUND )
577                 rc = 0;
578
579         mdb_cursor_close( mc );
580
581         return rc;
582 }
583
584 int mdb_ad_get( struct mdb_info *mdb, MDB_txn *txn, AttributeDescription *ad )
585 {
586         int i, rc;
587         MDB_val key, val;
588
589         rc = mdb_ad_read( mdb, txn );
590         if (rc)
591                 return rc;
592
593         if ( mdb->mi_adxs[ad->ad_index] )
594                 return 0;
595
596         i = mdb->mi_numads+1;
597         key.mv_size = sizeof(int);
598         key.mv_data = &i;
599         val.mv_size = ad->ad_cname.bv_len;
600         val.mv_data = ad->ad_cname.bv_val;
601
602         rc = mdb_put( txn, mdb->mi_ad2id, &key, &val, 0 );
603         if ( rc == MDB_SUCCESS ) {
604                 mdb->mi_adxs[ad->ad_index] = i;
605                 mdb->mi_ads[i] = ad;
606                 mdb->mi_numads++;
607         } else {
608                 Debug( LDAP_DEBUG_ANY,
609                         "mdb_ad_get: mdb_put failed %s(%d)\n",
610                         mdb_strerror(rc), rc, 0);
611         }
612
613         return rc;
614 }