1 1.3 christos /* $NetBSD: cache.c,v 1.4 2025/09/05 21:16:28 christos Exp $ */ 2 1.2 christos 3 1.1 lukem /* cache.c - routines to maintain an in-core cache of entries */ 4 1.2 christos /* $OpenLDAP$ */ 5 1.1 lukem /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 6 1.1 lukem * 7 1.4 christos * Copyright 2001-2024 The OpenLDAP Foundation. 8 1.1 lukem * Portions Copyright 2001-2003 Pierangelo Masarati. 9 1.1 lukem * All rights reserved. 10 1.1 lukem * 11 1.1 lukem * Redistribution and use in source and binary forms, with or without 12 1.1 lukem * modification, are permitted only as authorized by the OpenLDAP 13 1.1 lukem * Public License. 14 1.1 lukem * 15 1.1 lukem * A copy of this license is available in file LICENSE in the 16 1.1 lukem * top-level directory of the distribution or, alternatively, at 17 1.1 lukem * <http://www.OpenLDAP.org/license.html>. 18 1.1 lukem */ 19 1.1 lukem /* ACKNOWLEDGEMENTS: 20 1.1 lukem * This work was initially developed by Pierangelo Masarati for inclusion 21 1.1 lukem * in OpenLDAP Software. 22 1.1 lukem */ 23 1.1 lukem 24 1.2 christos #include <sys/cdefs.h> 25 1.3 christos __RCSID("$NetBSD: cache.c,v 1.4 2025/09/05 21:16:28 christos Exp $"); 26 1.2 christos 27 1.1 lukem #include "portable.h" 28 1.1 lukem 29 1.1 lukem #include <stdio.h> 30 1.1 lukem #include "ac/string.h" 31 1.1 lukem 32 1.1 lukem #include "slap.h" 33 1.1 lukem 34 1.1 lukem #include "back-monitor.h" 35 1.1 lukem 36 1.1 lukem /* 37 1.1 lukem * The cache maps DNs to Entries. 38 1.1 lukem * Each entry, on turn, holds the list of its children in the e_private field. 39 1.1 lukem * This is used by search operation to perform onelevel and subtree candidate 40 1.1 lukem * selection. 41 1.1 lukem */ 42 1.1 lukem typedef struct monitor_cache_t { 43 1.1 lukem struct berval mc_ndn; 44 1.1 lukem Entry *mc_e; 45 1.1 lukem } monitor_cache_t; 46 1.1 lukem 47 1.1 lukem /* 48 1.1 lukem * compares entries based on the dn 49 1.1 lukem */ 50 1.1 lukem int 51 1.1 lukem monitor_cache_cmp( 52 1.1 lukem const void *c1, 53 1.1 lukem const void *c2 ) 54 1.1 lukem { 55 1.1 lukem monitor_cache_t *cc1 = ( monitor_cache_t * )c1; 56 1.1 lukem monitor_cache_t *cc2 = ( monitor_cache_t * )c2; 57 1.1 lukem 58 1.1 lukem /* 59 1.1 lukem * case sensitive, because the dn MUST be normalized 60 1.1 lukem */ 61 1.1 lukem return ber_bvcmp( &cc1->mc_ndn, &cc2->mc_ndn ); 62 1.1 lukem } 63 1.1 lukem 64 1.1 lukem /* 65 1.1 lukem * checks for duplicate entries 66 1.1 lukem */ 67 1.1 lukem int 68 1.1 lukem monitor_cache_dup( 69 1.1 lukem void *c1, 70 1.1 lukem void *c2 ) 71 1.1 lukem { 72 1.1 lukem monitor_cache_t *cc1 = ( monitor_cache_t * )c1; 73 1.1 lukem monitor_cache_t *cc2 = ( monitor_cache_t * )c2; 74 1.1 lukem 75 1.1 lukem /* 76 1.1 lukem * case sensitive, because the dn MUST be normalized 77 1.1 lukem */ 78 1.1 lukem return ber_bvcmp( &cc1->mc_ndn, &cc2->mc_ndn ) == 0 ? -1 : 0; 79 1.1 lukem } 80 1.1 lukem 81 1.1 lukem /* 82 1.1 lukem * adds an entry to the cache and inits the mutex 83 1.1 lukem */ 84 1.1 lukem int 85 1.1 lukem monitor_cache_add( 86 1.1 lukem monitor_info_t *mi, 87 1.4 christos Entry *e, 88 1.4 christos Entry *parent ) 89 1.1 lukem { 90 1.4 christos monitor_cache_t tmp_mc, *mc, *pmc = NULL; 91 1.4 christos Entry **ep = NULL, *prev = NULL; 92 1.4 christos int rc = -1; 93 1.1 lukem 94 1.1 lukem assert( mi != NULL ); 95 1.1 lukem assert( e != NULL ); 96 1.1 lukem 97 1.4 christos dnParent( &e->e_nname, &tmp_mc.mc_ndn ); 98 1.4 christos 99 1.1 lukem mc = ( monitor_cache_t * )ch_malloc( sizeof( monitor_cache_t ) ); 100 1.1 lukem mc->mc_ndn = e->e_nname; 101 1.1 lukem mc->mc_e = e; 102 1.4 christos 103 1.4 christos if ( parent ) { 104 1.4 christos /* Shortcut, but follow lock order as a fallback */ 105 1.4 christos if ( ldap_pvt_thread_mutex_trylock( &mi->mi_cache_lock ) ) { 106 1.4 christos monitor_cache_release( mi, parent ); 107 1.4 christos ldap_pvt_thread_mutex_lock( &mi->mi_cache_lock ); 108 1.4 christos monitor_cache_lock( parent ); 109 1.4 christos } 110 1.4 christos } else { 111 1.4 christos ldap_pvt_thread_mutex_lock( &mi->mi_cache_lock ); 112 1.4 christos } 113 1.4 christos 114 1.4 christos /* Allow database root be added */ 115 1.4 christos if ( parent == NULL && mi->mi_cache != NULL ) { 116 1.4 christos pmc = ldap_avl_find( mi->mi_cache, &tmp_mc, monitor_cache_cmp ); 117 1.4 christos if ( pmc == NULL ) { 118 1.4 christos goto done; 119 1.4 christos } 120 1.4 christos parent = pmc->mc_e; 121 1.4 christos monitor_cache_lock( parent ); 122 1.4 christos } 123 1.4 christos 124 1.4 christos rc = ldap_avl_insert( &mi->mi_cache, mc, 125 1.1 lukem monitor_cache_cmp, monitor_cache_dup ); 126 1.4 christos if ( rc != LDAP_SUCCESS ) { 127 1.4 christos goto done; 128 1.4 christos } 129 1.4 christos 130 1.4 christos if ( parent != NULL ) { 131 1.4 christos monitor_entry_t *mp = parent->e_private; 132 1.4 christos 133 1.4 christos if ( mp->mp_children ) { 134 1.4 christos monitor_entry_t *tail; 135 1.4 christos 136 1.4 christos monitor_cache_lock( mp->mp_last ); 137 1.4 christos tail = mp->mp_last->e_private; 138 1.4 christos tail->mp_next = e; 139 1.4 christos monitor_cache_release( mi, mp->mp_last ); 140 1.4 christos mp->mp_last = e; 141 1.4 christos } else { 142 1.4 christos mp->mp_children = mp->mp_last = e; 143 1.4 christos } 144 1.4 christos } 145 1.1 lukem 146 1.4 christos done: 147 1.4 christos if ( pmc != NULL ) { 148 1.4 christos monitor_cache_release( mi, parent ); 149 1.4 christos } 150 1.4 christos ldap_pvt_thread_mutex_unlock( &mi->mi_cache_lock ); 151 1.4 christos 152 1.4 christos if ( rc != LDAP_SUCCESS ) { 153 1.4 christos ch_free( mc ); 154 1.4 christos } 155 1.1 lukem return rc; 156 1.1 lukem } 157 1.1 lukem 158 1.1 lukem /* 159 1.1 lukem * locks the entry (no r/w) 160 1.1 lukem */ 161 1.1 lukem int 162 1.1 lukem monitor_cache_lock( 163 1.1 lukem Entry *e ) 164 1.1 lukem { 165 1.1 lukem monitor_entry_t *mp; 166 1.1 lukem 167 1.1 lukem assert( e != NULL ); 168 1.1 lukem assert( e->e_private != NULL ); 169 1.1 lukem 170 1.1 lukem mp = ( monitor_entry_t * )e->e_private; 171 1.1 lukem ldap_pvt_thread_mutex_lock( &mp->mp_mutex ); 172 1.1 lukem 173 1.1 lukem return( 0 ); 174 1.1 lukem } 175 1.1 lukem 176 1.1 lukem /* 177 1.1 lukem * tries to lock the entry (no r/w) 178 1.1 lukem */ 179 1.1 lukem int 180 1.1 lukem monitor_cache_trylock( 181 1.1 lukem Entry *e ) 182 1.1 lukem { 183 1.1 lukem monitor_entry_t *mp; 184 1.1 lukem 185 1.1 lukem assert( e != NULL ); 186 1.1 lukem assert( e->e_private != NULL ); 187 1.1 lukem 188 1.1 lukem mp = ( monitor_entry_t * )e->e_private; 189 1.1 lukem return ldap_pvt_thread_mutex_trylock( &mp->mp_mutex ); 190 1.1 lukem } 191 1.1 lukem 192 1.1 lukem /* 193 1.1 lukem * gets an entry from the cache based on the normalized dn 194 1.1 lukem * with mutex locked 195 1.1 lukem */ 196 1.1 lukem int 197 1.1 lukem monitor_cache_get( 198 1.1 lukem monitor_info_t *mi, 199 1.1 lukem struct berval *ndn, 200 1.1 lukem Entry **ep ) 201 1.1 lukem { 202 1.1 lukem monitor_cache_t tmp_mc, *mc; 203 1.1 lukem 204 1.1 lukem assert( mi != NULL ); 205 1.1 lukem assert( ndn != NULL ); 206 1.1 lukem assert( ep != NULL ); 207 1.1 lukem 208 1.1 lukem *ep = NULL; 209 1.1 lukem 210 1.1 lukem tmp_mc.mc_ndn = *ndn; 211 1.4 christos 212 1.4 christos ldap_pvt_thread_mutex_lock( &mi->mi_cache_lock ); 213 1.3 christos mc = ( monitor_cache_t * )ldap_avl_find( mi->mi_cache, 214 1.1 lukem ( caddr_t )&tmp_mc, monitor_cache_cmp ); 215 1.1 lukem 216 1.1 lukem if ( mc != NULL ) { 217 1.1 lukem /* entry is returned with mutex locked */ 218 1.4 christos monitor_cache_lock( mc->mc_e ); 219 1.1 lukem *ep = mc->mc_e; 220 1.1 lukem } 221 1.1 lukem 222 1.4 christos ldap_pvt_thread_mutex_unlock( &mi->mi_cache_lock ); 223 1.1 lukem 224 1.1 lukem return ( *ep == NULL ? -1 : 0 ); 225 1.1 lukem } 226 1.1 lukem 227 1.1 lukem /* 228 1.1 lukem * gets an entry from the cache based on the normalized dn 229 1.1 lukem * with mutex locked 230 1.1 lukem */ 231 1.1 lukem int 232 1.1 lukem monitor_cache_remove( 233 1.1 lukem monitor_info_t *mi, 234 1.1 lukem struct berval *ndn, 235 1.1 lukem Entry **ep ) 236 1.1 lukem { 237 1.1 lukem monitor_cache_t tmp_mc, *mc; 238 1.1 lukem struct berval pndn; 239 1.1 lukem 240 1.1 lukem assert( mi != NULL ); 241 1.1 lukem assert( ndn != NULL ); 242 1.1 lukem assert( ep != NULL ); 243 1.1 lukem 244 1.1 lukem *ep = NULL; 245 1.1 lukem 246 1.1 lukem dnParent( ndn, &pndn ); 247 1.1 lukem 248 1.1 lukem retry:; 249 1.4 christos ldap_pvt_thread_mutex_lock( &mi->mi_cache_lock ); 250 1.1 lukem 251 1.1 lukem tmp_mc.mc_ndn = *ndn; 252 1.3 christos mc = ( monitor_cache_t * )ldap_avl_find( mi->mi_cache, 253 1.1 lukem ( caddr_t )&tmp_mc, monitor_cache_cmp ); 254 1.1 lukem 255 1.1 lukem if ( mc != NULL ) { 256 1.1 lukem monitor_cache_t *pmc; 257 1.1 lukem 258 1.1 lukem tmp_mc.mc_ndn = pndn; 259 1.3 christos pmc = ( monitor_cache_t * )ldap_avl_find( mi->mi_cache, 260 1.1 lukem ( caddr_t )&tmp_mc, monitor_cache_cmp ); 261 1.1 lukem if ( pmc != NULL ) { 262 1.1 lukem monitor_entry_t *mp = (monitor_entry_t *)mc->mc_e->e_private, 263 1.1 lukem *pmp = (monitor_entry_t *)pmc->mc_e->e_private; 264 1.4 christos Entry **entryp, *prev = NULL; 265 1.1 lukem 266 1.4 christos monitor_cache_lock( pmc->mc_e ); 267 1.1 lukem 268 1.1 lukem for ( entryp = &pmp->mp_children; *entryp != NULL; ) { 269 1.1 lukem monitor_entry_t *next = (monitor_entry_t *)(*entryp)->e_private; 270 1.4 christos 271 1.4 christos monitor_cache_lock( *entryp ); 272 1.1 lukem if ( next == mp ) { 273 1.4 christos if ( mc->mc_e == pmp->mp_last ) { 274 1.4 christos pmp->mp_last = prev; 275 1.4 christos } 276 1.1 lukem *entryp = next->mp_next; 277 1.1 lukem entryp = NULL; 278 1.1 lukem break; 279 1.1 lukem } 280 1.1 lukem 281 1.4 christos if ( prev != NULL ) { 282 1.4 christos monitor_cache_release( mi, prev ); 283 1.4 christos } 284 1.4 christos prev = *entryp; 285 1.1 lukem entryp = &next->mp_next; 286 1.1 lukem } 287 1.4 christos if ( prev ) { 288 1.4 christos monitor_cache_release( mi, prev ); 289 1.4 christos } 290 1.1 lukem 291 1.1 lukem if ( entryp != NULL ) { 292 1.1 lukem Debug( LDAP_DEBUG_ANY, 293 1.1 lukem "monitor_cache_remove(\"%s\"): " 294 1.1 lukem "not in parent's list\n", 295 1.3 christos ndn->bv_val ); 296 1.1 lukem } 297 1.1 lukem 298 1.1 lukem /* either succeeded, and the entry is no longer 299 1.1 lukem * in its parent's list, or failed, and the 300 1.1 lukem * entry is neither mucked with nor returned */ 301 1.1 lukem monitor_cache_release( mi, pmc->mc_e ); 302 1.1 lukem 303 1.1 lukem if ( entryp == NULL ) { 304 1.1 lukem monitor_cache_t *tmpmc; 305 1.1 lukem 306 1.1 lukem tmp_mc.mc_ndn = *ndn; 307 1.3 christos tmpmc = ldap_avl_delete( &mi->mi_cache, 308 1.1 lukem ( caddr_t )&tmp_mc, monitor_cache_cmp ); 309 1.1 lukem assert( tmpmc == mc ); 310 1.1 lukem 311 1.1 lukem *ep = mc->mc_e; 312 1.1 lukem ch_free( mc ); 313 1.1 lukem mc = NULL; 314 1.1 lukem 315 1.1 lukem /* NOTE: we destroy the mutex, but otherwise 316 1.1 lukem * leave the private data around; specifically, 317 1.1 lukem * callbacks need be freed by someone else */ 318 1.1 lukem 319 1.1 lukem ldap_pvt_thread_mutex_destroy( &mp->mp_mutex ); 320 1.1 lukem mp->mp_next = NULL; 321 1.1 lukem mp->mp_children = NULL; 322 1.4 christos mp->mp_last = NULL; 323 1.1 lukem } 324 1.1 lukem 325 1.1 lukem } 326 1.1 lukem 327 1.1 lukem if ( mc ) { 328 1.1 lukem monitor_cache_release( mi, mc->mc_e ); 329 1.1 lukem } 330 1.1 lukem } 331 1.1 lukem 332 1.4 christos ldap_pvt_thread_mutex_unlock( &mi->mi_cache_lock ); 333 1.1 lukem 334 1.1 lukem return ( *ep == NULL ? -1 : 0 ); 335 1.1 lukem } 336 1.1 lukem 337 1.1 lukem /* 338 1.1 lukem * If the entry exists in cache, it is returned in locked status; 339 1.1 lukem * otherwise, if the parent exists, if it may generate volatile 340 1.1 lukem * descendants an attempt to generate the required entry is 341 1.1 lukem * performed and, if successful, the entry is returned 342 1.1 lukem */ 343 1.1 lukem int 344 1.1 lukem monitor_cache_dn2entry( 345 1.1 lukem Operation *op, 346 1.1 lukem SlapReply *rs, 347 1.1 lukem struct berval *ndn, 348 1.1 lukem Entry **ep, 349 1.1 lukem Entry **matched ) 350 1.1 lukem { 351 1.1 lukem monitor_info_t *mi = (monitor_info_t *)op->o_bd->be_private; 352 1.1 lukem int rc; 353 1.1 lukem struct berval p_ndn = BER_BVNULL; 354 1.1 lukem Entry *e_parent; 355 1.1 lukem monitor_entry_t *mp; 356 1.1 lukem 357 1.1 lukem assert( mi != NULL ); 358 1.1 lukem assert( ndn != NULL ); 359 1.1 lukem assert( ep != NULL ); 360 1.1 lukem assert( matched != NULL ); 361 1.1 lukem 362 1.1 lukem *matched = NULL; 363 1.1 lukem 364 1.1 lukem if ( !dnIsSuffix( ndn, &op->o_bd->be_nsuffix[ 0 ] ) ) { 365 1.1 lukem return( -1 ); 366 1.1 lukem } 367 1.1 lukem 368 1.1 lukem rc = monitor_cache_get( mi, ndn, ep ); 369 1.1 lukem if ( !rc && *ep != NULL ) { 370 1.1 lukem return( 0 ); 371 1.1 lukem } 372 1.1 lukem 373 1.1 lukem /* try with parent/ancestors */ 374 1.1 lukem if ( BER_BVISNULL( ndn ) ) { 375 1.1 lukem BER_BVSTR( &p_ndn, "" ); 376 1.1 lukem 377 1.1 lukem } else { 378 1.1 lukem dnParent( ndn, &p_ndn ); 379 1.1 lukem } 380 1.1 lukem 381 1.1 lukem rc = monitor_cache_dn2entry( op, rs, &p_ndn, &e_parent, matched ); 382 1.1 lukem if ( rc || e_parent == NULL ) { 383 1.1 lukem return( -1 ); 384 1.1 lukem } 385 1.1 lukem 386 1.1 lukem mp = ( monitor_entry_t * )e_parent->e_private; 387 1.1 lukem rc = -1; 388 1.1 lukem if ( mp->mp_flags & MONITOR_F_VOLATILE_CH ) { 389 1.1 lukem /* parent entry generates volatile children */ 390 1.1 lukem rc = monitor_entry_create( op, rs, ndn, e_parent, ep ); 391 1.1 lukem } 392 1.1 lukem 393 1.1 lukem if ( !rc ) { 394 1.1 lukem monitor_cache_lock( *ep ); 395 1.1 lukem monitor_cache_release( mi, e_parent ); 396 1.1 lukem 397 1.1 lukem } else { 398 1.1 lukem *matched = e_parent; 399 1.1 lukem } 400 1.1 lukem 401 1.1 lukem return( rc ); 402 1.1 lukem } 403 1.1 lukem 404 1.1 lukem /* 405 1.1 lukem * releases the lock of the entry; if it is marked as volatile, it is 406 1.1 lukem * destroyed. 407 1.1 lukem */ 408 1.1 lukem int 409 1.1 lukem monitor_cache_release( 410 1.1 lukem monitor_info_t *mi, 411 1.1 lukem Entry *e ) 412 1.1 lukem { 413 1.1 lukem monitor_entry_t *mp; 414 1.1 lukem 415 1.1 lukem assert( mi != NULL ); 416 1.1 lukem assert( e != NULL ); 417 1.1 lukem assert( e->e_private != NULL ); 418 1.1 lukem 419 1.1 lukem mp = ( monitor_entry_t * )e->e_private; 420 1.1 lukem 421 1.1 lukem if ( mp->mp_flags & MONITOR_F_VOLATILE ) { 422 1.1 lukem ldap_pvt_thread_mutex_unlock( &mp->mp_mutex ); 423 1.1 lukem ldap_pvt_thread_mutex_destroy( &mp->mp_mutex ); 424 1.1 lukem ch_free( mp ); 425 1.1 lukem e->e_private = NULL; 426 1.1 lukem entry_free( e ); 427 1.1 lukem 428 1.1 lukem return( 0 ); 429 1.1 lukem } 430 1.1 lukem 431 1.1 lukem ldap_pvt_thread_mutex_unlock( &mp->mp_mutex ); 432 1.1 lukem 433 1.1 lukem return( 0 ); 434 1.1 lukem } 435 1.1 lukem 436 1.1 lukem static void 437 1.1 lukem monitor_entry_destroy( void *v_mc ) 438 1.1 lukem { 439 1.1 lukem monitor_cache_t *mc = (monitor_cache_t *)v_mc; 440 1.1 lukem 441 1.1 lukem if ( mc->mc_e != NULL ) { 442 1.1 lukem monitor_entry_t *mp; 443 1.1 lukem 444 1.1 lukem assert( mc->mc_e->e_private != NULL ); 445 1.1 lukem 446 1.1 lukem mp = ( monitor_entry_t * )mc->mc_e->e_private; 447 1.1 lukem 448 1.1 lukem if ( mp->mp_cb ) { 449 1.1 lukem monitor_callback_t *cb; 450 1.1 lukem 451 1.1 lukem for ( cb = mp->mp_cb; cb != NULL; ) { 452 1.1 lukem monitor_callback_t *next = cb->mc_next; 453 1.1 lukem 454 1.1 lukem if ( cb->mc_free ) { 455 1.1 lukem (void)cb->mc_free( mc->mc_e, &cb->mc_private ); 456 1.1 lukem } 457 1.1 lukem ch_free( mp->mp_cb ); 458 1.1 lukem 459 1.1 lukem cb = next; 460 1.1 lukem } 461 1.1 lukem } 462 1.1 lukem 463 1.1 lukem ldap_pvt_thread_mutex_destroy( &mp->mp_mutex ); 464 1.1 lukem 465 1.1 lukem ch_free( mp ); 466 1.1 lukem mc->mc_e->e_private = NULL; 467 1.1 lukem entry_free( mc->mc_e ); 468 1.1 lukem } 469 1.1 lukem 470 1.1 lukem ch_free( mc ); 471 1.1 lukem } 472 1.1 lukem 473 1.1 lukem int 474 1.1 lukem monitor_cache_destroy( 475 1.1 lukem monitor_info_t *mi ) 476 1.1 lukem { 477 1.1 lukem if ( mi->mi_cache ) { 478 1.3 christos ldap_avl_free( mi->mi_cache, monitor_entry_destroy ); 479 1.1 lukem } 480 1.1 lukem 481 1.1 lukem return 0; 482 1.1 lukem } 483 1.1 lukem 484 1.2 christos int monitor_back_release( 485 1.2 christos Operation *op, 486 1.2 christos Entry *e, 487 1.2 christos int rw ) 488 1.2 christos { 489 1.2 christos monitor_info_t *mi = ( monitor_info_t * )op->o_bd->be_private; 490 1.2 christos return monitor_cache_release( mi, e ); 491 1.2 christos } 492