1 1.3 christos /* $NetBSD: attr.c,v 1.4 2025/09/05 21:16:27 christos Exp $ */ 2 1.1 tron 3 1.1 tron /* attr.c - backend routines for dealing with attributes */ 4 1.1 tron /* $OpenLDAP$ */ 5 1.1 tron /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 6 1.1 tron * 7 1.4 christos * Copyright 2000-2024 The OpenLDAP Foundation. 8 1.1 tron * All rights reserved. 9 1.1 tron * 10 1.1 tron * Redistribution and use in source and binary forms, with or without 11 1.1 tron * modification, are permitted only as authorized by the OpenLDAP 12 1.1 tron * Public License. 13 1.1 tron * 14 1.1 tron * A copy of this license is available in the file LICENSE in the 15 1.1 tron * top-level directory of the distribution or, alternatively, at 16 1.1 tron * <http://www.OpenLDAP.org/license.html>. 17 1.1 tron */ 18 1.1 tron 19 1.2 christos #include <sys/cdefs.h> 20 1.3 christos __RCSID("$NetBSD: attr.c,v 1.4 2025/09/05 21:16:27 christos Exp $"); 21 1.2 christos 22 1.1 tron #include "portable.h" 23 1.1 tron 24 1.1 tron #include <stdio.h> 25 1.1 tron 26 1.1 tron #include <ac/socket.h> 27 1.1 tron #include <ac/string.h> 28 1.1 tron 29 1.1 tron #include "slap.h" 30 1.1 tron #include "back-mdb.h" 31 1.3 christos #include "slap-config.h" 32 1.1 tron #include "lutil.h" 33 1.1 tron 34 1.1 tron /* Find the ad, return -1 if not found, 35 1.1 tron * set point for insertion if ins is non-NULL 36 1.1 tron */ 37 1.1 tron int 38 1.1 tron mdb_attr_slot( struct mdb_info *mdb, AttributeDescription *ad, int *ins ) 39 1.1 tron { 40 1.1 tron unsigned base = 0, cursor = 0; 41 1.1 tron unsigned n = mdb->mi_nattrs; 42 1.1 tron int val = 0; 43 1.1 tron 44 1.1 tron while ( 0 < n ) { 45 1.1 tron unsigned pivot = n >> 1; 46 1.1 tron cursor = base + pivot; 47 1.1 tron 48 1.1 tron val = SLAP_PTRCMP( ad, mdb->mi_attrs[cursor]->ai_desc ); 49 1.1 tron if ( val < 0 ) { 50 1.1 tron n = pivot; 51 1.1 tron } else if ( val > 0 ) { 52 1.1 tron base = cursor + 1; 53 1.1 tron n -= pivot + 1; 54 1.1 tron } else { 55 1.1 tron return cursor; 56 1.1 tron } 57 1.1 tron } 58 1.1 tron if ( ins ) { 59 1.1 tron if ( val > 0 ) 60 1.1 tron ++cursor; 61 1.1 tron *ins = cursor; 62 1.1 tron } 63 1.1 tron return -1; 64 1.1 tron } 65 1.1 tron 66 1.1 tron static int 67 1.1 tron ainfo_insert( struct mdb_info *mdb, AttrInfo *a ) 68 1.1 tron { 69 1.1 tron int x; 70 1.1 tron int i = mdb_attr_slot( mdb, a->ai_desc, &x ); 71 1.1 tron 72 1.1 tron /* Is it a dup? */ 73 1.1 tron if ( i >= 0 ) 74 1.1 tron return -1; 75 1.1 tron 76 1.1 tron mdb->mi_attrs = ch_realloc( mdb->mi_attrs, ( mdb->mi_nattrs+1 ) * 77 1.1 tron sizeof( AttrInfo * )); 78 1.1 tron if ( x < mdb->mi_nattrs ) 79 1.1 tron AC_MEMCPY( &mdb->mi_attrs[x+1], &mdb->mi_attrs[x], 80 1.1 tron ( mdb->mi_nattrs - x ) * sizeof( AttrInfo *)); 81 1.1 tron mdb->mi_attrs[x] = a; 82 1.1 tron mdb->mi_nattrs++; 83 1.1 tron return 0; 84 1.1 tron } 85 1.1 tron 86 1.1 tron AttrInfo * 87 1.1 tron mdb_attr_mask( 88 1.1 tron struct mdb_info *mdb, 89 1.1 tron AttributeDescription *desc ) 90 1.1 tron { 91 1.1 tron int i = mdb_attr_slot( mdb, desc, NULL ); 92 1.1 tron return i < 0 ? NULL : mdb->mi_attrs[i]; 93 1.1 tron } 94 1.1 tron 95 1.1 tron /* Open all un-opened index DB handles */ 96 1.1 tron int 97 1.1 tron mdb_attr_dbs_open( 98 1.1 tron BackendDB *be, MDB_txn *tx0, ConfigReply *cr ) 99 1.1 tron { 100 1.1 tron struct mdb_info *mdb = (struct mdb_info *) be->be_private; 101 1.1 tron MDB_txn *txn; 102 1.1 tron MDB_dbi *dbis = NULL; 103 1.1 tron int i, flags; 104 1.1 tron int rc; 105 1.1 tron 106 1.4 christos if ( !mdb->mi_nattrs ) 107 1.4 christos return 0; 108 1.4 christos 109 1.1 tron txn = tx0; 110 1.1 tron if ( txn == NULL ) { 111 1.1 tron rc = mdb_txn_begin( mdb->mi_dbenv, NULL, 0, &txn ); 112 1.1 tron if ( rc ) { 113 1.1 tron snprintf( cr->msg, sizeof(cr->msg), "database \"%s\": " 114 1.1 tron "txn_begin failed: %s (%d).", 115 1.1 tron be->be_suffix[0].bv_val, mdb_strerror(rc), rc ); 116 1.1 tron Debug( LDAP_DEBUG_ANY, 117 1.1 tron LDAP_XSTRING(mdb_attr_dbs) ": %s\n", 118 1.3 christos cr->msg ); 119 1.1 tron return rc; 120 1.1 tron } 121 1.1 tron dbis = ch_calloc( 1, mdb->mi_nattrs * sizeof(MDB_dbi) ); 122 1.1 tron } else { 123 1.1 tron rc = 0; 124 1.1 tron } 125 1.1 tron 126 1.1 tron flags = MDB_DUPSORT|MDB_DUPFIXED|MDB_INTEGERDUP; 127 1.1 tron if ( !(slapMode & SLAP_TOOL_READONLY) ) 128 1.1 tron flags |= MDB_CREATE; 129 1.1 tron 130 1.1 tron for ( i=0; i<mdb->mi_nattrs; i++ ) { 131 1.1 tron if ( mdb->mi_attrs[i]->ai_dbi ) /* already open */ 132 1.1 tron continue; 133 1.3 christos if ( !( mdb->mi_attrs[i]->ai_indexmask || mdb->mi_attrs[i]->ai_newmask )) /* not an index record */ 134 1.3 christos continue; 135 1.1 tron rc = mdb_dbi_open( txn, mdb->mi_attrs[i]->ai_desc->ad_type->sat_cname.bv_val, 136 1.1 tron flags, &mdb->mi_attrs[i]->ai_dbi ); 137 1.1 tron if ( rc ) { 138 1.1 tron snprintf( cr->msg, sizeof(cr->msg), "database \"%s\": " 139 1.1 tron "mdb_dbi_open(%s) failed: %s (%d).", 140 1.1 tron be->be_suffix[0].bv_val, 141 1.1 tron mdb->mi_attrs[i]->ai_desc->ad_type->sat_cname.bv_val, 142 1.1 tron mdb_strerror(rc), rc ); 143 1.1 tron Debug( LDAP_DEBUG_ANY, 144 1.1 tron LDAP_XSTRING(mdb_attr_dbs) ": %s\n", 145 1.3 christos cr->msg ); 146 1.1 tron break; 147 1.1 tron } 148 1.1 tron /* Remember newly opened DBI handles */ 149 1.1 tron if ( dbis ) 150 1.1 tron dbis[i] = mdb->mi_attrs[i]->ai_dbi; 151 1.1 tron } 152 1.1 tron 153 1.1 tron /* Only commit if this is our txn */ 154 1.1 tron if ( tx0 == NULL ) { 155 1.1 tron if ( !rc ) { 156 1.1 tron rc = mdb_txn_commit( txn ); 157 1.1 tron if ( rc ) { 158 1.1 tron snprintf( cr->msg, sizeof(cr->msg), "database \"%s\": " 159 1.1 tron "txn_commit failed: %s (%d).", 160 1.1 tron be->be_suffix[0].bv_val, mdb_strerror(rc), rc ); 161 1.1 tron Debug( LDAP_DEBUG_ANY, 162 1.1 tron LDAP_XSTRING(mdb_attr_dbs) ": %s\n", 163 1.3 christos cr->msg ); 164 1.1 tron } 165 1.1 tron } else { 166 1.1 tron mdb_txn_abort( txn ); 167 1.1 tron } 168 1.1 tron /* Something failed, forget anything we just opened */ 169 1.1 tron if ( rc ) { 170 1.1 tron for ( i=0; i<mdb->mi_nattrs; i++ ) { 171 1.1 tron if ( dbis[i] ) { 172 1.1 tron mdb->mi_attrs[i]->ai_dbi = 0; 173 1.1 tron mdb->mi_attrs[i]->ai_indexmask |= MDB_INDEX_DELETING; 174 1.1 tron } 175 1.1 tron } 176 1.1 tron mdb_attr_flush( mdb ); 177 1.1 tron } 178 1.1 tron ch_free( dbis ); 179 1.1 tron } 180 1.1 tron 181 1.1 tron return rc; 182 1.1 tron } 183 1.1 tron 184 1.1 tron void 185 1.1 tron mdb_attr_dbs_close( 186 1.1 tron struct mdb_info *mdb 187 1.1 tron ) 188 1.1 tron { 189 1.1 tron int i; 190 1.1 tron for ( i=0; i<mdb->mi_nattrs; i++ ) 191 1.1 tron if ( mdb->mi_attrs[i]->ai_dbi ) { 192 1.1 tron mdb_dbi_close( mdb->mi_dbenv, mdb->mi_attrs[i]->ai_dbi ); 193 1.1 tron mdb->mi_attrs[i]->ai_dbi = 0; 194 1.1 tron } 195 1.1 tron } 196 1.1 tron 197 1.1 tron int 198 1.1 tron mdb_attr_index_config( 199 1.1 tron struct mdb_info *mdb, 200 1.1 tron const char *fname, 201 1.1 tron int lineno, 202 1.1 tron int argc, 203 1.1 tron char **argv, 204 1.1 tron struct config_reply_s *c_reply) 205 1.1 tron { 206 1.1 tron int rc = 0; 207 1.1 tron int i; 208 1.1 tron slap_mask_t mask; 209 1.1 tron char **attrs; 210 1.1 tron char **indexes = NULL; 211 1.1 tron 212 1.1 tron attrs = ldap_str2charray( argv[0], "," ); 213 1.1 tron 214 1.1 tron if( attrs == NULL ) { 215 1.1 tron fprintf( stderr, "%s: line %d: " 216 1.1 tron "no attributes specified: %s\n", 217 1.1 tron fname, lineno, argv[0] ); 218 1.1 tron return LDAP_PARAM_ERROR; 219 1.1 tron } 220 1.1 tron 221 1.1 tron if ( argc > 1 ) { 222 1.1 tron indexes = ldap_str2charray( argv[1], "," ); 223 1.1 tron 224 1.1 tron if( indexes == NULL ) { 225 1.1 tron fprintf( stderr, "%s: line %d: " 226 1.1 tron "no indexes specified: %s\n", 227 1.1 tron fname, lineno, argv[1] ); 228 1.1 tron rc = LDAP_PARAM_ERROR; 229 1.1 tron goto done; 230 1.1 tron } 231 1.1 tron } 232 1.1 tron 233 1.1 tron if( indexes == NULL ) { 234 1.1 tron mask = mdb->mi_defaultmask; 235 1.1 tron 236 1.1 tron } else { 237 1.1 tron mask = 0; 238 1.1 tron 239 1.1 tron for ( i = 0; indexes[i] != NULL; i++ ) { 240 1.1 tron slap_mask_t index; 241 1.1 tron rc = slap_str2index( indexes[i], &index ); 242 1.1 tron 243 1.1 tron if( rc != LDAP_SUCCESS ) { 244 1.1 tron if ( c_reply ) 245 1.1 tron { 246 1.1 tron snprintf(c_reply->msg, sizeof(c_reply->msg), 247 1.1 tron "index type \"%s\" undefined", indexes[i] ); 248 1.1 tron 249 1.1 tron fprintf( stderr, "%s: line %d: %s\n", 250 1.1 tron fname, lineno, c_reply->msg ); 251 1.1 tron } 252 1.1 tron rc = LDAP_PARAM_ERROR; 253 1.1 tron goto done; 254 1.1 tron } 255 1.1 tron 256 1.1 tron mask |= index; 257 1.1 tron } 258 1.1 tron } 259 1.1 tron 260 1.1 tron if( !mask ) { 261 1.1 tron if ( c_reply ) 262 1.1 tron { 263 1.1 tron snprintf(c_reply->msg, sizeof(c_reply->msg), 264 1.1 tron "no indexes selected" ); 265 1.1 tron fprintf( stderr, "%s: line %d: %s\n", 266 1.1 tron fname, lineno, c_reply->msg ); 267 1.1 tron } 268 1.1 tron rc = LDAP_PARAM_ERROR; 269 1.1 tron goto done; 270 1.1 tron } 271 1.1 tron 272 1.1 tron for ( i = 0; attrs[i] != NULL; i++ ) { 273 1.1 tron AttrInfo *a; 274 1.1 tron AttributeDescription *ad; 275 1.1 tron const char *text; 276 1.1 tron #ifdef LDAP_COMP_MATCH 277 1.1 tron ComponentReference* cr = NULL; 278 1.1 tron AttrInfo *a_cr = NULL; 279 1.1 tron #endif 280 1.1 tron 281 1.1 tron if( strcasecmp( attrs[i], "default" ) == 0 ) { 282 1.1 tron mdb->mi_defaultmask |= mask; 283 1.1 tron continue; 284 1.1 tron } 285 1.1 tron 286 1.1 tron #ifdef LDAP_COMP_MATCH 287 1.1 tron if ( is_component_reference( attrs[i] ) ) { 288 1.1 tron rc = extract_component_reference( attrs[i], &cr ); 289 1.1 tron if ( rc != LDAP_SUCCESS ) { 290 1.1 tron if ( c_reply ) 291 1.1 tron { 292 1.1 tron snprintf(c_reply->msg, sizeof(c_reply->msg), 293 1.1 tron "index component reference\"%s\" undefined", 294 1.1 tron attrs[i] ); 295 1.1 tron fprintf( stderr, "%s: line %d: %s\n", 296 1.1 tron fname, lineno, c_reply->msg ); 297 1.1 tron } 298 1.1 tron goto done; 299 1.1 tron } 300 1.1 tron cr->cr_indexmask = mask; 301 1.1 tron /* 302 1.1 tron * After extracting a component reference 303 1.1 tron * only the name of a attribute will be remaining 304 1.1 tron */ 305 1.1 tron } else { 306 1.1 tron cr = NULL; 307 1.1 tron } 308 1.1 tron #endif 309 1.1 tron ad = NULL; 310 1.1 tron rc = slap_str2ad( attrs[i], &ad, &text ); 311 1.1 tron 312 1.1 tron if( rc != LDAP_SUCCESS ) { 313 1.1 tron if ( c_reply ) 314 1.1 tron { 315 1.1 tron snprintf(c_reply->msg, sizeof(c_reply->msg), 316 1.1 tron "index attribute \"%s\" undefined", 317 1.1 tron attrs[i] ); 318 1.1 tron 319 1.1 tron fprintf( stderr, "%s: line %d: %s\n", 320 1.1 tron fname, lineno, c_reply->msg ); 321 1.1 tron } 322 1.3 christos fail: 323 1.3 christos #ifdef LDAP_COMP_MATCH 324 1.3 christos ch_free( cr ); 325 1.3 christos #endif 326 1.1 tron goto done; 327 1.1 tron } 328 1.1 tron 329 1.1 tron if( ad == slap_schema.si_ad_entryDN || slap_ad_is_binary( ad ) ) { 330 1.1 tron if (c_reply) { 331 1.1 tron snprintf(c_reply->msg, sizeof(c_reply->msg), 332 1.1 tron "index of attribute \"%s\" disallowed", attrs[i] ); 333 1.1 tron fprintf( stderr, "%s: line %d: %s\n", 334 1.1 tron fname, lineno, c_reply->msg ); 335 1.1 tron } 336 1.1 tron rc = LDAP_UNWILLING_TO_PERFORM; 337 1.3 christos goto fail; 338 1.1 tron } 339 1.1 tron 340 1.1 tron if( IS_SLAP_INDEX( mask, SLAP_INDEX_APPROX ) && !( 341 1.1 tron ad->ad_type->sat_approx 342 1.1 tron && ad->ad_type->sat_approx->smr_indexer 343 1.1 tron && ad->ad_type->sat_approx->smr_filter ) ) 344 1.1 tron { 345 1.1 tron if (c_reply) { 346 1.1 tron snprintf(c_reply->msg, sizeof(c_reply->msg), 347 1.1 tron "approx index of attribute \"%s\" disallowed", attrs[i] ); 348 1.1 tron fprintf( stderr, "%s: line %d: %s\n", 349 1.1 tron fname, lineno, c_reply->msg ); 350 1.1 tron } 351 1.1 tron rc = LDAP_INAPPROPRIATE_MATCHING; 352 1.3 christos goto fail; 353 1.1 tron } 354 1.1 tron 355 1.1 tron if( IS_SLAP_INDEX( mask, SLAP_INDEX_EQUALITY ) && !( 356 1.1 tron ad->ad_type->sat_equality 357 1.1 tron && ad->ad_type->sat_equality->smr_indexer 358 1.1 tron && ad->ad_type->sat_equality->smr_filter ) ) 359 1.1 tron { 360 1.1 tron if (c_reply) { 361 1.1 tron snprintf(c_reply->msg, sizeof(c_reply->msg), 362 1.1 tron "equality index of attribute \"%s\" disallowed", attrs[i] ); 363 1.1 tron fprintf( stderr, "%s: line %d: %s\n", 364 1.1 tron fname, lineno, c_reply->msg ); 365 1.1 tron } 366 1.1 tron rc = LDAP_INAPPROPRIATE_MATCHING; 367 1.3 christos goto fail; 368 1.1 tron } 369 1.1 tron 370 1.1 tron if( IS_SLAP_INDEX( mask, SLAP_INDEX_SUBSTR ) && !( 371 1.1 tron ad->ad_type->sat_substr 372 1.1 tron && ad->ad_type->sat_substr->smr_indexer 373 1.1 tron && ad->ad_type->sat_substr->smr_filter ) ) 374 1.1 tron { 375 1.1 tron if (c_reply) { 376 1.1 tron snprintf(c_reply->msg, sizeof(c_reply->msg), 377 1.1 tron "substr index of attribute \"%s\" disallowed", attrs[i] ); 378 1.1 tron fprintf( stderr, "%s: line %d: %s\n", 379 1.1 tron fname, lineno, c_reply->msg ); 380 1.1 tron } 381 1.1 tron rc = LDAP_INAPPROPRIATE_MATCHING; 382 1.3 christos goto fail; 383 1.1 tron } 384 1.1 tron 385 1.1 tron Debug( LDAP_DEBUG_CONFIG, "index %s 0x%04lx\n", 386 1.3 christos ad->ad_cname.bv_val, mask ); 387 1.1 tron 388 1.1 tron a = (AttrInfo *) ch_malloc( sizeof(AttrInfo) ); 389 1.1 tron 390 1.1 tron #ifdef LDAP_COMP_MATCH 391 1.1 tron a->ai_cr = NULL; 392 1.1 tron #endif 393 1.1 tron a->ai_cursor = NULL; 394 1.1 tron a->ai_root = NULL; 395 1.1 tron a->ai_desc = ad; 396 1.1 tron a->ai_dbi = 0; 397 1.3 christos a->ai_multi_hi = UINT_MAX; 398 1.3 christos a->ai_multi_lo = UINT_MAX; 399 1.1 tron 400 1.1 tron if ( mdb->mi_flags & MDB_IS_OPEN ) { 401 1.1 tron a->ai_indexmask = 0; 402 1.1 tron a->ai_newmask = mask; 403 1.1 tron } else { 404 1.1 tron a->ai_indexmask = mask; 405 1.1 tron a->ai_newmask = 0; 406 1.1 tron } 407 1.1 tron 408 1.1 tron #ifdef LDAP_COMP_MATCH 409 1.1 tron if ( cr ) { 410 1.1 tron a_cr = mdb_attr_mask( mdb, ad ); 411 1.1 tron if ( a_cr ) { 412 1.1 tron /* 413 1.1 tron * AttrInfo is already in AVL 414 1.1 tron * just add the extracted component reference 415 1.1 tron * in the AttrInfo 416 1.1 tron */ 417 1.3 christos ch_free( a ); 418 1.1 tron rc = insert_component_reference( cr, &a_cr->ai_cr ); 419 1.1 tron if ( rc != LDAP_SUCCESS) { 420 1.1 tron fprintf( stderr, " error during inserting component reference in %s ", attrs[i]); 421 1.1 tron rc = LDAP_PARAM_ERROR; 422 1.3 christos goto fail; 423 1.1 tron } 424 1.1 tron continue; 425 1.1 tron } else { 426 1.1 tron rc = insert_component_reference( cr, &a->ai_cr ); 427 1.1 tron if ( rc != LDAP_SUCCESS) { 428 1.1 tron fprintf( stderr, " error during inserting component reference in %s ", attrs[i]); 429 1.1 tron rc = LDAP_PARAM_ERROR; 430 1.3 christos ch_free( a ); 431 1.3 christos goto fail; 432 1.1 tron } 433 1.1 tron } 434 1.1 tron } 435 1.1 tron #endif 436 1.1 tron rc = ainfo_insert( mdb, a ); 437 1.1 tron if( rc ) { 438 1.3 christos AttrInfo *b = mdb_attr_mask( mdb, ad ); 439 1.3 christos /* If this is just a multival record, reuse it for index info */ 440 1.3 christos if ( !( b->ai_indexmask || b->ai_newmask ) && b->ai_multi_lo < UINT_MAX ) { 441 1.3 christos b->ai_indexmask = a->ai_indexmask; 442 1.3 christos b->ai_newmask = a->ai_newmask; 443 1.3 christos ch_free( a ); 444 1.3 christos rc = 0; 445 1.3 christos continue; 446 1.3 christos } 447 1.1 tron if ( mdb->mi_flags & MDB_IS_OPEN ) { 448 1.1 tron /* If there is already an index defined for this attribute 449 1.1 tron * it must be replaced. Otherwise we end up with multiple 450 1.1 tron * olcIndex values for the same attribute */ 451 1.1 tron if ( b->ai_indexmask & MDB_INDEX_DELETING ) { 452 1.1 tron /* If we were editing this attr, reset it */ 453 1.1 tron b->ai_indexmask &= ~MDB_INDEX_DELETING; 454 1.1 tron /* If this is leftover from a previous add, commit it */ 455 1.1 tron if ( b->ai_newmask ) 456 1.1 tron b->ai_indexmask = b->ai_newmask; 457 1.4 christos /* If the mask changed, remember it */ 458 1.4 christos if ( b->ai_indexmask != a->ai_newmask ) 459 1.4 christos b->ai_newmask = a->ai_newmask; 460 1.4 christos else /* else ignore it */ 461 1.4 christos b->ai_newmask = 0; 462 1.1 tron ch_free( a ); 463 1.1 tron rc = 0; 464 1.1 tron continue; 465 1.1 tron } 466 1.1 tron } 467 1.1 tron if (c_reply) { 468 1.1 tron snprintf(c_reply->msg, sizeof(c_reply->msg), 469 1.1 tron "duplicate index definition for attr \"%s\"", 470 1.1 tron attrs[i] ); 471 1.1 tron fprintf( stderr, "%s: line %d: %s\n", 472 1.1 tron fname, lineno, c_reply->msg ); 473 1.1 tron } 474 1.1 tron 475 1.1 tron rc = LDAP_PARAM_ERROR; 476 1.1 tron goto done; 477 1.1 tron } 478 1.1 tron } 479 1.1 tron 480 1.1 tron done: 481 1.1 tron ldap_charray_free( attrs ); 482 1.1 tron if ( indexes != NULL ) ldap_charray_free( indexes ); 483 1.1 tron 484 1.1 tron return rc; 485 1.1 tron } 486 1.1 tron 487 1.1 tron static int 488 1.1 tron mdb_attr_index_unparser( void *v1, void *v2 ) 489 1.1 tron { 490 1.1 tron AttrInfo *ai = v1; 491 1.1 tron BerVarray *bva = v2; 492 1.1 tron struct berval bv; 493 1.1 tron char *ptr; 494 1.1 tron 495 1.1 tron slap_index2bvlen( ai->ai_indexmask, &bv ); 496 1.1 tron if ( bv.bv_len ) { 497 1.1 tron bv.bv_len += ai->ai_desc->ad_cname.bv_len + 1; 498 1.1 tron ptr = ch_malloc( bv.bv_len+1 ); 499 1.1 tron bv.bv_val = lutil_strcopy( ptr, ai->ai_desc->ad_cname.bv_val ); 500 1.1 tron *bv.bv_val++ = ' '; 501 1.1 tron slap_index2bv( ai->ai_indexmask, &bv ); 502 1.1 tron bv.bv_val = ptr; 503 1.1 tron ber_bvarray_add( bva, &bv ); 504 1.1 tron } 505 1.1 tron return 0; 506 1.1 tron } 507 1.1 tron 508 1.1 tron static AttributeDescription addef = { NULL, NULL, BER_BVC("default") }; 509 1.1 tron static AttrInfo aidef = { &addef }; 510 1.1 tron 511 1.1 tron void 512 1.1 tron mdb_attr_index_unparse( struct mdb_info *mdb, BerVarray *bva ) 513 1.1 tron { 514 1.1 tron int i; 515 1.1 tron 516 1.1 tron if ( mdb->mi_defaultmask ) { 517 1.1 tron aidef.ai_indexmask = mdb->mi_defaultmask; 518 1.1 tron mdb_attr_index_unparser( &aidef, bva ); 519 1.1 tron } 520 1.1 tron for ( i=0; i<mdb->mi_nattrs; i++ ) 521 1.3 christos if ( mdb->mi_attrs[i]->ai_indexmask ) 522 1.3 christos mdb_attr_index_unparser( mdb->mi_attrs[i], bva ); 523 1.3 christos } 524 1.3 christos 525 1.3 christos int 526 1.3 christos mdb_attr_multi_config( 527 1.3 christos struct mdb_info *mdb, 528 1.3 christos const char *fname, 529 1.3 christos int lineno, 530 1.3 christos int argc, 531 1.3 christos char **argv, 532 1.3 christos struct config_reply_s *c_reply) 533 1.3 christos { 534 1.3 christos int rc = 0; 535 1.3 christos int i; 536 1.3 christos unsigned hi,lo; 537 1.3 christos char **attrs, *next, *s; 538 1.3 christos 539 1.3 christos attrs = ldap_str2charray( argv[0], "," ); 540 1.3 christos 541 1.3 christos if( attrs == NULL ) { 542 1.3 christos fprintf( stderr, "%s: line %d: " 543 1.3 christos "no attributes specified: %s\n", 544 1.3 christos fname, lineno, argv[0] ); 545 1.3 christos return LDAP_PARAM_ERROR; 546 1.3 christos } 547 1.3 christos 548 1.3 christos hi = strtoul( argv[1], &next, 10 ); 549 1.3 christos if ( next == argv[1] || next[0] != ',' ) 550 1.3 christos goto badval; 551 1.3 christos s = next+1; 552 1.3 christos lo = strtoul( s, &next, 10 ); 553 1.3 christos if ( next == s || next[0] != '\0' ) 554 1.3 christos goto badval; 555 1.3 christos 556 1.3 christos if ( lo > hi ) { 557 1.3 christos badval: 558 1.3 christos snprintf(c_reply->msg, sizeof(c_reply->msg), 559 1.3 christos "invalid hi/lo thresholds" ); 560 1.3 christos fprintf( stderr, "%s: line %d: %s\n", 561 1.3 christos fname, lineno, c_reply->msg ); 562 1.3 christos return LDAP_PARAM_ERROR; 563 1.3 christos } 564 1.3 christos 565 1.3 christos for ( i = 0; attrs[i] != NULL; i++ ) { 566 1.3 christos AttrInfo *a; 567 1.3 christos AttributeDescription *ad; 568 1.3 christos const char *text; 569 1.3 christos 570 1.3 christos if( strcasecmp( attrs[i], "default" ) == 0 ) { 571 1.3 christos mdb->mi_multi_hi = hi; 572 1.3 christos mdb->mi_multi_lo = lo; 573 1.3 christos continue; 574 1.3 christos } 575 1.3 christos 576 1.3 christos ad = NULL; 577 1.3 christos rc = slap_str2ad( attrs[i], &ad, &text ); 578 1.3 christos 579 1.3 christos if( rc != LDAP_SUCCESS ) { 580 1.3 christos if ( c_reply ) 581 1.3 christos { 582 1.3 christos snprintf(c_reply->msg, sizeof(c_reply->msg), 583 1.3 christos "multival attribute \"%s\" undefined", 584 1.3 christos attrs[i] ); 585 1.3 christos 586 1.3 christos fprintf( stderr, "%s: line %d: %s\n", 587 1.3 christos fname, lineno, c_reply->msg ); 588 1.3 christos } 589 1.3 christos fail: 590 1.3 christos goto done; 591 1.3 christos } 592 1.3 christos 593 1.3 christos a = (AttrInfo *) ch_calloc( 1, sizeof(AttrInfo) ); 594 1.3 christos 595 1.3 christos a->ai_desc = ad; 596 1.3 christos a->ai_multi_hi = hi; 597 1.3 christos a->ai_multi_lo = lo; 598 1.3 christos 599 1.3 christos rc = ainfo_insert( mdb, a ); 600 1.3 christos if( rc ) { 601 1.3 christos AttrInfo *b = mdb_attr_mask( mdb, ad ); 602 1.3 christos /* If this is just an index record, reuse it for multival info */ 603 1.3 christos if ( b->ai_multi_lo == UINT_MAX ) { 604 1.3 christos b->ai_multi_hi = a->ai_multi_hi; 605 1.3 christos b->ai_multi_lo = a->ai_multi_lo; 606 1.3 christos ch_free( a ); 607 1.3 christos rc = 0; 608 1.3 christos continue; 609 1.3 christos } 610 1.3 christos if (c_reply) { 611 1.3 christos snprintf(c_reply->msg, sizeof(c_reply->msg), 612 1.3 christos "duplicate multival definition for attr \"%s\"", 613 1.3 christos attrs[i] ); 614 1.3 christos fprintf( stderr, "%s: line %d: %s\n", 615 1.3 christos fname, lineno, c_reply->msg ); 616 1.3 christos } 617 1.3 christos 618 1.3 christos rc = LDAP_PARAM_ERROR; 619 1.3 christos goto done; 620 1.3 christos } 621 1.3 christos } 622 1.3 christos 623 1.3 christos done: 624 1.3 christos ldap_charray_free( attrs ); 625 1.3 christos 626 1.3 christos return rc; 627 1.3 christos } 628 1.3 christos 629 1.3 christos static int 630 1.3 christos mdb_attr_multi_unparser( void *v1, void *v2 ) 631 1.3 christos { 632 1.3 christos AttrInfo *ai = v1; 633 1.3 christos BerVarray *bva = v2; 634 1.3 christos struct berval bv; 635 1.3 christos char digbuf[sizeof("4294967296,4294967296")]; 636 1.3 christos char *ptr; 637 1.3 christos 638 1.3 christos bv.bv_len = snprintf( digbuf, sizeof(digbuf), "%u,%u", 639 1.3 christos ai->ai_multi_hi, ai->ai_multi_lo ); 640 1.3 christos if ( bv.bv_len ) { 641 1.3 christos bv.bv_len += ai->ai_desc->ad_cname.bv_len + 1; 642 1.3 christos ptr = ch_malloc( bv.bv_len+1 ); 643 1.3 christos bv.bv_val = lutil_strcopy( ptr, ai->ai_desc->ad_cname.bv_val ); 644 1.3 christos *bv.bv_val++ = ' '; 645 1.3 christos strcpy(bv.bv_val, digbuf); 646 1.3 christos bv.bv_val = ptr; 647 1.3 christos ber_bvarray_add( bva, &bv ); 648 1.3 christos } 649 1.3 christos return 0; 650 1.3 christos } 651 1.3 christos 652 1.3 christos void 653 1.3 christos mdb_attr_multi_unparse( struct mdb_info *mdb, BerVarray *bva ) 654 1.3 christos { 655 1.3 christos int i; 656 1.3 christos 657 1.3 christos if ( mdb->mi_multi_hi < UINT_MAX ) { 658 1.3 christos aidef.ai_multi_hi = mdb->mi_multi_hi; 659 1.3 christos aidef.ai_multi_lo = mdb->mi_multi_lo; 660 1.3 christos mdb_attr_multi_unparser( &aidef, bva ); 661 1.3 christos } 662 1.3 christos for ( i=0; i<mdb->mi_nattrs; i++ ) 663 1.3 christos if ( mdb->mi_attrs[i]->ai_multi_hi < UINT_MAX ) 664 1.3 christos mdb_attr_multi_unparser( mdb->mi_attrs[i], bva ); 665 1.3 christos } 666 1.3 christos 667 1.3 christos void 668 1.3 christos mdb_attr_multi_thresh( struct mdb_info *mdb, AttributeDescription *ad, unsigned *hi, unsigned *lo ) 669 1.3 christos { 670 1.3 christos AttrInfo *ai = mdb_attr_mask( mdb, ad ); 671 1.3 christos if ( ai && ai->ai_multi_hi < UINT_MAX ) 672 1.3 christos { 673 1.3 christos if ( hi ) 674 1.3 christos *hi = ai->ai_multi_hi; 675 1.3 christos if ( lo ) 676 1.3 christos *lo = ai->ai_multi_lo; 677 1.3 christos } else 678 1.3 christos { 679 1.3 christos if ( hi ) 680 1.3 christos *hi = mdb->mi_multi_hi; 681 1.3 christos if ( lo ) 682 1.3 christos *lo = mdb->mi_multi_lo; 683 1.3 christos } 684 1.1 tron } 685 1.1 tron 686 1.1 tron void 687 1.1 tron mdb_attr_info_free( AttrInfo *ai ) 688 1.1 tron { 689 1.1 tron #ifdef LDAP_COMP_MATCH 690 1.1 tron free( ai->ai_cr ); 691 1.1 tron #endif 692 1.1 tron free( ai ); 693 1.1 tron } 694 1.1 tron 695 1.1 tron void 696 1.1 tron mdb_attr_index_destroy( struct mdb_info *mdb ) 697 1.1 tron { 698 1.1 tron int i; 699 1.1 tron 700 1.1 tron for ( i=0; i<mdb->mi_nattrs; i++ ) 701 1.1 tron mdb_attr_info_free( mdb->mi_attrs[i] ); 702 1.1 tron 703 1.1 tron free( mdb->mi_attrs ); 704 1.1 tron } 705 1.1 tron 706 1.1 tron void mdb_attr_index_free( struct mdb_info *mdb, AttributeDescription *ad ) 707 1.1 tron { 708 1.1 tron int i; 709 1.1 tron 710 1.1 tron i = mdb_attr_slot( mdb, ad, NULL ); 711 1.1 tron if ( i >= 0 ) { 712 1.1 tron mdb_attr_info_free( mdb->mi_attrs[i] ); 713 1.1 tron mdb->mi_nattrs--; 714 1.1 tron for (; i<mdb->mi_nattrs; i++) 715 1.1 tron mdb->mi_attrs[i] = mdb->mi_attrs[i+1]; 716 1.1 tron } 717 1.1 tron } 718 1.1 tron 719 1.1 tron void mdb_attr_flush( struct mdb_info *mdb ) 720 1.1 tron { 721 1.1 tron int i; 722 1.1 tron 723 1.1 tron for ( i=0; i<mdb->mi_nattrs; i++ ) { 724 1.1 tron if ( mdb->mi_attrs[i]->ai_indexmask & MDB_INDEX_DELETING ) { 725 1.3 christos /* if this is also a multival rec, just clear index */ 726 1.3 christos if ( mdb->mi_attrs[i]->ai_multi_lo < UINT_MAX ) { 727 1.3 christos mdb->mi_attrs[i]->ai_indexmask = 0; 728 1.3 christos mdb->mi_attrs[i]->ai_newmask = 0; 729 1.3 christos } else { 730 1.3 christos int j; 731 1.3 christos mdb_attr_info_free( mdb->mi_attrs[i] ); 732 1.3 christos mdb->mi_nattrs--; 733 1.3 christos for (j=i; j<mdb->mi_nattrs; j++) 734 1.3 christos mdb->mi_attrs[j] = mdb->mi_attrs[j+1]; 735 1.3 christos i--; 736 1.3 christos } 737 1.1 tron } 738 1.1 tron } 739 1.1 tron } 740 1.1 tron 741 1.1 tron int mdb_ad_read( struct mdb_info *mdb, MDB_txn *txn ) 742 1.1 tron { 743 1.1 tron int i, rc; 744 1.1 tron MDB_cursor *mc; 745 1.1 tron MDB_val key, data; 746 1.1 tron struct berval bdata; 747 1.1 tron const char *text; 748 1.1 tron AttributeDescription *ad; 749 1.1 tron 750 1.1 tron rc = mdb_cursor_open( txn, mdb->mi_ad2id, &mc ); 751 1.1 tron if ( rc ) { 752 1.1 tron Debug( LDAP_DEBUG_ANY, 753 1.1 tron "mdb_ad_read: cursor_open failed %s(%d)\n", 754 1.3 christos mdb_strerror(rc), rc ); 755 1.1 tron return rc; 756 1.1 tron } 757 1.1 tron 758 1.1 tron /* our array is 1-based, an index of 0 means no data */ 759 1.1 tron i = mdb->mi_numads+1; 760 1.1 tron key.mv_size = sizeof(int); 761 1.1 tron key.mv_data = &i; 762 1.1 tron 763 1.1 tron rc = mdb_cursor_get( mc, &key, &data, MDB_SET ); 764 1.1 tron 765 1.1 tron while ( rc == MDB_SUCCESS ) { 766 1.1 tron bdata.bv_len = data.mv_size; 767 1.1 tron bdata.bv_val = data.mv_data; 768 1.1 tron ad = NULL; 769 1.1 tron rc = slap_bv2ad( &bdata, &ad, &text ); 770 1.1 tron if ( rc ) { 771 1.1 tron rc = slap_bv2undef_ad( &bdata, &mdb->mi_ads[i], &text, 0 ); 772 1.1 tron } else { 773 1.1 tron if ( ad->ad_index >= MDB_MAXADS ) { 774 1.1 tron Debug( LDAP_DEBUG_ANY, 775 1.3 christos "mdb_adb_read: too many AttributeDescriptions in use\n" ); 776 1.1 tron return LDAP_OTHER; 777 1.1 tron } 778 1.1 tron mdb->mi_adxs[ad->ad_index] = i; 779 1.1 tron mdb->mi_ads[i] = ad; 780 1.1 tron } 781 1.1 tron i++; 782 1.1 tron rc = mdb_cursor_get( mc, &key, &data, MDB_NEXT ); 783 1.1 tron } 784 1.1 tron mdb->mi_numads = i-1; 785 1.1 tron 786 1.1 tron done: 787 1.1 tron if ( rc == MDB_NOTFOUND ) 788 1.1 tron rc = 0; 789 1.1 tron 790 1.1 tron mdb_cursor_close( mc ); 791 1.1 tron 792 1.1 tron return rc; 793 1.1 tron } 794 1.1 tron 795 1.1 tron int mdb_ad_get( struct mdb_info *mdb, MDB_txn *txn, AttributeDescription *ad ) 796 1.1 tron { 797 1.1 tron int i, rc; 798 1.1 tron MDB_val key, val; 799 1.1 tron 800 1.1 tron rc = mdb_ad_read( mdb, txn ); 801 1.1 tron if (rc) 802 1.1 tron return rc; 803 1.1 tron 804 1.1 tron if ( mdb->mi_adxs[ad->ad_index] ) 805 1.1 tron return 0; 806 1.1 tron 807 1.1 tron i = mdb->mi_numads+1; 808 1.1 tron key.mv_size = sizeof(int); 809 1.1 tron key.mv_data = &i; 810 1.1 tron val.mv_size = ad->ad_cname.bv_len; 811 1.1 tron val.mv_data = ad->ad_cname.bv_val; 812 1.1 tron 813 1.1 tron rc = mdb_put( txn, mdb->mi_ad2id, &key, &val, 0 ); 814 1.1 tron if ( rc == MDB_SUCCESS ) { 815 1.1 tron mdb->mi_adxs[ad->ad_index] = i; 816 1.1 tron mdb->mi_ads[i] = ad; 817 1.2 christos mdb->mi_numads = i; 818 1.1 tron } else { 819 1.1 tron Debug( LDAP_DEBUG_ANY, 820 1.1 tron "mdb_ad_get: mdb_put failed %s(%d)\n", 821 1.3 christos mdb_strerror(rc), rc ); 822 1.1 tron } 823 1.1 tron 824 1.1 tron return rc; 825 1.1 tron } 826 1.2 christos 827 1.2 christos void mdb_ad_unwind( struct mdb_info *mdb, int prev_ads ) 828 1.2 christos { 829 1.2 christos int i; 830 1.2 christos 831 1.2 christos for (i=mdb->mi_numads; i>prev_ads; i--) { 832 1.2 christos mdb->mi_adxs[mdb->mi_ads[i]->ad_index] = 0; 833 1.2 christos mdb->mi_ads[i] = NULL; 834 1.2 christos } 835 1.2 christos mdb->mi_numads = i; 836 1.2 christos } 837