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