]> git.sur5r.net Git - openldap/blobdiff - servers/slapd/back-bdb2/txn.c
Change 'unsigned long len' to ber_len_t in get_filter()
[openldap] / servers / slapd / back-bdb2 / txn.c
index 335f9c549f6282a0497a05eb2f5113708188eae0..b2e2b0721f5ef9e0767931e65a68a0c2a2431240 100644 (file)
@@ -1,9 +1,14 @@
 /* txn.c - TP support functions of the bdb2 backend */
 
+#include "portable.h"
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
 #include "txn.h"
 
 
-void
+int
 bdb2i_txn_head_init( BDB2_TXN_HEAD *head )
 {
        int             dbFile;
@@ -16,55 +21,66 @@ bdb2i_txn_head_init( BDB2_TXN_HEAD *head )
 
                char fileName[MAXPATHLEN];
 
-               *fileNodeH = head->dbFileHandle[dbFile] =
-                       (BDB2_TXN_FILES *) ch_calloc( 1, sizeof( BDB2_TXN_FILES ));
+               *fileNodeH = (BDB2_TXN_FILES *) ch_calloc( 1, sizeof( BDB2_TXN_FILES ));
                if ( *fileNodeH == NULL ) {
 
                        Debug( LDAP_DEBUG_ANY, "bdb2i_txn_head_init(): out of memory!\n",
                                        0, 0, 0 );
-                       exit( 1 );
+                       return( 1 );
 
                }
 
-               sprintf( fileName, "%s%s", bdb2i_fixed_filenames[dbFile], LDBM_SUFFIX );
-               (*fileNodeH)->dbc_name = strdup( fileName );
+               sprintf( fileName, "%s%s", bdb2i_fixed_filenames[dbFile], BDB2_SUFFIX );
+               (*fileNodeH)->dbc_name = ch_strdup( fileName );
 
                fileNodeH = &(*fileNodeH)->next;
 
        }
 
+       /*  set defaults for checkpointing  */
+       head->txn_log  = BDB2_TXN_CHKP_MAX_LOG;
+       head->txn_time = BDB2_TXN_CHKP_MAX_TIME;
+
+       /*  initialize the txn_dirty_mutex  */
+       ldap_pvt_thread_mutex_init( &txn_dirty_mutex );
+
+       return 0;
 }
 
 
 static void
 bdb2i_init_db_file_cache( struct ldbminfo *li, BDB2_TXN_FILES *fileinfo )
 {
-       time_t      curtime;
        struct stat st;
        char        buf[MAXPATHLEN];
 
-       pthread_mutex_lock( &currenttime_mutex );
-       curtime = currenttime;
-       pthread_mutex_unlock( &currenttime_mutex );
-
        fileinfo->dbc_refcnt = 1;
-       fileinfo->dbc_lastref = curtime;
 
-       sprintf( buf, "%s%s%s", li->li_directory, DEFAULT_DIRSEP,
-                                       fileinfo->dbc_name );
+       sprintf( buf, "%s" LDAP_DIRSEP "%s",
+               li->li_directory, fileinfo->dbc_name );
+
        if ( stat( buf, &st ) == 0 ) {
                fileinfo->dbc_blksize = st.st_blksize;
        } else {
                fileinfo->dbc_blksize = DEFAULT_BLOCKSIZE;
        }
 
-       fileinfo->dbc_maxids = ( fileinfo->dbc_blksize / sizeof( ID )) - 2;
+       fileinfo->dbc_maxids = ( fileinfo->dbc_blksize / sizeof( ID )) -
+                       ID_BLOCK_IDS_OFFSET;
        fileinfo->dbc_maxindirect = ( SLAPD_LDBM_MIN_MAXIDS /
                fileinfo->dbc_maxids ) + 1;
 
 }
 
 
+/*  create a DB file cache entry for a specified index attribute
+       (if not already done); the function is called during config
+       file read for all index'ed attributes; if "default" index with
+       a non-none selection is given, this is remembered for run-time
+       extension of the list of index files; the function is also
+       called before add or modify operations to check for putative
+       new "default" index files; at that time, files are also opened
+*/
 void
 bdb2i_txn_attr_config(
        struct ldbminfo  *li,
@@ -80,7 +96,7 @@ bdb2i_txn_attr_config(
                BDB2_TXN_FILES  **fileNodeH;
                char            fileName[MAXPATHLEN];
 
-               sprintf( fileName, "%s%s", attr,  LDBM_SUFFIX );
+               sprintf( fileName, "%s%s", attr,  BDB2_SUFFIX );
 
                /*  search for the end of the list or a node describing
                        the current attribute  */
@@ -116,15 +132,19 @@ bdb2i_txn_attr_config(
                                }
                        }
 
-                       p->dbc_name = strdup( fileName );
+                       p->dbc_name = ch_strdup( fileName );
 
                        /*  if requested for, we have to open the DB file  */
                        /*  BUT NOT "objectclass", 'cause that's a default index !  */
                        if ( open && strcasecmp( fileName, "objectclass" )) {
 
+                               /*  re-use filename to get the complete path  */
+                               sprintf( fileName, "%s" LDAP_DIRSEP "%s",
+                                                       li->li_directory, p->dbc_name );
+
                                /*  since we have an mpool, we should not define a cache size */
-                               p->dbc_db = ldbm_open_env( p->dbc_name, LDBM_WRCREAT,
-                                                       li->li_mode, 0, &li->li_db_env );
+                               p->dbc_db = bdb2i_db_open( fileName, DB_TYPE,
+                                                                       LDBM_WRCREAT, li->li_mode, 0 );
 
                                /*  if the files could not be opened, something is wrong;
                                        complain  */
@@ -154,17 +174,68 @@ bdb2i_txn_attr_config(
 }
 
 
-void
-bdb2i_txn_open_files( struct ldbminfo *li )
+/*  open the NEXTID file for read/write; if it does not exist,
+       create it (access to the file must be preceeded by a rewind)
+*/
+static int
+bdb2i_open_nextid( BackendDB *be )
+{
+       struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+       BDB2_TXN_HEAD   *head = &li->li_txn_head;
+       LDBM            db = NULL;
+       DB_INFO                 dbinfo;
+       char            fileName[MAXPATHLEN];
+
+       sprintf( fileName, "%s" LDAP_DIRSEP "%s",
+                               li->li_directory, NEXTID_NAME );
+
+       /*  try to open the file for read and write  */
+       memset( &dbinfo, 0, sizeof( dbinfo ));
+       dbinfo.db_pagesize  = DEFAULT_DB_PAGE_SIZE;
+       dbinfo.db_malloc    = ldbm_malloc;
+
+       (void) db_open( fileName, DB_RECNO, DB_CREATE | DB_THREAD,
+                                       li->li_mode, &bdb2i_dbEnv, &dbinfo, &db );
+
+       if ( db == NULL ) {
+
+                       Debug( LDAP_DEBUG_ANY,
+                               "bdb2i_open_nextid: could not open \"%s\"\n",
+                               NEXTID_NAME, 0, 0 );
+                       return( -1 );
+
+       }
+
+       /*  the file is open for read/write  */
+       head->nextidFile = db;
+
+       return( 0 );
+}
+
+
+/*  open all DB during startup of the backend (necessary due to TP)
+       additional files may be opened during slapd life-time due to
+       default indexes (must be configured in slapd.conf;
+       see bdb2i_txn_attr_config)
+       also, set the counter and timer for TP checkpointing
+*/
+int
+bdb2i_txn_open_files( BackendDB *be )
 {
+       struct ldbminfo *li = (struct ldbminfo *) be->be_private;
        BDB2_TXN_HEAD   *head = &li->li_txn_head;
        BDB2_TXN_FILES  *dbFile;
+       int             rc;
 
        for ( dbFile = head->dbFiles; dbFile; dbFile = dbFile->next ) {
+               char   fileName[MAXPATHLEN];
+
+               sprintf( fileName, "%s" LDAP_DIRSEP "%s",
+                                       li->li_directory, dbFile->dbc_name );
 
                /*  since we have an mpool, we should not define a cache size */
-               dbFile->dbc_db = ldbm_open_env( dbFile->dbc_name, LDBM_WRCREAT,
-                                                       li->li_mode, 0, &li->li_db_env );
+               dbFile->dbc_db = bdb2i_db_open( fileName, DB_TYPE,
+                                                       LDBM_WRCREAT, li->li_mode, 0 );
 
                /*  if the files could not be opened, something is wrong; complain  */
                if ( dbFile->dbc_db == NULL ) {
@@ -172,7 +243,7 @@ bdb2i_txn_open_files( struct ldbminfo *li )
                        Debug( LDAP_DEBUG_ANY,
                                "bdb2i_txn_open_files(): couldn't open file \"%s\" -- FATAL.\n",
                                dbFile->dbc_name, 0, 0 );
-                       exit( 1 );
+                       return( -1 );
 
                }
 
@@ -184,22 +255,38 @@ bdb2i_txn_open_files( struct ldbminfo *li )
 
        }
 
+       rc = bdb2i_open_nextid( be );
+
+       txn_max_pending_log  = head->txn_log;
+       txn_max_pending_time = head->txn_time;
+
+       return rc;
 }
 
 
+/*  close all DB files during shutdown of the backend  */
 void
-bdb2i_txn_close_files( BDB2_TXN_HEAD *head)
+bdb2i_txn_close_files( BackendDB *be )
 {
-       BDB2_TXN_FILES  *dbFile;
+       struct ldbminfo  *li = (struct ldbminfo *) be->be_private;
+       BDB2_TXN_HEAD    *head = &li->li_txn_head;
+       BDB2_TXN_FILES   *dbFile;
 
        for ( dbFile = head->dbFiles; dbFile; dbFile = dbFile->next ) {
 
                ldbm_close( dbFile->dbc_db );
 
        }
+
+       if ( head->nextidFile )
+               ldbm_close( head->nextidFile );
+
 }
 
 
+/*  get the db_cache structure associated with a specified
+       DB file (replaces the on-the-fly opening of files in cache_open()
+*/
 BDB2_TXN_FILES *
 bdb2i_get_db_file_cache( struct ldbminfo *li, char *name )
 {
@@ -207,8 +294,12 @@ bdb2i_get_db_file_cache( struct ldbminfo *li, char *name )
        BDB2_TXN_FILES *dbFile;
        int            dbFileNum;
 
+       Debug( LDAP_DEBUG_TRACE, "bdb2i_get_db_file_cache(): looking for file %s\n",
+                       name, 0, 0 );
+
        for ( dbFile = head->dbFiles; dbFile; dbFile = dbFile->next ) {
 
+               /*  we've got it  */
                if ( !strcasecmp( dbFile->dbc_name, name )) return( dbFile );
 
        }
@@ -225,19 +316,19 @@ bdb2i_get_db_file_cache( struct ldbminfo *li, char *name )
 /*  check for new attribute indexes, that might have been created
     during former runs of slapd  */
 /*  this is called during startup of the slapd server  */
-void
+int
 bdb2i_check_additional_attr_index( struct ldbminfo *li )
 {
        DIR            *datadir;
        struct dirent  *file;
 
        if ( ( datadir = opendir( li->li_directory ) ) == NULL ) {
-       /* if ( ( datadir = opendir( "/tmp" ) ) == NULL ) { */
+               int err = errno;
 
                Debug( LDAP_DEBUG_ANY,
        "bdb2i_check_additional_attr_index(): ERROR while opening datadir: %s\n",
-                               strerror( errno ), 0, 0 );
-               exit( 1 );
+                               strerror( err ), 0, 0 );
+               return( 1 );
 
        }
 
@@ -248,12 +339,12 @@ bdb2i_check_additional_attr_index( struct ldbminfo *li )
                strcpy( filename, file->d_name );
                namelen = strlen( filename );
 
-               if ( namelen > strlen( LDBM_SUFFIX )) {
+               if ( namelen > strlen( BDB2_SUFFIX )) {
 
-                       if ( !strcasecmp( filename + namelen - strlen( LDBM_SUFFIX ),
-                                                       LDBM_SUFFIX )) {
+                       if ( !strcasecmp( filename + namelen - strlen( BDB2_SUFFIX ),
+                                                       BDB2_SUFFIX )) {
 
-                               *(filename + namelen - strlen( LDBM_SUFFIX )) = '\0';
+                               *(filename + namelen - strlen( BDB2_SUFFIX )) = '\0';
                                bdb2i_txn_attr_config( li, filename, 0 );
 
                                Debug( LDAP_DEBUG_TRACE, "INDEX FILE: %s\n", filename, 0, 0 );
@@ -266,6 +357,7 @@ bdb2i_check_additional_attr_index( struct ldbminfo *li )
 
        closedir( datadir );
 
+       return 0;
 }
 
 
@@ -319,3 +411,407 @@ bdb2i_check_default_attr_index_mod( struct ldbminfo *li, LDAPModList *modlist )
 }
 
 
+/*  get the next ID from the NEXTID file  */
+ID
+bdb2i_get_nextid( BackendDB *be )
+{
+       struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+       BDB2_TXN_HEAD   *head = &li->li_txn_head;
+       ID                              id;
+       Datum                   key;
+       Datum                   data;
+       db_recno_t              rec = NEXTID_RECNO;
+
+       ldbm_datum_init( key );
+       ldbm_datum_init( data );
+
+       key.data = &rec;
+       key.size = sizeof( rec );
+
+       data = bdb2i_db_fetch( head->nextidFile, key );
+       if ( data.data == NULL ) {
+               Debug( LDAP_DEBUG_ANY,
+                       "next_id_read: could not get nextid from \"%s\"\n",
+                       NEXTID_NAME, 0, 0 );
+               return NOID;
+       }
+
+       id = atol( data.data );
+       ldbm_datum_free( head->nextidFile, data );
+
+       if ( id < 1 ) {
+               Debug( LDAP_DEBUG_ANY,
+                       "next_id_read %ld: return non-positive integer\n",
+                       id, 0, 0 );
+               return NOID;
+       }
+
+       return( id );
+}
+
+
+int
+bdb2i_put_nextid( BackendDB *be, ID id )
+{
+       struct ldbminfo *li = (struct ldbminfo *) be->be_private;
+       BDB2_TXN_HEAD   *head = &li->li_txn_head;
+       int                             rc, flags;
+       Datum                   key;
+       Datum                   data;
+       db_recno_t              rec = NEXTID_RECNO;
+       char                    buf[20];
+
+       sprintf( buf, "%ld\n", id );
+
+       ldbm_datum_init( key );
+       ldbm_datum_init( data );
+
+       key.data = &rec;
+       key.size = sizeof( rec );
+
+       data.data = &buf;
+       data.size = sizeof( buf );
+
+       flags = LDBM_REPLACE;
+       if ( li->li_dbcachewsync ) flags |= LDBM_SYNC;
+
+       if (( rc = bdb2i_db_store( head->nextidFile, key, data, flags )) != 0 ) {
+               Debug( LDAP_DEBUG_ANY, "next_id_write(%ld): store failed (%d)\n",
+                       id, rc, 0 );
+               return( -1 );
+       }
+
+       return( rc );
+}
+
+
+/*  BDB2 backend-private functions of libldbm  */
+LDBM
+bdb2i_db_open(
+       char *name,
+       int type,
+       int rw,
+       int mode,
+       int dbcachesize )
+{
+       LDBM            ret = NULL;
+       DB_INFO         dbinfo;
+
+       memset( &dbinfo, 0, sizeof( dbinfo ));
+       if ( bdb2i_dbEnv.mp_info == NULL )
+               dbinfo.db_cachesize = dbcachesize;
+       dbinfo.db_pagesize  = DEFAULT_DB_PAGE_SIZE;
+       dbinfo.db_malloc    = ldbm_malloc;
+
+       (void) db_open( name, type, rw, mode, &bdb2i_dbEnv, &dbinfo, &ret );
+
+       return( ret );
+}
+
+
+int
+bdb2i_db_store( LDBM ldbm, Datum key, Datum data, int flags )
+{
+       int     rc;
+
+       rc = (*ldbm->put)( ldbm, txnid, &key, &data, flags & ~LDBM_SYNC );
+       rc = (-1 ) * rc;
+
+       if ( txnid != NULL ) {
+
+               /*  if the store was OK, set the dirty flag,
+                       otherwise set the abort flag  */
+               if ( rc == 0 ) {
+
+                       txn_dirty = 1;
+
+               } else {
+
+                       Debug( LDAP_DEBUG_ANY,
+                               "bdb2i_db_store: transaction failed: aborted.\n",
+                               0, 0, 0 );
+                       txn_do_abort = 1;
+
+               }
+       }
+
+       return( rc );
+}
+
+
+int
+bdb2i_db_delete( LDBM ldbm, Datum key )
+{
+       int     rc;
+
+       rc = (*ldbm->del)( ldbm, txnid, &key, 0 );
+       rc = (-1 ) * rc;
+
+       if ( txnid != NULL ) {
+
+               /*  if the delete was OK, set the dirty flag,
+                       otherwise set the abort flag  */
+               if ( rc == 0 ) {
+
+                       txn_dirty = 1;
+
+               } else {
+
+                       Debug( LDAP_DEBUG_ANY,
+                               "bdb2i_db_delete: transaction failed: aborted.\n",
+                               0, 0, 0 );
+                       txn_do_abort = 1;
+
+               }
+       }
+
+       return( rc );
+}
+
+
+Datum
+bdb2i_db_fetch( LDBM ldbm, Datum key )
+{
+       Datum   data;
+       int     rc;
+
+       ldbm_datum_init( data );
+       data.flags = DB_DBT_MALLOC;
+
+       if ( (rc = (*ldbm->get)( ldbm, txnid, &key, &data, 0 )) != 0 ) {
+               if (( txnid != NULL ) && ( rc != DB_NOTFOUND )) {
+
+                       Debug( LDAP_DEBUG_ANY,
+                               "bdb2i_db_fetch: transaction failed: aborted.\n",
+                               0, 0, 0 );
+                       txn_do_abort = 1;
+
+               }
+               if ( data.dptr ) free( data.dptr );
+               data.dptr = NULL;
+               data.dsize = 0;
+       }
+
+       return( data );
+}
+
+
+Datum
+bdb2i_db_firstkey( LDBM ldbm, DBC **dbch )
+{
+       Datum   key, data;
+       int             rc;
+       DBC             *dbci;
+
+       ldbm_datum_init( key );
+       ldbm_datum_init( data );
+
+       key.flags = data.flags = DB_DBT_MALLOC;
+
+#if defined( DB_VERSION_MAJOR ) && defined( DB_VERSION_MINOR ) && \
+   DB_VERSION_MAJOR == 2 && DB_VERSION_MINOR < 6
+
+       if ( (*ldbm->cursor)( ldbm, txnid, &dbci ))
+
+#else
+
+       if ( (*ldbm->cursor)( ldbm, txnid, &dbci, 0 ))
+
+#endif
+       {
+               if ( txnid != NULL ) {
+
+                       Debug( LDAP_DEBUG_ANY,
+                               "bdb2i_db_firstkey: transaction failed: aborted.\n",
+                               0, 0, 0 );
+                       txn_do_abort = 1;
+
+               }
+               key.flags = 0;
+               return( key );
+       } else {
+               *dbch = dbci;
+               if ( (*dbci->c_get)( dbci, &key, &data, DB_NEXT ) == 0 ) {
+                       ldbm_datum_free( ldbm, data );
+               } else {
+                       if ( txnid != NULL ) {
+
+                               Debug( LDAP_DEBUG_ANY,
+                                       "bdb2i_db_firstkey: transaction failed: aborted.\n",
+                                       0, 0, 0 );
+                               txn_do_abort = 1;
+
+                       }
+                       ldbm_datum_free( ldbm, key );
+                       key.flags = 0;
+                       key.dptr = NULL;
+                       key.dsize = 0;
+               }
+       }
+
+       return( key );
+}
+
+
+Datum
+bdb2i_db_nextkey( LDBM ldbm, Datum key, DBC *dbcp )
+{
+       Datum   data;
+       int             rc;
+
+       ldbm_datum_init( data );
+       ldbm_datum_free( ldbm, key );
+       key.flags = data.flags = DB_DBT_MALLOC;
+
+       if ( (*dbcp->c_get)( dbcp, &key, &data, DB_NEXT ) == 0 ) {
+               ldbm_datum_free( ldbm, data );
+       } else {
+               if ( txnid != NULL ) {
+
+                       Debug( LDAP_DEBUG_ANY,
+                               "bdb2i_db_nextkey: transaction failed: aborted.\n",
+                               0, 0, 0 );
+                       txn_do_abort = 1;
+
+               }
+               key.flags = 0;
+               key.dptr = NULL;
+               key.dsize = 0;
+       }
+
+       return( key );
+}
+
+
+/*  Transaction control of write access  */
+/*  Since these functions are only used by one writer at a time,
+       we do not have any concurrency (locking) problem  */
+
+/*  initialize a new transaction  */
+int
+bdb2i_start_transction( DB_TXNMGR *txmgr )
+{
+       int             rc;
+
+       txnid        = NULL;
+       txn_do_abort = 0;
+
+       if (( rc = txn_begin( txmgr, NULL, &txnid )) != 0 ) {
+               int err = errno;
+               Debug( LDAP_DEBUG_ANY, "bdb2i_start_transction failed: %d: errno=%s\n",
+                                       rc, strerror( err ), 0 );
+
+               if ( txnid != NULL )
+                       (void) txn_abort( txnid );
+               return( -1 );
+       }
+
+       Debug( LDAP_DEBUG_TRACE,
+                       "bdb2i_start_transaction: transaction started.\n",
+                       0, 0, 0 );
+
+       return( 0 );
+}
+
+
+/*  finish the transaction  */
+int
+bdb2i_finish_transaction()
+{
+       int             rc = 0;
+
+       /*  if transaction was NOT selected, just return  */
+       if ( txnid == NULL ) return( 0 );
+
+       /*  if nothing was wrong so far, we can try to commit the transaction  */
+       /*  complain, if the commit fails  */
+       if (( txn_do_abort == 0 ) && ( txn_commit( txnid )) != 0 ) {
+               Debug( LDAP_DEBUG_ANY,
+                       "bdb2i_finish_transaction: transaction commit failed: aborted.\n",
+                       0, 0, 0 );
+               txn_do_abort = 1;
+       }
+
+       /*  if anything went wrong, we have to abort the transaction  */
+       if ( txn_do_abort ) {
+               Debug( LDAP_DEBUG_ANY,
+                       "bdb2i_finish_transaction: transaction aborted.\n",
+                       0, 0, 0 );
+               (void) txn_abort( txnid );
+               rc = -1;
+       } else {
+               Debug( LDAP_DEBUG_TRACE,
+                       "bdb2i_finish_transaction: transaction commited.\n",
+                       0, 0, 0 );
+       }
+
+       /*  XXX do NOT free the txnid memory !!!  */
+       txnid        = NULL;
+       txn_do_abort = 0;
+
+       return( rc );
+}
+
+
+/*  set a checkpoint
+       either forced (during shutdown) or when logsize or time are exceeded
+       (is called by reader and writer, so protect txn_dirty)
+*/
+int
+bdb2i_set_txn_checkpoint( DB_TXNMGR *txmgr, int forced )
+{
+       int   rc = 0;
+
+       /*  set dirty mutex  */
+       ldap_pvt_thread_mutex_lock( &txn_dirty_mutex );
+
+       if ( txn_dirty ) {
+               int  rc;
+               u_int32_t   logsize;
+               u_int32_t   mins;
+               time_t      now;
+
+               logsize = forced ? (u_int32_t) 0 : txn_max_pending_log;
+               mins    = forced ? (u_int32_t) 0 : txn_max_pending_time;
+
+               now = slap_get_time();
+
+               rc = txn_checkpoint( txmgr, logsize, mins );
+
+               /*  if checkpointing was successful, reset txn_dirty  */
+               if ( rc == 0 ) {
+                       DB_TXN_STAT  *statp = NULL;
+
+                       /*  check whether the checkpoint was actually written;
+                               if so, unset the txn_dirty flag  */
+                       if (( rc = txn_stat( txmgr, &statp, ldbm_malloc )) == 0 ) {
+
+                               if ( statp && ( statp->st_time_ckp >= now )) {
+
+                                       Debug( LDAP_DEBUG_TRACE,
+                                               "bdb2i_set_txn_checkpoint succeded.\n",
+                                               0, 0, 0 );
+                                       txn_dirty = 0;
+
+                               }
+
+                               if ( statp ) free( statp );
+
+                       } else {
+                               Debug( LDAP_DEBUG_ANY,
+                                               "bdb2i_set_txn_checkpoint: txn_stat failed: %d\n",
+                                               rc, 0, 0 );
+                       }
+               } else {
+                       Debug( LDAP_DEBUG_ANY, "bdb2i_set_txn_checkpoint failed: %d\n",
+                                       rc, 0, 0 );
+               }
+       }
+
+       /*  release dirty mutex  */
+       ldap_pvt_thread_mutex_unlock( &txn_dirty_mutex );
+
+       return( rc );
+}
+
+