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