Example #1
0
/*
 * Initialize ENTRYID and USN sequence.
 */
static
DWORD
MDBInitSequence(
    PVDIR_MDB_DB pDB,
    VDIR_DB      mdbDbi
    )
{
    DWORD           dwError = 0;
    PVDIR_DB_TXN    pTxn = NULL;
    VDIR_DB_DBT     key = {0};
    VDIR_DB_DBT     value = {0};
    unsigned char   EidBytes[sizeof( ENTRYID )] = {0};
    ENTRYID         initEIDValue = ENTRY_ID_SEQ_INITIAL_VALUE;
    ENTRYID         initUNSValue = USN_SEQ_INITIAL_VALUE;

    assert(pDB);

    dwError = mdb_txn_begin( pDB->mdbEnv, NULL, BE_DB_FLAGS_ZERO, &pTxn );
    BAIL_ON_VMDIR_ERROR(dwError);

    key.mv_data = &EidBytes[0];
    MDBEntryIdToDBT(BE_MDB_ENTRYID_SEQ_KEY, &key);

    dwError =  mdb_get(pTxn, mdbDbi, &key, &value);
    if (dwError == MDB_NOTFOUND)
    {
        // first time, initialize two sequence records
        value.mv_data = &initEIDValue;
        value.mv_size = sizeof(initEIDValue);

        // set entryid sequence record
        dwError = mdb_put(pTxn, mdbDbi, &key, &value,  MDB_NOOVERWRITE);
        BAIL_ON_VMDIR_ERROR(dwError);

        MDBEntryIdToDBT(BE_MDB_USN_SEQ_KEY, &key);
        value.mv_data = &initUNSValue;
        value.mv_size = sizeof(initUNSValue);

        // set usn sequence record
        dwError = mdb_put(pTxn, mdbDbi, &key, &value, MDB_NOOVERWRITE);
        BAIL_ON_VMDIR_ERROR(dwError);
    }

    dwError = mdb_txn_commit(pTxn);
    pTxn = NULL;
    BAIL_ON_VMDIR_ERROR(dwError);

cleanup:

    return dwError;

error:

    if (pTxn)
    {
        mdb_txn_abort(pTxn);
    }

    goto cleanup;
}
Example #2
0
// To get the current max ENTRYID
DWORD
VmDirMDBMaxEntryId(
    PVDIR_BACKEND_CTX   pBECtx,
    ENTRYID*            pEId)
{
    DWORD           dwError = 0;
    PVDIR_DB_TXN    pTxn = NULL;
    MDB_val         key = {0};
    MDB_val         value  = {0};
    unsigned char   EIDBytes[sizeof( ENTRYID )] = {0};

    assert(pBECtx && pEId);

    dwError = mdb_txn_begin( gVdirMdbGlobals.mdbEnv, NULL, MDB_RDONLY, &pTxn );
    BAIL_ON_VMDIR_ERROR(dwError);

    key.mv_data = &EIDBytes[0];
    MDBEntryIdToDBT(BE_MDB_ENTRYID_SEQ_KEY, &key);

    dwError =  mdb_get(pTxn, gVdirMdbGlobals.mdbSeqDBi, &key, &value);
    BAIL_ON_VMDIR_ERROR(dwError);

    dwError = mdb_txn_commit(pTxn);
    pTxn = NULL;
    BAIL_ON_VMDIR_ERROR(dwError);

    assert(value.mv_size == sizeof(ENTRYID));
    *pEId = *((ENTRYID*)value.mv_data);

cleanup:

    return dwError;

error:

    if (pTxn)
    {
        mdb_txn_abort(pTxn);
    }

    VMDIR_LOG_ERROR( VMDIR_LOG_MASK_ALL,
            "VmDirMDBMaxEntryId: failed with error (%d),(%s)",
             dwError, mdb_strerror(dwError) );

    dwError = MDBToBackendError(dwError, 0, ERROR_BACKEND_ERROR, pBECtx, "MaxEntryId");

    goto cleanup;
}
Example #3
0
/* MdbEIdToEntry: For a given entry ID, reads an entry from the entry DB.
 *
 * Returns: BE error - BACKEND_ERROR, BACKEND OPERATIONS, BACKEND_ENTRY_NOTFOUND
 *
 */
DWORD
VmDirMDBEIdToEntry(
    PVDIR_BACKEND_CTX           pBECtx,
    PVDIR_SCHEMA_CTX            pSchemaCtx,
    ENTRYID                     eId,
    PVDIR_ENTRY                 pEntry,
    VDIR_BACKEND_ENTRY_LOCKTYPE entryLockType)
{
    DWORD           dwError = 0;
    VDIR_DB         mdbDBi = 0;
    PVDIR_DB_TXN    pTxn = NULL;
    VDIR_DB_DBT     key = {0};
    VDIR_DB_DBT     value = {0};
    unsigned char   eIdBytes[sizeof( ENTRYID )] = {0};
    unsigned char*  pszBlob = NULL;

    assert(pBECtx && pBECtx->pBEPrivate && pSchemaCtx && pEntry);

    pTxn = (PVDIR_DB_TXN)pBECtx->pBEPrivate;

    mdbDBi = gVdirMdbGlobals.mdbEntryDB.pMdbDataFiles[0].mdbDBi;

    // Set key
    key.mv_data = &eIdBytes[0];
    MDBEntryIdToDBT(eId, &key);

    if ((dwError = mdb_get(pTxn, mdbDBi, &key, &value) ) != 0)
    {
        dwError = MDBToBackendError(dwError, MDB_NOTFOUND, ERROR_BACKEND_ENTRY_NOTFOUND, pBECtx, "EIDToEntry");
        BAIL_ON_VMDIR_ERROR( dwError );
    }

    if ((dwError = VmDirAllocateMemory( value.mv_size, (PVOID *)&pszBlob)) != 0)
    {
        dwError = ERROR_BACKEND_OPERATIONS;
        BAIL_ON_VMDIR_ERROR( dwError );
    }

    if ((dwError = VmDirCopyMemory(pszBlob, value.mv_size, value.mv_data, value.mv_size)) != 0)
    {
        dwError = ERROR_BACKEND_OPERATIONS;
        BAIL_ON_VMDIR_ERROR( dwError );
    }

    // encodedEntry takes over pszBlob
    pEntry->encodedEntry = pszBlob;
    pszBlob = NULL;

    pEntry->eId = eId;
    dwError = VmDirDecodeEntry(pSchemaCtx, pEntry );
    BAIL_ON_VMDIR_ERROR(dwError);

cleanup:

    return dwError;

error:

    VMDIR_LOG_ERROR( LDAP_DEBUG_BACKEND, "VmDirMDBEIdToEntry, eid(%u) failed (%u)", eId, dwError);

    VMDIR_SAFE_FREE_MEMORY(pszBlob);
    VmDirFreeEntryContent( pEntry );

    VMDIR_SET_BACKEND_ERROR(dwError);   // if dwError no in BE space, set to ERROR_BACKEND_ERROR

    goto cleanup;
}
Example #4
0
/* MdbAddEntry: Creates an entry in the MDB DBs.
 *
 * Returns: BE error codes.
 *
 */
DWORD
VmDirMDBAddEntry(
    PVDIR_BACKEND_CTX   pBECtx,
    PVDIR_ENTRY         pEntry)
{
    DWORD             dwError = 0;
    ENTRYID           entryId = 0;
    VDIR_DB_TXN*      pTxn = NULL;
    VDIR_BERVALUE     encodedEntry = VDIR_BERVALUE_INIT;
    VDIR_ATTRIBUTE *  nextAttr = NULL;

    assert( pEntry && pBECtx && pBECtx->pBEPrivate );

    pTxn = (PVDIR_DB_TXN)pBECtx->pBEPrivate;

    dwError = VmDirEncodeEntry( pEntry, &encodedEntry );
    BAIL_ON_VMDIR_ERROR(dwError);

    if (pEntry->eId != 0)    // Reserved entries have eId already
    {
        entryId = pEntry->eId;
    }
    else
    {
        VDIR_DB_DBT     EIDkey = {0};
        VDIR_DB_DBT     EIDvalue  = {0};
        unsigned char   EIDKeyBytes[sizeof( ENTRYID )] = {0};
        unsigned char   EIDValueBytes[sizeof( ENTRYID )] = {0};

        EIDkey.mv_data = &EIDKeyBytes[0];
        MDBEntryIdToDBT(BE_MDB_ENTRYID_SEQ_KEY, &EIDkey);

        dwError =  mdb_get(pTxn, gVdirMdbGlobals.mdbSeqDBi, &EIDkey, &EIDvalue);
        BAIL_ON_VMDIR_ERROR(dwError);

        assert( EIDvalue.mv_size == sizeof(ENTRYID) );
        entryId = *((ENTRYID*)EIDvalue.mv_data);

        *((ENTRYID*)&EIDValueBytes[0]) = entryId + 1;
        EIDvalue.mv_data = &EIDValueBytes[0];
        EIDvalue.mv_size = sizeof(ENTRYID);

        dwError =  mdb_put(pTxn, gVdirMdbGlobals.mdbSeqDBi, &EIDkey, &EIDvalue, BE_DB_FLAGS_ZERO);
        BAIL_ON_VMDIR_ERROR(dwError);
    }

    assert( entryId > 0 );

    if ((dwError = MDBCreateParentIdIndex(pBECtx, &(pEntry->pdn), entryId)) != 0)
    {
        dwError = MDBToBackendError(dwError, ERROR_BACKEND_ENTRY_NOTFOUND,
                                    ERROR_BACKEND_PARENT_NOTFOUND, pBECtx, "CreateParentIdIndex");
        BAIL_ON_VMDIR_ERROR( dwError );
    }

    // Update DN index first. this make sure we always return ERROR_BACKEND_ENTRY_EXISTS in such case.
    for (nextAttr = pEntry->attrs; nextAttr != NULL; nextAttr = nextAttr->next)
    {
        if (VmDirStringCompareA(nextAttr->type.lberbv.bv_val, ATTR_DN, FALSE) == 0)
        {
            // make sure we store normalized DN value.
            dwError = VmDirNormalizeDN( &(nextAttr->vals[0]), pEntry->pSchemaCtx );
            BAIL_ON_VMDIR_ERROR(dwError);

            if ((dwError = MdbUpdateIndicesForAttr( pTxn, &(nextAttr->type), nextAttr->vals, nextAttr->numVals,
                                                    entryId, BE_INDEX_OP_TYPE_CREATE)) != 0)
            {
                dwError = MDBToBackendError( dwError,
                                             MDB_KEYEXIST,
                                             ERROR_BACKEND_ENTRY_EXISTS,
                                             pBECtx,
                                             VDIR_SAFE_STRING(nextAttr->vals[0].bvnorm_val));
                BAIL_ON_VMDIR_ERROR( dwError );
            }
            if ((dwError = MdbUpdateAttrMetaData( pTxn, nextAttr, entryId, BE_INDEX_OP_TYPE_CREATE )) != 0)
            {
                dwError = MDBToBackendError(dwError, 0, ERROR_BACKEND_ERROR, pBECtx, "UpdateDNAttrMetaData");
                BAIL_ON_VMDIR_ERROR( dwError );
            }
        }
    }

    // Update remaining indices
    for (nextAttr = pEntry->attrs; nextAttr != NULL; nextAttr = nextAttr->next)
    {
        if (VmDirStringCompareA(nextAttr->type.lberbv.bv_val, ATTR_DN, FALSE) != 0)
        {
            if ((dwError = MdbUpdateIndicesForAttr( pTxn, &(nextAttr->type), nextAttr->vals, nextAttr->numVals,
                                                    entryId, BE_INDEX_OP_TYPE_CREATE)) != 0)
            {
                dwError = MDBToBackendError( dwError,
                                             MDB_KEYEXIST,
                                             ERROR_BACKEND_CONSTRAINT,
                                             pBECtx,
                                             VDIR_SAFE_STRING(nextAttr->type.lberbv.bv_val));
                BAIL_ON_VMDIR_ERROR( dwError );
            }
            if ((dwError = MdbUpdateAttrMetaData( pTxn, nextAttr, entryId, BE_INDEX_OP_TYPE_CREATE )) != 0)
            {
                dwError = MDBToBackendError(dwError, 0, ERROR_BACKEND_ERROR, pBECtx,
                                            VDIR_SAFE_STRING(nextAttr->type.lberbv.bv_val));
                BAIL_ON_VMDIR_ERROR( dwError );
            }
        }
    }

    // Update entry/blob database
    if ((dwError = MdbCreateEIDIndex(pTxn, entryId, &encodedEntry, TRUE /* 1st time new entry creation */)) != 0)
    {
        dwError = MDBToBackendError(dwError, MDB_KEYEXIST, ERROR_BACKEND_CONSTRAINT, pBECtx, "CreateEIDIndex");
        BAIL_ON_VMDIR_ERROR( dwError );
    }

cleanup:

    VMDIR_SAFE_FREE_MEMORY( encodedEntry.lberbv.bv_val );

    return dwError;

error:
    // TODO set pBECtx->pszBEErrorMsg?
    VMDIR_LOG_ERROR( LDAP_DEBUG_BACKEND, "BEAddEntry DN (%s),  (%u)(%s)", VDIR_SAFE_STRING(pEntry->dn.lberbv.bv_val),
                              dwError, VDIR_SAFE_STRING(pBECtx->pszBEErrorMsg));

    VMDIR_SET_BACKEND_ERROR(dwError);   // if dwError no in BE space, set to ERROR_BACKEND_ERROR

    goto cleanup;
}
Example #5
0
/*
 * Get the next available USN number.
 */
DWORD
VmDirMDBGetNextUSN(
    PVDIR_BACKEND_CTX   pBECtx,
    USN *               pUsn)
{
    DWORD           dwError = 0;
    PVDIR_DB_TXN    pTxn = NULL;
    PVDIR_DB_TXN    pLocalTxn = NULL;
    VDIR_DB_DBT     key = {0};
    VDIR_DB_DBT     value  = {0};
    unsigned char   USNKeyBytes[sizeof( USN )] = {0};
    unsigned char   USNValueBytes[sizeof( USN )] = {0};
    USN             localUSN = 0;
    BOOLEAN         bRevertUSN = FALSE;
    BOOLEAN         bUnsetMaxUSN = TRUE;

    assert( pBECtx && pUsn );

    pTxn = (PVDIR_DB_TXN)pBECtx->pBEPrivate;
    if (pTxn)
    {
        pLocalTxn = pTxn;
    }
    else
    {
        dwError = mdb_txn_begin( gVdirMdbGlobals.mdbEnv, BE_DB_PARENT_TXN_NULL, BE_DB_FLAGS_ZERO, &pLocalTxn );
        BAIL_ON_VMDIR_ERROR(dwError);
    }

    key.mv_data = &USNKeyBytes[0];
    MDBEntryIdToDBT(BE_MDB_USN_SEQ_KEY, &key);

    dwError =  mdb_get(pLocalTxn, gVdirMdbGlobals.mdbSeqDBi, &key, &value);
    BAIL_ON_VMDIR_ERROR(dwError);

    assert( value.mv_size == sizeof(USN) );
    localUSN = *((USN*)value.mv_data);

    *((USN*)&USNValueBytes[0]) = localUSN + 1;
    value.mv_size = sizeof(USN);
    value.mv_data = &USNValueBytes[0];

    dwError =  mdb_put(pLocalTxn, gVdirMdbGlobals.mdbSeqDBi, &key, &value, BE_DB_FLAGS_ZERO);
    BAIL_ON_VMDIR_ERROR(dwError);

    if (pBECtx->wTxnUSN == 0)
    {   // capture and set outstanding USN within txn scope
        pBECtx->wTxnUSN = localUSN;

        dwError = VmDirBackendAddOutstandingUSN( pBECtx );
        BAIL_ON_VMDIR_ERROR(dwError);
        bRevertUSN = TRUE;
    }
    else
    {   // if BECtx has wTxnUSN already (nested txn), it should be smaller than new USN here.
        if (pBECtx->wTxnUSN >= localUSN)
        {
            VMDIR_LOG_ERROR( VMDIR_LOG_MASK_ALL, "nested OP/TXN USN ordering logic error: (%ld)(%ld)",
                      pBECtx->wTxnUSN, localUSN );
            dwError = LDAP_OPERATIONS_ERROR;
            BAIL_ON_VMDIR_ERROR(dwError);
        }
        VmDirBackendSetMaxOutstandingUSN(pBECtx, localUSN);
        bUnsetMaxUSN = TRUE;
    }

    if (pLocalTxn != pTxn)
    {
        dwError = mdb_txn_commit(pLocalTxn);
        pLocalTxn = NULL;
        BAIL_ON_VMDIR_ERROR(dwError);
    }

    *pUsn = localUSN;

cleanup:

    return dwError;

error:

    if (bUnsetMaxUSN)
    {
        VmDirBackendSetMaxOutstandingUSN(pBECtx, localUSN - 1);
    }

    if (bRevertUSN)
    {
        VmDirBackendRemoveOutstandingUSN( pBECtx );
        pBECtx->wTxnUSN = 0;
    }

    if (pLocalTxn && pLocalTxn != pTxn)
    {
        mdb_txn_abort(pLocalTxn);
    }

    VMDIR_LOG_ERROR( VMDIR_LOG_MASK_ALL,
            "VmDirMDBGetNextUSN: failed with error (%d),(%s)",
             dwError, mdb_strerror(dwError) );

    dwError = MDBToBackendError(dwError, 0, ERROR_BACKEND_ERROR, pBECtx, "GetNextUSN");

    goto cleanup;
}
Example #6
0
// To get the current max ENTRYID
// Entry database is separated from log databases.
DWORD
VmDirMDBMaxEntryId(
    PVDIR_BACKEND_INTERFACE pBE,
    ENTRYID*                pEId
    )
{
    DWORD           dwError = 0;
    VDIR_DB         mdbDBi = 0;
    PVDIR_DB_DBC    pCursor = NULL;
    MDB_val         key = {0};
    MDB_val         value  = {0};
    ENTRYID         eId = {0};
    unsigned char   EIDBytes[sizeof(ENTRYID)] = {0};
    VDIR_BACKEND_CTX    mdbBECtx = {0};
    BOOLEAN             bHasTxn = FALSE;

    PVDIR_MDB_DB pDB = VmDirSafeDBFromBE(pBE);

    assert(pEId && pDB);
    mdbBECtx.pBE = pBE;

    dwError = VmDirMDBTxnBegin(&mdbBECtx, VDIR_BACKEND_TXN_READ, &bHasTxn);
    BAIL_ON_VMDIR_ERROR(dwError);

    mdbDBi = pDB->mdbEntryDB.pMdbDataFiles[0].mdbDBi;

    dwError = mdb_cursor_open((PVDIR_DB_TXN)mdbBECtx.pBEPrivate, mdbDBi, &pCursor);
    BAIL_ON_VMDIR_ERROR(dwError);

    key.mv_data = &EIDBytes[0];

    if (pBE == VmDirBackendSelect(ALIAS_MAIN))
    {
       //Set cursor position equal or above LOG_ENTRY_EID_PREFIX
       // it would find the first entry id of raft log entry on legacy data
       // i.e. database creaded before we implemented a split log.
       eId = LOG_ENTRY_EID_PREFIX;
       MDBEntryIdToDBT(eId, &key);
       dwError = mdb_cursor_get(pCursor, &key, &value, MDB_SET_RANGE);
       if (dwError == MDB_NOTFOUND)
       {
           //mainDb was created after we implemented split log (no log entries in mainDb)
           // - simply find the last id which will be the last entry-id in normal entries.
           eId = 0;
           MDBEntryIdToDBT(eId, &key);
           dwError = mdb_cursor_get(pCursor, &key, &value, MDB_LAST);
           BAIL_ON_VMDIR_ERROR(dwError);

           MDBDBTToEntryId(&key, &eId);

       } else
       {
          BAIL_ON_VMDIR_ERROR(dwError);
          //Found an entry that is LOG_ENTRY_EID_PREFIX or above
          // - search backward, and the first one is the last normal entry
          do
          {
             dwError = mdb_cursor_get(pCursor, &key, &value, MDB_PREV);
             BAIL_ON_VMDIR_ERROR(dwError);

             MDBDBTToEntryId(&key, &eId);
          }
          while (eId >= LOG_ENTRY_EID_PREFIX);
       }
    } else
    {
       //For logDb, the last one is the log entry.
       eId = 0;
       MDBEntryIdToDBT(eId, &key);
       dwError = mdb_cursor_get(pCursor, &key, &value, MDB_LAST);
       BAIL_ON_VMDIR_ERROR(dwError);

       MDBDBTToEntryId(&key, &eId);
    }

    mdb_cursor_close(pCursor);
    pCursor = NULL;

    if (bHasTxn)
    {
        dwError = VmDirMDBTxnCommit(&mdbBECtx);
        bHasTxn = FALSE;
        BAIL_ON_VMDIR_ERROR(dwError);
    }

    *pEId = eId;

cleanup:
    return dwError;

error:
    VMDIR_LOG_ERROR( VMDIR_LOG_MASK_ALL,
            "VmDirMDBMaxEntryId: failed with error (%d),(%s)",
             dwError, mdb_strerror(dwError) );

    mdb_cursor_close(pCursor);
    if (bHasTxn)
    {
        VmDirMDBTxnAbort(&mdbBECtx);
    }
    VMDIR_SET_BACKEND_ERROR(dwError);
    goto cleanup;
}