1/* $NetBSD: id2entry.c,v 1.1.1.3 2010/12/12 15:22:55 adam Exp $ */ 2 3/* id2entry.c - routines to deal with the id2entry database */ 4/* OpenLDAP: pkg/ldap/servers/slapd/back-bdb/id2entry.c,v 1.72.2.14 2010/04/13 20:23:24 kurt Exp */ 5/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 6 * 7 * Copyright 2000-2010 The OpenLDAP Foundation. 8 * All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted only as authorized by the OpenLDAP 12 * Public License. 13 * 14 * A copy of this license is available in the file LICENSE in the 15 * top-level directory of the distribution or, alternatively, at 16 * <http://www.OpenLDAP.org/license.html>. 17 */ 18 19#include "portable.h" 20 21#include <stdio.h> 22#include <ac/string.h> 23#include <ac/errno.h> 24 25#include "back-bdb.h" 26 27static int bdb_id2entry_put( 28 BackendDB *be, 29 DB_TXN *tid, 30 Entry *e, 31 int flag ) 32{ 33 struct bdb_info *bdb = (struct bdb_info *) be->be_private; 34 DB *db = bdb->bi_id2entry->bdi_db; 35 DBT key, data; 36 struct berval bv; 37 int rc; 38 ID nid; 39#ifdef BDB_HIER 40 struct berval odn, ondn; 41 42 /* We only store rdns, and they go in the dn2id database. */ 43 44 odn = e->e_name; ondn = e->e_nname; 45 46 e->e_name = slap_empty_bv; 47 e->e_nname = slap_empty_bv; 48#endif 49 DBTzero( &key ); 50 51 /* Store ID in BigEndian format */ 52 key.data = &nid; 53 key.size = sizeof(ID); 54 BDB_ID2DISK( e->e_id, &nid ); 55 56 rc = entry_encode( e, &bv ); 57#ifdef BDB_HIER 58 e->e_name = odn; e->e_nname = ondn; 59#endif 60 if( rc != LDAP_SUCCESS ) { 61 return -1; 62 } 63 64 DBTzero( &data ); 65 bv2DBT( &bv, &data ); 66 67 rc = db->put( db, tid, &key, &data, flag ); 68 69 free( bv.bv_val ); 70 return rc; 71} 72 73/* 74 * This routine adds (or updates) an entry on disk. 75 * The cache should be already be updated. 76 */ 77 78 79int bdb_id2entry_add( 80 BackendDB *be, 81 DB_TXN *tid, 82 Entry *e ) 83{ 84 return bdb_id2entry_put(be, tid, e, DB_NOOVERWRITE); 85} 86 87int bdb_id2entry_update( 88 BackendDB *be, 89 DB_TXN *tid, 90 Entry *e ) 91{ 92 return bdb_id2entry_put(be, tid, e, 0); 93} 94 95int bdb_id2entry( 96 BackendDB *be, 97 DB_TXN *tid, 98 ID id, 99 Entry **e ) 100{ 101 struct bdb_info *bdb = (struct bdb_info *) be->be_private; 102 DB *db = bdb->bi_id2entry->bdi_db; 103 DBT key, data; 104 DBC *cursor; 105 EntryHeader eh; 106 char buf[16]; 107 int rc = 0, off; 108 ID nid; 109 110 *e = NULL; 111 112 DBTzero( &key ); 113 key.data = &nid; 114 key.size = sizeof(ID); 115 BDB_ID2DISK( id, &nid ); 116 117 DBTzero( &data ); 118 data.flags = DB_DBT_USERMEM | DB_DBT_PARTIAL; 119 120 /* fetch it */ 121 rc = db->cursor( db, tid, &cursor, bdb->bi_db_opflags ); 122 if ( rc ) return rc; 123 124 /* Get the nattrs / nvals counts first */ 125 data.ulen = data.dlen = sizeof(buf); 126 data.data = buf; 127 rc = cursor->c_get( cursor, &key, &data, DB_SET ); 128 if ( rc ) goto finish; 129 130 131 eh.bv.bv_val = buf; 132 eh.bv.bv_len = data.size; 133 rc = entry_header( &eh ); 134 if ( rc ) goto finish; 135 136 /* Get the size */ 137 data.flags ^= DB_DBT_PARTIAL; 138 data.ulen = 0; 139 rc = cursor->c_get( cursor, &key, &data, DB_CURRENT ); 140 if ( rc != DB_BUFFER_SMALL ) goto finish; 141 142 /* Allocate a block and retrieve the data */ 143 off = eh.data - eh.bv.bv_val; 144 eh.bv.bv_len = eh.nvals * sizeof( struct berval ) + data.size; 145 eh.bv.bv_val = ch_malloc( eh.bv.bv_len ); 146 eh.data = eh.bv.bv_val + eh.nvals * sizeof( struct berval ); 147 data.data = eh.data; 148 data.ulen = data.size; 149 150 /* skip past already parsed nattr/nvals */ 151 eh.data += off; 152 153 rc = cursor->c_get( cursor, &key, &data, DB_CURRENT ); 154 155finish: 156 cursor->c_close( cursor ); 157 158 if( rc != 0 ) { 159 return rc; 160 } 161 162#ifdef SLAP_ZONE_ALLOC 163 rc = entry_decode(&eh, e, bdb->bi_cache.c_zctx); 164#else 165 rc = entry_decode(&eh, e); 166#endif 167 168 if( rc == 0 ) { 169 (*e)->e_id = id; 170 } else { 171 /* only free on error. On success, the entry was 172 * decoded in place. 173 */ 174#ifndef SLAP_ZONE_ALLOC 175 ch_free(eh.bv.bv_val); 176#endif 177 } 178#ifdef SLAP_ZONE_ALLOC 179 ch_free(eh.bv.bv_val); 180#endif 181 182 return rc; 183} 184 185int bdb_id2entry_delete( 186 BackendDB *be, 187 DB_TXN *tid, 188 Entry *e ) 189{ 190 struct bdb_info *bdb = (struct bdb_info *) be->be_private; 191 DB *db = bdb->bi_id2entry->bdi_db; 192 DBT key; 193 int rc; 194 ID nid; 195 196 DBTzero( &key ); 197 key.data = &nid; 198 key.size = sizeof(ID); 199 BDB_ID2DISK( e->e_id, &nid ); 200 201 /* delete from database */ 202 rc = db->del( db, tid, &key, 0 ); 203 204 return rc; 205} 206 207int bdb_entry_return( 208 Entry *e 209) 210{ 211 /* Our entries are allocated in two blocks; the data comes from 212 * the db itself and the Entry structure and associated pointers 213 * are allocated in entry_decode. The db data pointer is saved 214 * in e_bv. 215 */ 216 if ( e->e_bv.bv_val ) { 217 /* See if the DNs were changed by modrdn */ 218 if( e->e_nname.bv_val < e->e_bv.bv_val || e->e_nname.bv_val > 219 e->e_bv.bv_val + e->e_bv.bv_len ) { 220 ch_free(e->e_name.bv_val); 221 ch_free(e->e_nname.bv_val); 222 } 223 e->e_name.bv_val = NULL; 224 e->e_nname.bv_val = NULL; 225 /* In tool mode the e_bv buffer is realloc'd, leave it alone */ 226 if( !(slapMode & SLAP_TOOL_MODE) ) { 227 free( e->e_bv.bv_val ); 228 } 229 BER_BVZERO( &e->e_bv ); 230 } 231 entry_free( e ); 232 return 0; 233} 234 235int bdb_entry_release( 236 Operation *op, 237 Entry *e, 238 int rw ) 239{ 240 struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private; 241 struct bdb_op_info *boi; 242 OpExtra *oex; 243 244 /* slapMode : SLAP_SERVER_MODE, SLAP_TOOL_MODE, 245 SLAP_TRUNCATE_MODE, SLAP_UNDEFINED_MODE */ 246 247 if ( slapMode == SLAP_SERVER_MODE ) { 248 /* If not in our cache, just free it */ 249 if ( !e->e_private ) { 250#ifdef SLAP_ZONE_ALLOC 251 return bdb_entry_return( bdb, e, -1 ); 252#else 253 return bdb_entry_return( e ); 254#endif 255 } 256 /* free entry and reader or writer lock */ 257 LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) { 258 if ( oex->oe_key == bdb ) break; 259 } 260 boi = (struct bdb_op_info *)oex; 261 262 /* lock is freed with txn */ 263 if ( !boi || boi->boi_txn ) { 264 bdb_unlocked_cache_return_entry_rw( bdb, e, rw ); 265 } else { 266 struct bdb_lock_info *bli, *prev; 267 for ( prev=(struct bdb_lock_info *)&boi->boi_locks, 268 bli = boi->boi_locks; bli; prev=bli, bli=bli->bli_next ) { 269 if ( bli->bli_id == e->e_id ) { 270 bdb_cache_return_entry_rw( bdb, e, rw, &bli->bli_lock ); 271 prev->bli_next = bli->bli_next; 272 /* Cleanup, or let caller know we unlocked */ 273 if ( bli->bli_flag & BLI_DONTFREE ) 274 bli->bli_flag = 0; 275 else 276 op->o_tmpfree( bli, op->o_tmpmemctx ); 277 break; 278 } 279 } 280 if ( !boi->boi_locks ) { 281 LDAP_SLIST_REMOVE( &op->o_extra, &boi->boi_oe, OpExtra, oe_next ); 282 if ( !(boi->boi_flag & BOI_DONTFREE)) 283 op->o_tmpfree( boi, op->o_tmpmemctx ); 284 } 285 } 286 } else { 287#ifdef SLAP_ZONE_ALLOC 288 int zseq = -1; 289 if (e->e_private != NULL) { 290 BEI(e)->bei_e = NULL; 291 zseq = BEI(e)->bei_zseq; 292 } 293#else 294 if (e->e_private != NULL) 295 BEI(e)->bei_e = NULL; 296#endif 297 e->e_private = NULL; 298#ifdef SLAP_ZONE_ALLOC 299 bdb_entry_return ( bdb, e, zseq ); 300#else 301 bdb_entry_return ( e ); 302#endif 303 } 304 305 return 0; 306} 307 308/* return LDAP_SUCCESS IFF we can retrieve the specified entry. 309 */ 310int bdb_entry_get( 311 Operation *op, 312 struct berval *ndn, 313 ObjectClass *oc, 314 AttributeDescription *at, 315 int rw, 316 Entry **ent ) 317{ 318 struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private; 319 struct bdb_op_info *boi = NULL; 320 DB_TXN *txn = NULL; 321 Entry *e = NULL; 322 EntryInfo *ei; 323 int rc; 324 const char *at_name = at ? at->ad_cname.bv_val : "(null)"; 325 326 DB_LOCK lock; 327 328 Debug( LDAP_DEBUG_ARGS, 329 "=> bdb_entry_get: ndn: \"%s\"\n", ndn->bv_val, 0, 0 ); 330 Debug( LDAP_DEBUG_ARGS, 331 "=> bdb_entry_get: oc: \"%s\", at: \"%s\"\n", 332 oc ? oc->soc_cname.bv_val : "(null)", at_name, 0); 333 334 if( op ) { 335 OpExtra *oex; 336 LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) { 337 if ( oex->oe_key == bdb ) break; 338 } 339 boi = (struct bdb_op_info *)oex; 340 if ( boi ) 341 txn = boi->boi_txn; 342 } 343 344 if ( !txn ) { 345 rc = bdb_reader_get( op, bdb->bi_dbenv, &txn ); 346 switch(rc) { 347 case 0: 348 break; 349 default: 350 return LDAP_OTHER; 351 } 352 } 353 354dn2entry_retry: 355 /* can we find entry */ 356 rc = bdb_dn2entry( op, txn, ndn, &ei, 0, &lock ); 357 switch( rc ) { 358 case DB_NOTFOUND: 359 case 0: 360 break; 361 case DB_LOCK_DEADLOCK: 362 case DB_LOCK_NOTGRANTED: 363 /* the txn must abort and retry */ 364 if ( txn ) { 365 if ( boi ) boi->boi_err = rc; 366 return LDAP_BUSY; 367 } 368 ldap_pvt_thread_yield(); 369 goto dn2entry_retry; 370 default: 371 if ( boi ) boi->boi_err = rc; 372 return (rc != LDAP_BUSY) ? LDAP_OTHER : LDAP_BUSY; 373 } 374 if (ei) e = ei->bei_e; 375 if (e == NULL) { 376 Debug( LDAP_DEBUG_ACL, 377 "=> bdb_entry_get: cannot find entry: \"%s\"\n", 378 ndn->bv_val, 0, 0 ); 379 return LDAP_NO_SUCH_OBJECT; 380 } 381 382 Debug( LDAP_DEBUG_ACL, 383 "=> bdb_entry_get: found entry: \"%s\"\n", 384 ndn->bv_val, 0, 0 ); 385 386 if ( oc && !is_entry_objectclass( e, oc, 0 )) { 387 Debug( LDAP_DEBUG_ACL, 388 "<= bdb_entry_get: failed to find objectClass %s\n", 389 oc->soc_cname.bv_val, 0, 0 ); 390 rc = LDAP_NO_SUCH_ATTRIBUTE; 391 goto return_results; 392 } 393 394 /* NOTE: attr_find() or attrs_find()? */ 395 if ( at && attr_find( e->e_attrs, at ) == NULL ) { 396 Debug( LDAP_DEBUG_ACL, 397 "<= bdb_entry_get: failed to find attribute %s\n", 398 at->ad_cname.bv_val, 0, 0 ); 399 rc = LDAP_NO_SUCH_ATTRIBUTE; 400 goto return_results; 401 } 402 403return_results: 404 if( rc != LDAP_SUCCESS ) { 405 /* free entry */ 406 bdb_cache_return_entry_rw(bdb, e, rw, &lock); 407 408 } else { 409 if ( slapMode == SLAP_SERVER_MODE ) { 410 *ent = e; 411 /* big drag. we need a place to store a read lock so we can 412 * release it later?? If we're in a txn, nothing is needed 413 * here because the locks will go away with the txn. 414 */ 415 if ( op ) { 416 if ( !boi ) { 417 boi = op->o_tmpcalloc(1,sizeof(struct bdb_op_info),op->o_tmpmemctx); 418 boi->boi_oe.oe_key = bdb; 419 LDAP_SLIST_INSERT_HEAD( &op->o_extra, &boi->boi_oe, oe_next ); 420 } 421 if ( !boi->boi_txn ) { 422 struct bdb_lock_info *bli; 423 bli = op->o_tmpalloc( sizeof(struct bdb_lock_info), 424 op->o_tmpmemctx ); 425 bli->bli_next = boi->boi_locks; 426 bli->bli_id = e->e_id; 427 bli->bli_flag = 0; 428 bli->bli_lock = lock; 429 boi->boi_locks = bli; 430 } 431 } 432 } else { 433 *ent = entry_dup( e ); 434 bdb_cache_return_entry_rw(bdb, e, rw, &lock); 435 } 436 } 437 438 Debug( LDAP_DEBUG_TRACE, 439 "bdb_entry_get: rc=%d\n", 440 rc, 0, 0 ); 441 return(rc); 442} 443