Back to index

openldap  2.4.31
id2entry.c
Go to the documentation of this file.
00001 /* id2entry.c - routines to deal with the id2entry database */
00002 /* $OpenLDAP$ */
00003 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
00004  *
00005  * Copyright 2000-2012 The OpenLDAP Foundation.
00006  * All rights reserved.
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted only as authorized by the OpenLDAP
00010  * Public License.
00011  *
00012  * A copy of this license is available in the file LICENSE in the
00013  * top-level directory of the distribution or, alternatively, at
00014  * <http://www.OpenLDAP.org/license.html>.
00015  */
00016 
00017 #include "portable.h"
00018 
00019 #include <stdio.h>
00020 #include <ac/string.h>
00021 #include <ac/errno.h>
00022 
00023 #include "back-bdb.h"
00024 
00025 static int bdb_id2entry_put(
00026        BackendDB *be,
00027        DB_TXN *tid,
00028        Entry *e,
00029        int flag )
00030 {
00031        struct bdb_info *bdb = (struct bdb_info *) be->be_private;
00032        DB *db = bdb->bi_id2entry->bdi_db;
00033        DBT key, data;
00034        struct berval bv;
00035        int rc;
00036        ID nid;
00037 #ifdef BDB_HIER
00038        struct berval odn, ondn;
00039 
00040        /* We only store rdns, and they go in the dn2id database. */
00041 
00042        odn = e->e_name; ondn = e->e_nname;
00043 
00044        e->e_name = slap_empty_bv;
00045        e->e_nname = slap_empty_bv;
00046 #endif
00047        DBTzero( &key );
00048 
00049        /* Store ID in BigEndian format */
00050        key.data = &nid;
00051        key.size = sizeof(ID);
00052        BDB_ID2DISK( e->e_id, &nid );
00053 
00054        rc = entry_encode( e, &bv );
00055 #ifdef BDB_HIER
00056        e->e_name = odn; e->e_nname = ondn;
00057 #endif
00058        if( rc != LDAP_SUCCESS ) {
00059               return -1;
00060        }
00061 
00062        DBTzero( &data );
00063        bv2DBT( &bv, &data );
00064 
00065        rc = db->put( db, tid, &key, &data, flag );
00066 
00067        free( bv.bv_val );
00068        return rc;
00069 }
00070 
00071 /*
00072  * This routine adds (or updates) an entry on disk.
00073  * The cache should be already be updated.
00074  */
00075 
00076 
00077 int bdb_id2entry_add(
00078        BackendDB *be,
00079        DB_TXN *tid,
00080        Entry *e )
00081 {
00082        return bdb_id2entry_put(be, tid, e, DB_NOOVERWRITE);
00083 }
00084 
00085 int bdb_id2entry_update(
00086        BackendDB *be,
00087        DB_TXN *tid,
00088        Entry *e )
00089 {
00090        return bdb_id2entry_put(be, tid, e, 0);
00091 }
00092 
00093 int bdb_id2entry(
00094        BackendDB *be,
00095        DB_TXN *tid,
00096        ID id,
00097        Entry **e )
00098 {
00099        struct bdb_info *bdb = (struct bdb_info *) be->be_private;
00100        DB *db = bdb->bi_id2entry->bdi_db;
00101        DBT key, data;
00102        DBC *cursor;
00103        EntryHeader eh;
00104        char buf[16];
00105        int rc = 0, off;
00106        ID nid;
00107 
00108        *e = NULL;
00109 
00110        DBTzero( &key );
00111        key.data = &nid;
00112        key.size = sizeof(ID);
00113        BDB_ID2DISK( id, &nid );
00114 
00115        DBTzero( &data );
00116        data.flags = DB_DBT_USERMEM | DB_DBT_PARTIAL;
00117 
00118        /* fetch it */
00119        rc = db->cursor( db, tid, &cursor, bdb->bi_db_opflags );
00120        if ( rc ) return rc;
00121 
00122        /* Get the nattrs / nvals counts first */
00123        data.ulen = data.dlen = sizeof(buf);
00124        data.data = buf;
00125        rc = cursor->c_get( cursor, &key, &data, DB_SET );
00126        if ( rc ) goto finish;
00127 
00128 
00129        eh.bv.bv_val = buf;
00130        eh.bv.bv_len = data.size;
00131        rc = entry_header( &eh );
00132        if ( rc ) goto finish;
00133 
00134        if ( eh.nvals ) {
00135               /* Get the size */
00136               data.flags ^= DB_DBT_PARTIAL;
00137               data.ulen = 0;
00138               rc = cursor->c_get( cursor, &key, &data, DB_CURRENT );
00139               if ( rc != DB_BUFFER_SMALL ) goto finish;
00140 
00141               /* Allocate a block and retrieve the data */
00142               off = eh.data - eh.bv.bv_val;
00143               eh.bv.bv_len = eh.nvals * sizeof( struct berval ) + data.size;
00144               eh.bv.bv_val = ch_malloc( eh.bv.bv_len );
00145               eh.data = eh.bv.bv_val + eh.nvals * sizeof( struct berval );
00146               data.data = eh.data;
00147               data.ulen = data.size;
00148 
00149               /* skip past already parsed nattr/nvals */
00150               eh.data += off;
00151 
00152               rc = cursor->c_get( cursor, &key, &data, DB_CURRENT );
00153        }
00154 
00155 finish:
00156        cursor->c_close( cursor );
00157 
00158        if( rc != 0 ) {
00159               return rc;
00160        }
00161 
00162        if ( eh.nvals ) {
00163 #ifdef SLAP_ZONE_ALLOC
00164               rc = entry_decode(&eh, e, bdb->bi_cache.c_zctx);
00165 #else
00166               rc = entry_decode(&eh, e);
00167 #endif
00168        } else {
00169               *e = entry_alloc();
00170        }
00171 
00172        if( rc == 0 ) {
00173               (*e)->e_id = id;
00174        } else {
00175               /* only free on error. On success, the entry was
00176                * decoded in place.
00177                */
00178 #ifndef SLAP_ZONE_ALLOC
00179               ch_free(eh.bv.bv_val);
00180 #endif
00181        }
00182 #ifdef SLAP_ZONE_ALLOC
00183        ch_free(eh.bv.bv_val);
00184 #endif
00185 
00186        return rc;
00187 }
00188 
00189 int bdb_id2entry_delete(
00190        BackendDB *be,
00191        DB_TXN *tid,
00192        Entry *e )
00193 {
00194        struct bdb_info *bdb = (struct bdb_info *) be->be_private;
00195        DB *db = bdb->bi_id2entry->bdi_db;
00196        DBT key;
00197        int rc;
00198        ID nid;
00199 
00200        DBTzero( &key );
00201        key.data = &nid;
00202        key.size = sizeof(ID);
00203        BDB_ID2DISK( e->e_id, &nid );
00204 
00205        /* delete from database */
00206        rc = db->del( db, tid, &key, 0 );
00207 
00208        return rc;
00209 }
00210 
00211 int bdb_entry_return(
00212        Entry *e
00213 )
00214 {
00215        /* Our entries are allocated in two blocks; the data comes from
00216         * the db itself and the Entry structure and associated pointers
00217         * are allocated in entry_decode. The db data pointer is saved
00218         * in e_bv.
00219         */
00220        if ( e->e_bv.bv_val ) {
00221               /* See if the DNs were changed by modrdn */
00222               if( e->e_nname.bv_val < e->e_bv.bv_val || e->e_nname.bv_val >
00223                      e->e_bv.bv_val + e->e_bv.bv_len ) {
00224                      ch_free(e->e_name.bv_val);
00225                      ch_free(e->e_nname.bv_val);
00226               }
00227               e->e_name.bv_val = NULL;
00228               e->e_nname.bv_val = NULL;
00229               /* In tool mode the e_bv buffer is realloc'd, leave it alone */
00230               if( !(slapMode & SLAP_TOOL_MODE) ) {
00231                      free( e->e_bv.bv_val );
00232               }
00233               BER_BVZERO( &e->e_bv );
00234        }
00235        entry_free( e );
00236        return 0;
00237 }
00238 
00239 int bdb_entry_release(
00240        Operation *op,
00241        Entry *e,
00242        int rw )
00243 {
00244        struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
00245        struct bdb_op_info *boi;
00246        OpExtra *oex;
00247  
00248        /* slapMode : SLAP_SERVER_MODE, SLAP_TOOL_MODE,
00249                      SLAP_TRUNCATE_MODE, SLAP_UNDEFINED_MODE */
00250  
00251        if ( slapMode == SLAP_SERVER_MODE ) {
00252               /* If not in our cache, just free it */
00253               if ( !e->e_private ) {
00254 #ifdef SLAP_ZONE_ALLOC
00255                      return bdb_entry_return( bdb, e, -1 );
00256 #else
00257                      return bdb_entry_return( e );
00258 #endif
00259               }
00260               /* free entry and reader or writer lock */
00261               LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
00262                      if ( oex->oe_key == bdb ) break;
00263               }
00264               boi = (struct bdb_op_info *)oex;
00265 
00266               /* lock is freed with txn */
00267               if ( !boi || boi->boi_txn ) {
00268                      bdb_unlocked_cache_return_entry_rw( bdb, e, rw );
00269               } else {
00270                      struct bdb_lock_info *bli, *prev;
00271                      for ( prev=(struct bdb_lock_info *)&boi->boi_locks,
00272                             bli = boi->boi_locks; bli; prev=bli, bli=bli->bli_next ) {
00273                             if ( bli->bli_id == e->e_id ) {
00274                                    bdb_cache_return_entry_rw( bdb, e, rw, &bli->bli_lock );
00275                                    prev->bli_next = bli->bli_next;
00276                                    /* Cleanup, or let caller know we unlocked */
00277                                    if ( bli->bli_flag & BLI_DONTFREE )
00278                                           bli->bli_flag = 0;
00279                                    else
00280                                           op->o_tmpfree( bli, op->o_tmpmemctx );
00281                                    break;
00282                             }
00283                      }
00284                      if ( !boi->boi_locks ) {
00285                             LDAP_SLIST_REMOVE( &op->o_extra, &boi->boi_oe, OpExtra, oe_next );
00286                             if ( !(boi->boi_flag & BOI_DONTFREE))
00287                                    op->o_tmpfree( boi, op->o_tmpmemctx );
00288                      }
00289               }
00290        } else {
00291 #ifdef SLAP_ZONE_ALLOC
00292               int zseq = -1;
00293               if (e->e_private != NULL) {
00294                      BEI(e)->bei_e = NULL;
00295                      zseq = BEI(e)->bei_zseq;
00296               }
00297 #else
00298               if (e->e_private != NULL)
00299                      BEI(e)->bei_e = NULL;
00300 #endif
00301               e->e_private = NULL;
00302 #ifdef SLAP_ZONE_ALLOC
00303               bdb_entry_return ( bdb, e, zseq );
00304 #else
00305               bdb_entry_return ( e );
00306 #endif
00307        }
00308  
00309        return 0;
00310 }
00311 
00312 /* return LDAP_SUCCESS IFF we can retrieve the specified entry.
00313  */
00314 int bdb_entry_get(
00315        Operation *op,
00316        struct berval *ndn,
00317        ObjectClass *oc,
00318        AttributeDescription *at,
00319        int rw,
00320        Entry **ent )
00321 {
00322        struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
00323        struct bdb_op_info *boi = NULL;
00324        DB_TXN *txn = NULL;
00325        Entry *e = NULL;
00326        EntryInfo *ei;
00327        int    rc;
00328        const char *at_name = at ? at->ad_cname.bv_val : "(null)";
00329 
00330        DB_LOCK              lock;
00331 
00332        Debug( LDAP_DEBUG_ARGS,
00333               "=> bdb_entry_get: ndn: \"%s\"\n", ndn->bv_val, 0, 0 ); 
00334        Debug( LDAP_DEBUG_ARGS,
00335               "=> bdb_entry_get: oc: \"%s\", at: \"%s\"\n",
00336               oc ? oc->soc_cname.bv_val : "(null)", at_name, 0);
00337 
00338        if( op ) {
00339               OpExtra *oex;
00340               LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
00341                      if ( oex->oe_key == bdb ) break;
00342               }
00343               boi = (struct bdb_op_info *)oex;
00344               if ( boi )
00345                      txn = boi->boi_txn;
00346        }
00347 
00348        if ( !txn ) {
00349               rc = bdb_reader_get( op, bdb->bi_dbenv, &txn );
00350               switch(rc) {
00351               case 0:
00352                      break;
00353               default:
00354                      return LDAP_OTHER;
00355               }
00356        }
00357 
00358 dn2entry_retry:
00359        /* can we find entry */
00360        rc = bdb_dn2entry( op, txn, ndn, &ei, 0, &lock );
00361        switch( rc ) {
00362        case DB_NOTFOUND:
00363        case 0:
00364               break;
00365        case DB_LOCK_DEADLOCK:
00366        case DB_LOCK_NOTGRANTED:
00367               /* the txn must abort and retry */
00368               if ( txn ) {
00369                      if ( boi ) boi->boi_err = rc;
00370                      return LDAP_BUSY;
00371               }
00372               ldap_pvt_thread_yield();
00373               goto dn2entry_retry;
00374        default:
00375               if ( boi ) boi->boi_err = rc;
00376               return (rc != LDAP_BUSY) ? LDAP_OTHER : LDAP_BUSY;
00377        }
00378        if (ei) e = ei->bei_e;
00379        if (e == NULL) {
00380               Debug( LDAP_DEBUG_ACL,
00381                      "=> bdb_entry_get: cannot find entry: \"%s\"\n",
00382                             ndn->bv_val, 0, 0 ); 
00383               return LDAP_NO_SUCH_OBJECT; 
00384        }
00385        
00386        Debug( LDAP_DEBUG_ACL,
00387               "=> bdb_entry_get: found entry: \"%s\"\n",
00388               ndn->bv_val, 0, 0 ); 
00389 
00390        if ( oc && !is_entry_objectclass( e, oc, 0 )) {
00391               Debug( LDAP_DEBUG_ACL,
00392                      "<= bdb_entry_get: failed to find objectClass %s\n",
00393                      oc->soc_cname.bv_val, 0, 0 ); 
00394               rc = LDAP_NO_SUCH_ATTRIBUTE;
00395               goto return_results;
00396        }
00397 
00398        /* NOTE: attr_find() or attrs_find()? */
00399        if ( at && attr_find( e->e_attrs, at ) == NULL ) {
00400               Debug( LDAP_DEBUG_ACL,
00401                      "<= bdb_entry_get: failed to find attribute %s\n",
00402                      at->ad_cname.bv_val, 0, 0 ); 
00403               rc = LDAP_NO_SUCH_ATTRIBUTE;
00404               goto return_results;
00405        }
00406 
00407 return_results:
00408        if( rc != LDAP_SUCCESS ) {
00409               /* free entry */
00410               bdb_cache_return_entry_rw(bdb, e, rw, &lock);
00411 
00412        } else {
00413               if ( slapMode == SLAP_SERVER_MODE ) {
00414                      *ent = e;
00415                      /* big drag. we need a place to store a read lock so we can
00416                       * release it later?? If we're in a txn, nothing is needed
00417                       * here because the locks will go away with the txn.
00418                       */
00419                      if ( op ) {
00420                             if ( !boi ) {
00421                                    boi = op->o_tmpcalloc(1,sizeof(struct bdb_op_info),op->o_tmpmemctx);
00422                                    boi->boi_oe.oe_key = bdb;
00423                                    LDAP_SLIST_INSERT_HEAD( &op->o_extra, &boi->boi_oe, oe_next );
00424                             }
00425                             if ( !boi->boi_txn ) {
00426                                    struct bdb_lock_info *bli;
00427                                    bli = op->o_tmpalloc( sizeof(struct bdb_lock_info),
00428                                           op->o_tmpmemctx );
00429                                    bli->bli_next = boi->boi_locks;
00430                                    bli->bli_id = e->e_id;
00431                                    bli->bli_flag = 0;
00432                                    bli->bli_lock = lock;
00433                                    boi->boi_locks = bli;
00434                             }
00435                      }
00436               } else {
00437                      *ent = entry_dup( e );
00438                      bdb_cache_return_entry_rw(bdb, e, rw, &lock);
00439               }
00440        }
00441 
00442        Debug( LDAP_DEBUG_TRACE,
00443               "bdb_entry_get: rc=%d\n",
00444               rc, 0, 0 ); 
00445        return(rc);
00446 }