/**@@@+++@@@@******************************************************************
**
** Microsoft Windows Media
** Copyright (C) Microsoft Corporation. All rights reserved.
**
***@@@---@@@@******************************************************************
*/


#include "drmhds_impl.c"


/* Lock Order: 
** - Slot: to lock a slot SHARED/EXCLUSIVE among other processes
** - CS:  to guard exclusive access to file handle and local stack allocator
**        among other threads
** - SRN: to lock the SRN SHARED/EXCLUSIVE among other processes
*/


#if _MULTI_THREADING_SUPPORT==1

    #define DEFINE_LOCK_VARS \
        DRM_BOOL fStoreLocked=FALSE;\
        DRM_BOOL fSRNLocked=FALSE;\
        DRM_BOOL fCSEntered=FALSE;

    #define ENTER_HDS_CS(p)  {\
        OEM_EnterCriticalSection(&((p)->oCS));\
        fCSEntered = TRUE;\
    }

    #define LEAVE_HDS_CS(p)  {\
        if ( fCSEntered )\
        {\
            OEM_LeaveCriticalSection(&((p)->oCS));\
            fCSEntered = FALSE;\
        }\
    }

    #define LOCK_SRN( hds, mode )\
        ChkDR(_HdsLockSRN((hds), (mode)))\
        fSRNLocked = TRUE;

    #define UNLOCK_SRN(hds)  {\
        if (fSRNLocked)\
        {\
            _HdsUnlockSRN((hds));\
            fSRNLocked = FALSE;\
        }\
    }
    
    #define ENTER_CS_AND_LOCK_SRN(hds, mode)\
        ENTER_HDS_CS((hds));\
        LOCK_SRN( (hds), (mode) );

    #define UNLOCK_SRN_AND_LEAVE_CS(hds)\
        UNLOCK_SRN((hds));\
        LEAVE_HDS_CS((hds));

    #define LOCK_STORE(hds, mode)\
        ChkDR( _HdsLockStore( (hds), (mode) ) );\
        fStoreLocked = TRUE;
    
    #define UNLOCK_STORE(hds) {\
        if ( fStoreLocked )\
        {\
            _HdsUnlockStore((hds));\
            fStoreLocked = FALSE;\
        }\
    }
    
#else

    #define DEFINE_LOCK_VARS
    #define ENTER_HDS_CS(p)
    #define LEAVE_HDS_CS(p)
    #define LOCK_SRN( hds, mode )
    #define UNLOCK_SRN(hds)
    #define ENTER_CS_AND_LOCK_SRN(hds, mode)
    #define UNLOCK_SRN_AND_LEAVE_CS(hds)
    #define LOCK_STORE(hds, mode)
    #define UNLOCK_STORE(hds)

#endif




/******************************************************************************
** File Flushing scheme:
*******************************************************************************
** For performance, the following file flushing scheme is applied:
** If _DATASTORE_WRITE_THRU is #define'd, all write operation will be flushed to 
** disk immediately. If not, only when expanding file or creating new namespace 
** will cause flush. Caller is responsible to call DRM_HDS_CommitStore or
** DRM_HDS_CommitNamespace to flush the store at higher level.
*******************************************************************************
*/



/******************************************************************************
** Store locking scheme:
*******************************************************************************
** Function             Access       CS   Lock (temp)      Lock (Persistent)
*******************************************************************************
** Init                 n/a          n/a  n/a              n/a
** Uninit               n/a          n/a  n/a              n/a
** Commit store         n/a           Y   n/a              n/a
** Cleanup store        file          Y   SRN Exclusive    n/a
** Checkpoint           SRN           N   SRN Exclusive    n/a
** Create store         file          Y   file Exclusive   n/a
** Open store           SRN           Y   SRN Shared       n/a
** Close store          n/a           Y   n/a              n/a
** Commit Namespace     n/a          n/a  n/a              n/a
** Create Namespace     SRN,BLOCKS    Y   SRN Exclusive    n/a
** Open Namespace       SRN,BLOCKS    Y   SRN Shared       n/a
** Close Namespace      n/a          n/a  n/a              n/a
** Delete Namespace     SRN,BLOCKS    Y   Store Exclusive  n/a
** Create Slot          SRN,BLOCKS    Y   SRN Exclusive    Slot Shared/Exclusive
** Open Slot            SRN,BLOCKS    Y   SRN Shared       Slot Shared/Exclusive
** Close Slot           n/a          n/a  n/a              n/a 
** Seek Slot            n/a          n/a  n/a              Slot Shared/Exclusive
** Read Slot            n/a          n/a  n/a              Slot Shared/Exclusive
** Write Slot           n/a          n/a  n/a              Slot Shared/Exclusive
** Resize Slot          SRN,BLOCKS    Y   SRN Exclusive    Slot Shared/Exclusive
** Delete Slot          SRN,BLOCKS    Y   SRN Exclusive
** Init enum Slot       SRN,BLOCKS    Y   SRN Shared       Slot Shared/Exclusive
** Enum next Slot       SRN,BLOCKS    Y   SRN Shared       Slot Shared/Exclusive
**
** Note:
** -  Store level locks (SHARED and EXCLUSIVE) are issued at this file
**    only.
** -  Block level locks (SHARED and EXCLUSIVE) are issued at hds_impl.c
**    as needed
*******************************************************************************
*/



/**********************************************************************
** API functions
***********************************************************************
*/


DRM_RESULT DRM_API DRM_HDS_Init( 
    IN OUT DRM_HDS_CONTEXT *pcontextHDS)
{
    DRM_RESULT   dr=DRM_SUCCESS;
    _HdsContext *pHDS=(_HdsContext*)pcontextHDS;

    ChkArg(pcontextHDS);

    ZEROMEM(pHDS, SIZEOF(_HdsContext));
    pHDS->fp = OEM_INVALID_HANDLE_VALUE;

#if _MULTI_THREADING_SUPPORT==1
    OEM_InitializeCriticalSection(&pHDS->oCS);
    pHDS->fCSInited = TRUE;
#endif

ErrorExit:
    return dr;
}



DRM_RESULT DRM_API DRM_HDS_Uninit( 
    IN OUT DRM_HDS_CONTEXT *pcontextHDS)
{
    DRM_RESULT   dr=DRM_SUCCESS;
    _HdsContext *pHDS=(_HdsContext*)pcontextHDS;

    ChkArg(pcontextHDS);

#if _MULTI_THREADING_SUPPORT==1
    if( pHDS->fCSInited )
    {
        /* delete ciritical section */
        OEM_DeleteCriticalSection(&pHDS->oCS);
        pHDS->fCSInited = FALSE;
    }
#endif

ErrorExit:
    return dr;
}




DRM_RESULT DRM_API DRM_HDS_CommitStore(
    IN DRM_HDS_CONTEXT *pcontextHDS )
{
    DRM_RESULT dr = DRM_SUCCESS;

    /*
    ** Note: 
    ** if _DATASTORE_WRITE_THRU is defined, file buffers are already flushed.
    ** There is no need to flush again.
    */

#if !_DATASTORE_WRITE_THRU
    _HdsContext *pHDS=(_HdsContext*)pcontextHDS;
    DEFINE_LOCK_VARS

    ChkArg(ISVALIDCONTEXT(pHDS, eHdsContextSignature));
    DRMASSERT(pcontextHDS != NULL
          &&  pHDS->fInited 
          &&  pHDS->fp != OEM_INVALID_HANDLE_VALUE);

    /* enter CS to get exclusive access to file handle */
    ENTER_HDS_CS(pHDS);
    
    if (!OEM_FlushFileBuffers(pHDS->fp))
    {
        dr = DRM_E_FILEWRITEERROR;
    }

ErrorExit:

    LEAVE_HDS_CS(pHDS);
#endif

    return dr;
}


DRM_RESULT DRM_API DRM_HDS_CleanupStore(
    IN DRM_HDS_CONTEXT *pcontextHDS,
    IN DRM_BOOL         fWait)
{
    DRM_RESULT   dr=DRM_SUCCESS;
    _HdsContext *pHDS = (_HdsContext*)pcontextHDS;
    DEFINE_LOCK_VARS

    ChkArg(ISVALIDCONTEXT(pHDS, eHdsContextSignature));
    DRMASSERT(pcontextHDS 
           && pHDS->fInited 
           && pHDS->fp!=OEM_INVALID_HANDLE_VALUE);

    /* 
    ** First, lock the store for EXCLUSIVE. So if there are any slots locked, 
    ** this call will fail, or wait until all locks are released
    */
    LOCK_STORE( pHDS, eDRM_HDS_LOCKEXCLUSIVE | (fWait ? eDRM_HDS_LOCKWAIT : 0) );
    ENTER_CS_AND_LOCK_SRN(pHDS, eDRM_HDS_LOCKEXCLUSIVE | (fWait ? eDRM_HDS_LOCKWAIT : 0) );

    dr = _HdsCleanupStore(pHDS);
    if ( DRM_SUCCEEDED(dr) )
    {
        if (!OEM_FlushFileBuffers(pHDS->fp))
        {
            dr = DRM_E_FILEWRITEERROR;
        }
    }
    
ErrorExit:
    UNLOCK_SRN_AND_LEAVE_CS(pHDS);
    UNLOCK_STORE(pHDS);
    return dr;
}


DRM_RESULT DRM_API DRM_HDS_CreateStore( 
    IN const DRM_WCHAR       *f_wszHdsName, 
    IN const DRM_DWORD        f_dwBlockSize,       /* block size is limited up to 64K */
    IN const DRM_HDSBLKNUM    f_eBlkNumType,
    IN OUT   DRM_HDS_CONTEXT *f_pcontextHDS,
    IN       DRM_BOOL         f_fTruncateExist)
{
   return DRM_HDS_CreateStore2( 
                f_wszHdsName, 
                f_dwBlockSize,
                f_eBlkNumType,
                f_pcontextHDS,
                f_fTruncateExist,
                0) ;
}


DRM_RESULT DRM_API DRM_HDS_CreateStore2( 
    IN const DRM_WCHAR       *wszHdsName, 
    IN const DRM_DWORD        dwBlockSize,       /* block size is limited up to 64K */
    IN const DRM_HDSBLKNUM    eBlkNumType,
    IN OUT   DRM_HDS_CONTEXT *pcontextHDS,
    IN       DRM_BOOL         fTruncateExist,
    IN const DRM_DWORD        nInitSizeinKB)
{
    DRM_RESULT      dr            = DRM_SUCCESS;    
    DRM_DWORD       nMinBlockSize = 0;
    _CommBlockHDR  *tmpBlock      = NULL;
    _ChildBlockHDR *tmpChild      = NULL;
    _HdsContext    *pHDS          = NULL;
    DRM_DWORD       i             = 0;
    DEFINE_LOCK_VARS

    /* Lock order:
    **   - CS
    **   - Whole Store
    */

    DRMASSERT(wszHdsName  != NULL
           && dwBlockSize != 0
           && pcontextHDS != NULL);
           
    DRMASSERT(eBlkNumType == eDRM_HDSBLKNUM_WORD 
           || eBlkNumType == eDRM_HDSBLKNUM_DWORD);

    /* check store name 
    ** Note: we expect the filename is NULL terminated.
    */
    for (i=0; ; i++)
    {
        if (wszHdsName[i] == g_wchNull)   /* empty filename is not allowed */
        {
            DRMASSERT(FALSE);
        }   
        if (wszHdsName[i] != g_wchSpace)
        {
            break;
        }
    }

    /* verify required min block size:
    ** SIZEOF generic header + SIZEOF child header + 1 Alloc table elem + 1 slotheader + SIZEOF datablock#
    */
    nMinBlockSize = SIZEOF(tmpBlock->File.bFileImage)        /* minimum _CommBlockHDR size */
                  + SIZEOF(tmpChild->File.bFileImage)        /* minimum _ChildBlockHDR size */
                  + (DRM_WORD)eBlkNumType                    /* 1 element in alloctable */
                  + SIZEOF(_SlotHeader)                      /* 1 _SlotHeader */
                  + SIZEOF(DRM_DWORD);                       /* Block num of _DataBlockHDR */
    if (dwBlockSize < nMinBlockSize)
    {
        TRACE(("Block size too small. Minimum size is %d.\n", nMinBlockSize));
        ChkDR(DRM_E_BUFFERTOOSMALL);    /* DRM_E_HDSBLOCKTOOSMALL */
    }

    {
        /* The few fields of RootNode is const, we cannot assign values 
        ** to them. Instead, we init values to a tmp and use memcpy to 
        ** the structure. 
        ** Note: the reason for const of the first 4 fields is to prevent 
        ** accidental alteration of the values in the program. If so, the 
        ** compiler will catch it.
        */
        _SRN tmpSRN;
        
        ZEROMEM(&tmpSRN, SIZEOF (tmpSRN));
        
        tmpSRN.dwSRNSize     = SIZEOF(_SRN); 
        tmpSRN.dwBlockSize   = dwBlockSize;
        tmpSRN.eBlockNumType = eBlkNumType; 
        
        pHDS=(_HdsContext*)pcontextHDS;
                
        MEMCPY(&pHDS->oSRN, &tmpSRN, SIZEOF(_SRN));
        ZEROMEM(pHDS->oSRN.bSRNHash, SIZEOF( pHDS->oSRN.bSRNHash ) );

        /* The following logic ensures:
        ** - if the store file exists, it is opened, locked exclusive and 
        **   truncated (if requested)
        ** - if the store file does not exist, it is created and locked exclusive.
        ** - All of the above are thread safe.
        */

        /* try Open the exist store */
        pHDS->fp = OEM_OpenFile(wszHdsName, OEM_GENERIC_READ|OEM_GENERIC_WRITE,
            OEM_FILE_SHARE_READ|OEM_FILE_SHARE_WRITE, OEM_OPEN_EXISTING, OEM_ATTRIBUTE_NORMAL);

        if (pHDS->fp == OEM_INVALID_HANDLE_VALUE)
        {
            /* store does not exist */
            pHDS->fp = OEM_OpenFile(wszHdsName, OEM_GENERIC_READ|OEM_GENERIC_WRITE,
                OEM_FILE_SHARE_READ|OEM_FILE_SHARE_WRITE, OEM_CREATE_NEW, OEM_ATTRIBUTE_NORMAL);
            if (pHDS->fp == OEM_INVALID_HANDLE_VALUE)
            {
                ChkDR(DRM_E_FILEWRITEERROR);
            }

            /* get EXCLUSIVE lock, do not wait */
            LOCK_STORE( pHDS, eDRM_HDS_LOCKEXCLUSIVE );
            ENTER_CS_AND_LOCK_SRN( pHDS, eDRM_HDS_LOCKEXCLUSIVE );
        }

        else if ( !fTruncateExist )
        {
            ChkDR(DRM_E_HDSFILEEXISTS);     /* found exists, do not truncate */
        }

        else
        {
            /* get EXCLUSIVE lock, do not wait */
            LOCK_STORE( pHDS, eDRM_HDS_LOCKEXCLUSIVE );
            ENTER_CS_AND_LOCK_SRN( pHDS, eDRM_HDS_LOCKEXCLUSIVE );
            
            /* truncate exist file */
            if (!OEM_SetFilePointer(pHDS->fp, 0, OEM_FILE_BEGIN, NULL))
            {
                ChkDR(DRM_E_FILESEEKERROR);
            }
            OEM_SetEndOfFile(pHDS->fp);
        }

        pHDS->fInited = TRUE;
        pHDS->eContextSignature = eHdsContextSignature;

        /* init internal stack info */
        pHDS->oHeap.pbStack = (DRM_BYTE*)&pHDS->oSRN + SIZEOF(_SRN);
        pHDS->oHeap.cbStack = pHDS->dwContextSize - SIZEOF(_HdsContext);
        pHDS->oHeap.nStackTop = 0;
        
        ChkDR(_HdsUpdateSRN(pHDS));
        if ( !OEM_FlushFileBuffers(pHDS->fp) )
        {
            dr = DRM_E_FILEWRITEERROR;
        }

        // prealloc file size
        if ( nInitSizeinKB > 0 )
        {
            /* ignore the error. */
            (void)_HdsPreAlloc( pHDS, nInitSizeinKB, TRUE, NULL);
        }
    }

ErrorExit:

    UNLOCK_SRN_AND_LEAVE_CS( pHDS );
    UNLOCK_STORE( pHDS );

    if ( pHDS != NULL )
    {
        if (pHDS->fp != OEM_INVALID_HANDLE_VALUE)
        {
            OEM_CloseFile(pHDS->fp);
        }
        
        /* we don't need it now, clear it */
        _ZeroHDSContextIgnoringCritSec(pHDS);
    }

    return dr;
}


DRM_RESULT DRM_API DRM_HDS_OpenStore( 
    IN const DRM_WCHAR       *f_wszHdsName, 
    OUT      DRM_HDS_CONTEXT *f_pcontextHDS )
{
    return DRM_HDS_OpenStore2(f_wszHdsName, f_pcontextHDS, 0);
}


DRM_RESULT DRM_API DRM_HDS_OpenStore2( 
    IN const DRM_WCHAR       *wszHdsName, 
       OUT   DRM_HDS_CONTEXT *pcontextHDS,
    IN const DRM_DWORD        nGrowbySizeinKB)
{
    DRM_RESULT     dr = DRM_SUCCESS;    
    _HdsContext   *pHDS=(_HdsContext *)pcontextHDS;
    _CommBlockHDR *tmpBlock=NULL;
    _DataBlockHDR *dataBlock=NULL;
    OEM_FILEHDL    fp=OEM_INVALID_HANDLE_VALUE;
    DEFINE_LOCK_VARS

    DRM_PROFILING_ENTER_SCOPE(L"DRM_HDS_OpenStore2", g_pwszEnteringFunction, DRM_PROFILING_DONT_START);

    DRMASSERT(wszHdsName && pcontextHDS);

    /* initialize context */
#ifdef _CHKHDS_    
    /* chkhds utility opens the store in read only mode */
    fp = OEM_OpenFile(wszHdsName, OEM_GENERIC_READ,OEM_FILE_SHARE_READ, 
        OEM_OPEN_EXISTING, OEM_ATTRIBUTE_NORMAL);
#else
    fp = OEM_OpenFile(wszHdsName, OEM_GENERIC_READ|OEM_GENERIC_WRITE,
        OEM_FILE_SHARE_READ|OEM_FILE_SHARE_WRITE, OEM_OPEN_EXISTING, OEM_ATTRIBUTE_NORMAL);
#endif
    if ( OEM_INVALID_HANDLE_VALUE==fp )
    {
        pHDS->fp = OEM_INVALID_HANDLE_VALUE;
        ChkDR(DRM_E_FILENOTFOUND);
    }

    _ZeroHDSContextIgnoringCritSec(pHDS);

    pHDS->fp                = fp;
    pHDS->dwContextSize     = HDS_CONTEXT_LENGTH(SIZEOF(_SRN));
    pHDS->fInited           = TRUE;
    pHDS->nGrowbySizeinKB   = nGrowbySizeinKB;
    pHDS->eContextSignature = eHdsContextSignature;

    /* Lock SRN SHARED among other processes */
    ENTER_CS_AND_LOCK_SRN(pHDS, (eDRM_HDS_LOCKSHARED | eDRM_HDS_LOCKWAIT));

    /* init internal stack info */
    pHDS->oHeap.pbStack = (DRM_BYTE*)&pHDS->oSRN + SIZEOF(_SRN);
    pHDS->oHeap.cbStack = pHDS->dwContextSize - SIZEOF(_HdsContext);
    pHDS->oHeap.nStackTop = 0;

    ChkDR(_HdsLoadSRN(pHDS));

    /* compute handy info */
    pHDS->nImagesize_FileBlockHDR = SIZEOF(tmpBlock->File._image.ui64TimeStamp)
                                  + SIZEOF(tmpBlock->File._image.bBlockHash)
                                  + SIZEOF(tmpBlock->File._image.nParentBlockNum)
                                  + 1; /* SIZEOF(tmpBlock->File._image.bBlockType) */;
    pHDS->nImagesize_DataBlockHDR = SIZEOF(dataBlock->File._image.nCascadingBlockNum);
    pHDS->nDataBlockPayloadPos    = pHDS->nImagesize_FileBlockHDR 
                                  + pHDS->nImagesize_DataBlockHDR;
    if( pHDS->oSRN.dwBlockSize <= pHDS->nDataBlockPayloadPos )
    {
        /* Not enough room in each block for any data */
        ChkDR(DRM_E_HDSINVALIDSTORE);
    }

    pHDS->nImagesize_DataBlockPayload = pHDS->oSRN.dwBlockSize - pHDS->nDataBlockPayloadPos;

ErrorExit:

    UNLOCK_SRN_AND_LEAVE_CS(pHDS);
    
    if (DRM_FAILED(dr))
    {
        if ( pHDS != NULL )
        {
            _ZeroHDSContextIgnoringCritSec(pHDS);
        }
        if ( fp != OEM_INVALID_HANDLE_VALUE)
        {
            OEM_CloseFile( fp );
        }
    }
    
    DRM_PROFILING_LEAVE_SCOPE(L"DRM_HDS_OpenStore2", g_pwszLeavingFunction);

    return dr;
}


/*
**
*/
DRM_RESULT DRM_API DRM_HDS_CloseStore( 
    IN DRM_HDS_CONTEXT *pcontextHDS )
{
    DRM_RESULT   dr = DRM_SUCCESS;
    _HdsContext *pHDS=(_HdsContext*)pcontextHDS;
    DEFINE_LOCK_VARS

    DRM_PROFILING_ENTER_SCOPE(L"DRM_HDS_CloseStore", g_pwszEnteringFunction, DRM_PROFILING_DONT_START);
    ChkArg(pHDS && ISVALIDCONTEXT(pHDS, eHdsContextSignature));
    DRMASSERT(pcontextHDS && pHDS->fInited);


    /* Lock Order: 
    ** - CS:  to guard exclusive access to file handle
    */
    ENTER_HDS_CS(pHDS);

    if ( pHDS->fp != OEM_INVALID_HANDLE_VALUE )
    {
        OEM_CloseFile( pHDS->fp );
        pHDS->fp = OEM_INVALID_HANDLE_VALUE;
    }
    
    _ZeroHDSContextIgnoringCritSec(pHDS);

ErrorExit:    
    LEAVE_HDS_CS(pHDS);
    DRM_PROFILING_LEAVE_SCOPE(L"DRM_HDS_CloseStore", g_pwszLeavingFunction);

    return dr;
}



/*
**
*/
DRM_RESULT DRM_API DRM_HDS_CommitNamespace(
    IN DRM_HDS_NAMESPACE_CONTEXT *pcontextNS)
{
    DRM_RESULT  dr = DRM_SUCCESS;
    _NsContext *pUserNsCFG = (_NsContext *)pcontextNS;

    ChkArg( pcontextNS != NULL  
        &&  ISVALIDCONTEXT(pUserNsCFG, eCfgContextSignature));

    /* we do not need to do any locking, DRM_HDS_CommitStore will do it 
    */
    ChkDR(DRM_HDS_CommitStore( (DRM_HDS_CONTEXT *)(pUserNsCFG->pHDS) ));

ErrorExit:

    return dr;
}




/******************************************************************************
** 
** Function :   DRM_HDS_OpenNamespace
** 
** Synopsis :   Opens/creates a namespace
** 
** Arguments :  pcontextHDS - HDS context
**              pNamespace  - Namespace identifier to be opened/created
**              eOpenMode   - eDRM_HDS_CREATE_IF_NEW / eDRM_HDS_OPEN_EXISTING
**              wMaxNumChildren - Number of children if a new namespace is to
**                                be created
**              pcontextNS  - Namespace context
** 
** Returns :    DRM_E_HDSNAMESPACENOTFOUND - if namespace is not found and 
**                  request is only to open existing existing namespace
** 
** Notes :      
** 
******************************************************************************/
DRM_RESULT DRM_API DRM_HDS_OpenNamespace(
    IN       DRM_HDS_CONTEXT           *pcontextHDS,    
    IN const DRM_HDS_NAMESPACE         *pNamespace,
    IN       DRM_HDS_OPENMODE           eOpenMode,
    IN       DRM_WORD                   wMaxNumChildren,
    OUT      DRM_HDS_NAMESPACE_CONTEXT *pcontextNS )
{
    DRM_RESULT   dr     = DRM_E_HDSNAMESPACENOTFOUND;
    _HdsContext *pHDS   = (_HdsContext*)pcontextHDS;
    DEFINE_LOCK_VARS
    DRM_PROFILING_ENTER_SCOPE(L"DRM_HDS_OpenNamespace", g_pwszEnteringFunction, DRM_PROFILING_DONT_START);

    DRMASSERT( ( eOpenMode & eDRM_HDS_CREATE_IF_NEW ) != 0
            || ( eOpenMode & eDRM_HDS_OPEN_EXISTING ) != 0);
    DRMASSERT( pcontextHDS != NULL );    

    /* Lock SRN EXCLUSIVE */
    ENTER_CS_AND_LOCK_SRN(pHDS, (eOpenMode | eDRM_HDS_LOCKEXCLUSIVE));

	dr = DRM_E_HDSNAMESPACENOTFOUND;
    if ( (pHDS->oSRN.nNsStoreRootBlockNum > 0) )
    {
        /* open existing */
        dr = _HdsOpenExistingNamespace(pcontextHDS, pNamespace, pcontextNS);
        if ( DRM_SUCCEEDED(dr) )
        {
            goto ErrorExit;
        }
    }

    /* namespace not found and caller ask to create new */
    if ( dr == DRM_E_HDSNAMESPACENOTFOUND  &&  (eOpenMode & eDRM_HDS_CREATE_IF_NEW) != 0 )
    {        
        ChkDR( _HdsCreateNamespace( 
                    pcontextHDS, 
                    pNamespace,
                    wMaxNumChildren,
                    (eOpenMode & eDRM_HDS_LOCKWAIT)!=0 ) );

        dr = _HdsOpenExistingNamespace(pcontextHDS, pNamespace, pcontextNS);
    }

ErrorExit:
    UNLOCK_SRN_AND_LEAVE_CS(pHDS);
    DRM_PROFILING_LEAVE_SCOPE(L"DRM_HDS_OpenNamespace", g_pwszLeavingFunction);
    return dr;
}


/*
**
*/
DRM_RESULT DRM_API DRM_HDS_CloseNamespace(
    IN DRM_HDS_NAMESPACE_CONTEXT *pcontextNS)
{
    DRM_RESULT    dr=DRM_SUCCESS;
    _HdsContext  *pHDS=NULL;
    _NsContext   *pUserNsCFG=(_NsContext *)pcontextNS;
    _SlotContext *pNsStoreSlotCtx=NULL;
    DEFINE_LOCK_VARS

        
    ChkArg(pcontextNS != NULL 
        && ISVALIDCONTEXT(pUserNsCFG, eCfgContextSignature));
    DRMASSERT(pcontextNS != NULL
           && pUserNsCFG->fInited 
           && pUserNsCFG->pHDS 
           && pUserNsCFG->pHDS->fp != OEM_INVALID_HANDLE_VALUE );

#if DBG
{
    /* let's check if the namespace is valid
    ** this may be redandunt so just do it in DBG build 
    */

    DRM_HDS_HASHKEY   oNsHashKey;
    DRM_HDS_UNIQUEKEY oNsUniqueKey;
    DRM_BOOL          fResult=FALSE;
    DRM_DWORD         cbNsStoreSlotCtx=0;
    _NsContext        oNsStoreCFG;

    pHDS = pUserNsCFG->pHDS;

    /* lock SRN SHARED among other processes */
    ENTER_CS_AND_LOCK_SRN(pHDS, (eDRM_HDS_LOCKSHARED | eDRM_HDS_LOCKWAIT));

    /* First, set up a NS context for accessing the NamespaceStore */
    ChkDR(_HdsInitNsContext(pUserNsCFG->pHDS, &oNsStoreCFG, NULL, 
        pUserNsCFG->pHDS->oSRN.nNsStoreRootBlockNum, NsStoreNumofChildren));

    /* Second, alloc buffer and setup _SlotContext (pNsStoreSlotCtx) to store  
    ** the given namespace in NamespaceStore
    */
    cbNsStoreSlotCtx = CALC_SLOTCONTEXTLEN(&oNsStoreCFG);  /* size of slot context */
    ChkDR(_Hds_malloc(pUserNsCFG->pHDS, cbNsStoreSlotCtx, (DRM_VOID**)&pNsStoreSlotCtx));
    ChkDR(_HdsInitSlotContext(&oNsStoreCFG, (DRM_BYTE *)pNsStoreSlotCtx, 
        cbNsStoreSlotCtx));

    /* Last, search if the namespace is still exist in file */
    _GenNamespaceKeys(&pUserNsCFG->pHDS->contextMD5, (DRM_CHAR *)pUserNsCFG->oNamespace, 
        DRM_HDS_NSNAME_SIZE, oNsHashKey.rgb, oNsUniqueKey.rgb);
    ChkDR(_HdsSearchSlotInFile(&oNsStoreCFG, &oNsHashKey, &oNsUniqueKey, eSearchNamespace, 
        NULL, pNsStoreSlotCtx, &fResult));
    if ( !fResult )
    {
        TRACE(("DRM_HDS_CloseNamespace(): namespace '%s' does not exist.\n", pUserNsCFG->oNamespace));
        ChkDR(DRM_E_HDSNAMESPACENOTFOUND);
    }
}
#endif /* DBG */

    /* wipe the context */
    ZEROMEM(pcontextNS, SIZEOF(_NsContext));

ErrorExit:
#if DBG
    if ( pNsStoreSlotCtx )
    {
        _Hds_free(pHDS, pNsStoreSlotCtx);
    }

    UNLOCK_SRN_AND_LEAVE_CS(pHDS);
#endif

    return dr;      
}

/*
**  This function is not being used anywhere. It was included for completeness
**  of the HDS API. It will be updated if needed.
*/
DRM_RESULT DRM_API DRM_HDS_DeleteNamespace( 
    IN       DRM_HDS_CONTEXT   *pcontextHDS,
    IN const DRM_HDS_NAMESPACE *pNamespace,
    IN       DRM_BOOL           fWait)
{
    DRM_RESULT        dr=DRM_SUCCESS;
    DRM_HDS_HASHKEY   oNsHashKey;
    DRM_HDS_UNIQUEKEY oNsUniqueKey;
    DRM_BOOL          fResult=FALSE;
    _HdsContext      *pHDS=(_HdsContext*)pcontextHDS;
    _NsContext        oNsStoreCFG;
    _SlotContext     *pNsStoreSlotCtx=NULL;
    DRM_DWORD         cbNsStoreSlotCtx=0;
    DRM_DWORD         cbNsSlotSize=0;
    _NsContext       *pcontextNS=NULL;
    _NSEntry         *pNsEntry=NULL;
    DEFINE_LOCK_VARS

    ChkArg(pcontextHDS != NULL
        && pNamespace  != NULL
        && ISVALIDCONTEXT(pHDS, eHdsContextSignature));
    DRMASSERT(pcontextHDS != NULL
          &&  pHDS->fInited 
          &&  pNamespace  != NULL
          &&  pHDS->fp!=OEM_INVALID_HANDLE_VALUE);

    /* 
    ** Lock STORE EXCLUSIVE among other processes. So if there are any 
    ** locked slots, this call will fail, or wait until all locks are released
    */
    LOCK_STORE( pHDS,  eDRM_HDS_LOCKEXCLUSIVE | (fWait ? eDRM_HDS_LOCKWAIT : 0) );
    ENTER_CS_AND_LOCK_SRN(pHDS, eDRM_HDS_LOCKEXCLUSIVE | (fWait ? eDRM_HDS_LOCKWAIT : 0) );

    /*  First, set up a HDS Config (oNsStoreCFG) for accessing the NamespaceStore 
    */
    ChkDR(_HdsInitNsContext(pHDS, &oNsStoreCFG, NULL, pHDS->oSRN.nNsStoreRootBlockNum, 
        NsStoreNumofChildren));

    /* Second, alloc buffer and setup _SlotContext (pNsStoreSlotCtx) to store the given 
    ** namespace in NamespaceStore
    */
    cbNsStoreSlotCtx = CALC_SLOTCONTEXTLEN(&oNsStoreCFG);  /* size of slot context */
    ChkDR(_Hds_malloc(pHDS, cbNsStoreSlotCtx, (DRM_VOID**)&pNsStoreSlotCtx));
    ChkDR(_HdsInitSlotContext(&oNsStoreCFG, (DRM_BYTE *)pNsStoreSlotCtx, cbNsStoreSlotCtx));

    /* Third, prepare the namespace key and search it in NamespaceStore
    */
    _GenNamespaceKeys (&pHDS->contextMD5, 
           (DRM_CHAR *) pNamespace->rgb, 
                        DRM_HDS_NSNAME_SIZE, 
                        oNsHashKey.rgb, 
                        oNsUniqueKey.rgb);
    ChkDR(_HdsSearchSlotInFile(&oNsStoreCFG, &oNsHashKey, &oNsUniqueKey, eSearchNamespace,
        &cbNsSlotSize, pNsStoreSlotCtx, &fResult));
    if ( !fResult )
    {
        TRACE(("DRM_HDS_DeleteNamespace(): namespace '%s' does not exist.\n", pNamespace->rgb));
        ChkDR(DRM_E_HDSNAMESPACENOTFOUND);
    }
    else
    {
        DRM_HDS_HASHKEY   oHashKey; 
        DRM_HDS_UNIQUEKEY oUniqueKey;
    
        /* allocate buffer and load the User's namespace entry from NsStore */
        ChkDR(_Hds_malloc(pHDS, cbNsSlotSize, (DRM_VOID**)&pNsEntry));

        ChkDR(_HdsSlotRead(pNsStoreSlotCtx, cbNsSlotSize, (DRM_BYTE *)pNsEntry, NULL));

        FIX_ENDIAN_WORD( pNsEntry->wMaxNumChildren );
        FIX_ENDIAN_DWORD( pNsEntry->nNSRBlockNum );
            ChkDR(_HdsValidateNSEntryFromDisk( pHDS, pNsEntry ) );

        /* setup cfg context for the namespace */
        ChkDR(_Hds_malloc(pHDS, SIZEOF(_NsContext), (DRM_VOID**)&pcontextNS));
        ChkDR(_HdsInitNsContext(pHDS, pcontextNS, pNamespace, pNsEntry->nNSRBlockNum,
            pNsEntry->wMaxNumChildren));

        /* delete the namespace's subtree */
        ChkDR(_HdsDeleteSubTree(pcontextNS, pNsEntry->nNSRBlockNum));

        /* delete the Config's slot in NamespaceStore */
        /*ChkDR(_HdsCloseSlot(pNsStoreSlotCtx)); */

        MEMCPY(oHashKey.rgb,   oNsHashKey.rgb,   DRM_HDS_HASHKEY_SIZE);
        MEMCPY(oUniqueKey.rgb, oNsUniqueKey.rgb, DRM_HDS_UNIQUEKEY_SIZE);

        ChkDR( _HdsRemoveSlot(pNsStoreSlotCtx, eRemoveSlotPermanent ) );
    }

ErrorExit:
    if ( pcontextNS )
    {
        _Hds_free(pHDS, pcontextNS);
    }
    if ( pNsEntry )
    {
        _Hds_free(pHDS, pNsEntry);
    }
    if ( pNsStoreSlotCtx )
    {
        _Hds_free(pHDS, pNsStoreSlotCtx);
    }

    UNLOCK_SRN_AND_LEAVE_CS(pHDS);
    UNLOCK_STORE(pHDS);
    return dr;      
}

/*
**
*/
DRM_RESULT DRM_API DRM_HDS_DeleteSlot( 
    IN       DRM_HDS_NAMESPACE_CONTEXT *pcontextNS,    /* Namespace context returned from DRM_HDS_OpenNamespace */
    IN const DRM_HDS_HASHKEY           *pHashKey, 
    IN const DRM_HDS_UNIQUEKEY         *pUniqueKey,
    IN       DRM_HDS_SLOT_HINT         *pSlotHint,
    IN       DRM_BOOL                   fWait)
{
    DRM_RESULT       dr             = DRM_SUCCESS;
    _NsContext      *pNS            = (_NsContext*)pcontextNS;
    _SlotContext    *pSlotCtx       = NULL;
    DRM_BOOL         fValid         = FALSE;
    DRM_BOOL         fBlockLocked   = FALSE;
    DRM_DWORD        cbSlotSize     = 0;
    DRM_BYTE         rgbBuff [__CB_DECL(CALC_MAXSLOTCONTEXTLEN)];
    DEFINE_LOCK_VARS
    
    ChkArg(pcontextNS != NULL
       && ISVALIDCONTEXT(pNS, eCfgContextSignature));
    DRMASSERT(pcontextNS 
          &&  pNS->fInited
          &&  pHashKey   != NULL
          &&  pUniqueKey != NULL
          &&  pNS->pHDS  != NULL
          &&  pNS->pHDS->fp!=OEM_INVALID_HANDLE_VALUE);

    /* initialize slot context structure */
    
    pSlotCtx = (_SlotContext *) rgbBuff;

    ChkOverflowSLOTCONTEXTLEN( pNS );
    ChkDR(_HdsInitSlotContext(pNS, (DRM_BYTE*)pSlotCtx, CALC_SLOTCONTEXTLEN(pNS)));
                        
    /* Lock SRN SHARED among other processes. */
    ENTER_CS_AND_LOCK_SRN( pNS->pHDS, ~eDRM_HDS_LOCKEXCLUSIVE & eDRM_HDS_LOCKWAIT );
    
    while ( TRUE )
    { 
        if ( pSlotHint != NULL )
        {
            dr = _HdsOpenSlotFromHint(pNS, pHashKey, pUniqueKey, (_SlotHint*)pSlotHint, TRUE, &cbSlotSize, pSlotCtx);
        }
        
        if ( pSlotHint == NULL
            || DRM_FAILED ( dr ) )
        {
            ChkDR( _HdsOpenExistingSlot(pNS, pHashKey, pUniqueKey, TRUE, &cbSlotSize, pSlotCtx) );
        }

        ChkDR( _HdsLockBlock2DeleteSlot( 
                    pSlotCtx, 
                    eDRM_HDS_LOCKEXCLUSIVE | (fWait ? eDRM_HDS_LOCKWAIT : 0) ) );                            
        fBlockLocked = TRUE;

        ChkDR(_HdsVerifySlotContext(pSlotCtx, &fValid));

        if ( fValid )
        {
            break;
        }

        /* slot context verified failed, try again */
        (void)_HdsUnlockBlock2DeleteSlot(pSlotCtx);
        fBlockLocked = FALSE;        
    } 

    /*
    **  We currently have the lock on the block as well as CS and SRN lock
    */
    ChkDR( _HdsRemoveSlot(pSlotCtx, eRemoveSlotPermanent) );
    
    if ( pSlotHint != NULL )
    {
        ZEROMEM ( pSlotHint, DRM_HDS_SLOT_HINT_LEN );
    }

ErrorExit:  

    if ( fBlockLocked )
    {
        /* error occured during verifying slot context, return error */
        (void)_HdsUnlockBlock2DeleteSlot(pSlotCtx);
    }
        
    if (pNS != NULL)
    {
        UNLOCK_SRN_AND_LEAVE_CS(pNS->pHDS);
    }
    return dr;
}



DRM_RESULT DRM_API DRM_HDS_Checkpoint( 
    IN OUT   DRM_HDS_CONTEXT *pcontextHDS,
    IN       DRM_VOID        *pvOpaqueData,
    IN       DRM_BOOL         fCreate)                /* TRUE to create, FALSE to verify */
{
    DRM_RESULT   dr=DRM_SUCCESS;
    _HdsContext *pHDS=(_HdsContext*)pcontextHDS;
    DRM_DWORD    dwByte=0;
    DRM_BYTE     hash[__CB_DECL(MD5DIGESTLEN)];
    DRM_DWORD    dwNumberOfBytesIO;
    DEFINE_LOCK_VARS

    ChkArg(pHDS!=NULL  
       &&  ISVALIDCONTEXT(pHDS, eHdsContextSignature));
    DRMASSERT(pcontextHDS 
           && pHDS->fInited
           && pHDS->fp!=OEM_INVALID_HANDLE_VALUE);

    if ( fCreate )
    {
        DRM_UINT64 timeStamp = _GetTimeStamp();
        
        /* Wait and lock SRN EXCLUSIVE among other processes. */
        ENTER_CS_AND_LOCK_SRN(pHDS, (eDRM_HDS_LOCKEXCLUSIVE | eDRM_HDS_LOCKWAIT));

        /* create checkpoint using timestamp */
        DRM_MD5_Init( &pHDS->contextMD5 );
        DRM_MD5_Update( &pHDS->contextMD5, (DRM_BYTE*)&timeStamp, SIZEOF(DRM_UINT64));
        DRM_MD5_Final( &pHDS->contextMD5 );
        MEMCPY( hash, pHDS->contextMD5.digest, SIZEOF( pHDS->contextMD5.digest ) );

        /* write the hash to file right after SRN */
        if (!OEM_SetFilePointer(pHDS->fp, SIZEOF(DRM_DWORD)+SIZEOF(_SRN), OEM_FILE_BEGIN, NULL))
        {
            ChkDR(DRM_E_FILESEEKERROR);
        }
        if (!OEM_WriteFile(pHDS->fp, hash, __CB_DECL(MD5DIGESTLEN), &dwNumberOfBytesIO) 
            || dwNumberOfBytesIO != __CB_DECL(MD5DIGESTLEN))
        {
            ChkDR(DRM_E_FILEWRITEERROR);
        }

        /* Set datastore redundancy entry */
        ChkDR( OEM_GetSetDataStoreRedundancy(pvOpaqueData,(DRM_BYTE*)&timeStamp,SIZEOF(DRM_UINT64),FALSE));
    }
    else
    {
        DRM_BYTE   _hash[__CB_DECL(MD5DIGESTLEN)];
        DRM_UINT64 timeStamp;

        /* Wait and lock SRN SHARED among other processes. */
        ENTER_CS_AND_LOCK_SRN(pHDS, (eDRM_HDS_LOCKSHARED | eDRM_HDS_LOCKWAIT));

        /* Get datastore redundancy entry */
        dr = OEM_GetSetDataStoreRedundancy(pvOpaqueData,(DRM_BYTE*)&timeStamp,SIZEOF(DRM_UINT64),TRUE);
        if (DRM_FAILED(dr))
        {
            /* datastore redundancy entry not found. just return ok, for now ... */
            dr = DRM_SUCCESS;
            goto ErrorExit;
        }

        /* create hash using restored timestamp from datastore redundancy entry */
        DRM_MD5_Init( &pHDS->contextMD5 );
        DRM_MD5_Update( &pHDS->contextMD5, (DRM_BYTE*)&timeStamp,SIZEOF(DRM_UINT64));
        DRM_MD5_Final( &pHDS->contextMD5 );
        MEMCPY( hash, pHDS->contextMD5.digest, SIZEOF( pHDS->contextMD5.digest ) );

        /* load hash from file */
        if (!OEM_SetFilePointer(pHDS->fp, SIZEOF(DRM_DWORD)+SIZEOF(_SRN), OEM_FILE_BEGIN, NULL))
        {
            ChkDR(DRM_E_FILESEEKERROR);
        }
        if (!OEM_ReadFile(pHDS->fp, _hash, __CB_DECL(MD5DIGESTLEN), &dwNumberOfBytesIO) 
            || dwNumberOfBytesIO != __CB_DECL(MD5DIGESTLEN))
        {
            ChkDR(DRM_E_FILEREADERROR);
        }

        if ( MEMCMP(hash, _hash, __CB_DECL(MD5DIGESTLEN)) != 0 )
        {
            ChkDR(DRM_E_HASHMISMATCH);
        }
    }

ErrorExit:

    UNLOCK_SRN_AND_LEAVE_CS(pHDS);
    return dr;
}

/* Initialize a slot hint based on the slot context passed */
DRM_RESULT DRM_API DRM_HDS_MakeSlotHint(
    IN     DRM_HDS_SLOT_CONTEXT      *pcontextSlot,
    IN OUT DRM_HDS_SLOT_HINT         *pslotHint)
{
    DRM_RESULT    dr        = DRM_SUCCESS;
    _SlotContext *pSlotCtx  = (_SlotContext*)pcontextSlot;
    _SlotHint *pHint        = (_SlotHint*)pslotHint;

    ChkArg(ISVALIDCONTEXT(pSlotCtx, eSlotContextSignature));
    DRMASSERT(pcontextSlot != NULL
          &&  pslotHint != NULL
          &&  pSlotCtx->pNS != NULL
          &&  pSlotCtx->pNS->pHDS != NULL
          &&  pSlotCtx->pNS->pHDS->fp!=OEM_INVALID_HANDLE_VALUE);
    DRMSIZEASSERT( SIZEOF( DRM_HDS_SLOT_CONTEXT ), CALC_MAXSLOTCONTEXTLEN );

    pHint->nBlockNum = pSlotCtx->oFileBlock.nBlockNum;
    pHint->nSlotPosInBlock = pSlotCtx->dwSlotPosInBlock;

ErrorExit:
    if( DRM_FAILED( dr )
        && pslotHint != NULL )
    {
        ZEROMEM( pslotHint, SIZEOF( _SlotHint ) );
    }
    
    return dr;
}

/* 
** Create or open dataslot using given key1 and key2 
*/
DRM_RESULT DRM_API DRM_HDS_OpenSlot( 
    IN     DRM_HDS_NAMESPACE_CONTEXT *pcontextNS,   /* Namespace context returned from DRM_HDS_OpenNamespace */
    IN     DRM_DWORD                  dwMode,       /* combo of DRM_HDS_OPENMODE and DRM_HDS_LOCKMODE */
    IN     const DRM_HDS_HASHKEY     *pHashKey, 
    IN     const DRM_HDS_UNIQUEKEY   *pUniqueKey, 
    IN     DRM_HDS_SLOT_HINT         *pSlotHint,
    IN OUT DRM_DWORD                 *pcbSlotSize,  /* current dataslot size */
    OUT    DRM_HDS_SLOT_CONTEXT      *pcontextSlot) /* user given Dataslot context buffer */
{
    DRM_RESULT    dr=DRM_SUCCESS;
    _NsContext   *pNS=(_NsContext*)pcontextNS;
    _SlotContext *pSlotCtx=NULL;
    DRM_BOOL      fSlotLocked = FALSE;
    DRM_BOOL      fValid        = FALSE;
    DEFINE_LOCK_VARS

    DRM_PROFILING_ENTER_SCOPE(L"DRM_HDS_OpenSlot", g_pwszEnteringFunction, DRM_PROFILING_DONT_START);
    ChkArg(ISVALIDCONTEXT(pNS, eCfgContextSignature)
        && pHashKey != NULL
        && pUniqueKey != NULL
        && !_IsNULL(pHashKey->rgb, DRM_HDS_HASHKEY_SIZE));
    DRMASSERT(pcontextNS != NULL
          && pcontextSlot != NULL
          && pNS->fInited 
          && pNS->nCfgRootBlockNum > 0
          && pNS->pHDS != NULL
          && pNS->pHDS->fp != OEM_INVALID_HANDLE_VALUE
          && ((dwMode & eDRM_HDS_CREATE_IF_NEW) || (dwMode & eDRM_HDS_OPEN_EXISTING)) );
    DRMSIZEASSERT( SIZEOF( DRM_HDS_SLOT_CONTEXT ), CALC_MAXSLOTCONTEXTLEN );

    /* check slot size */
    if ( (SIZEOF(_SlotHeader) + (*pcbSlotSize)) < (*pcbSlotSize) )
    {
        /* (*pcbSlotSize) is a negative number */
        ChkDR(DRM_E_INVALID_HDSSLOTSIZE);
    }

    /* initialize slot context structure */
    ChkDR(_HdsInitSlotContext(pNS, (DRM_BYTE*)pcontextSlot,SIZEOF(DRM_HDS_SLOT_CONTEXT)));
    pSlotCtx = (_SlotContext*)pcontextSlot;
            
    while ( TRUE )
    {
        /* Lock SRN SHARED among other processes. */
        ENTER_CS_AND_LOCK_SRN(pNS->pHDS, (dwMode & (~eDRM_HDS_LOCKEXCLUSIVE)));


        /* try if we can open the slot first */
        if ( pSlotHint != NULL )
        {
            dr = _HdsOpenSlotFromHint(pNS, pHashKey, pUniqueKey, (_SlotHint*)pSlotHint, TRUE, pcbSlotSize, pSlotCtx);

            if ( DRM_FAILED(dr) )
            {
                dr = _HdsOpenExistingSlot(pNS, pHashKey, pUniqueKey, TRUE, pcbSlotSize, pSlotCtx);

                if ( DRM_SUCCEEDED(dr) )
                {
                    DRM_HDS_MakeSlotHint(pcontextSlot, pSlotHint);
                }
            }
        }
        else
        {
            dr = _HdsOpenExistingSlot(pNS, pHashKey, pUniqueKey, TRUE, pcbSlotSize, pSlotCtx);
        }

        /* slot exists but caller ask to create new only */
        if ( DRM_SUCCEEDED(dr)  &&  (dwMode & eDRM_HDS_OPEN_EXISTING) == 0 )
        {
            ChkDR(DRM_E_HDSSLOTEXIST);
        }

        /* slot does not exist and caller asked to create new */
        if ( dr == DRM_E_HDSSLOTNOTFOUND && (dwMode & eDRM_HDS_CREATE_IF_NEW) != 0)
        {
            if ( (dwMode & eDRM_HDS_LOCKEXCLUSIVE) == 0 )
            {
                ChkDR(DRM_E_HDSEXCLUSIVELOCKONLY);
            }
        
            UNLOCK_SRN( pNS->pHDS );  
            
            /* Lock SRN EXCLUSIVE among other processes. */
            LOCK_SRN( pNS->pHDS, dwMode | eDRM_HDS_LOCKEXCLUSIVE );
            
            /* 
            **  note: _HdsCreateAndOpenSlot() will issue lock for the slot             
            */
            ChkDR( _HdsCreateAndOpenSlot( pNS, 
                                   pHashKey, 
                                   pUniqueKey, 
                                   TRUE, 
                                   *pcbSlotSize, 
                                   pSlotCtx, 
                                   dwMode ) );
            fSlotLocked = TRUE;

            break;                            
        }

        /* return if other error */
        ChkDR(dr);

        /* here, a slot exists and caller is asking to open existing ...
        ** drop all the locks and CS to allow concurrency to occur 
        ** while waiting for the slot lock 
        */
        UNLOCK_SRN_AND_LEAVE_CS(pNS->pHDS);

        /* issue specific slot lock */
        ChkDR(_HdsLockSlot(pSlotCtx, dwMode));
        fSlotLocked = TRUE;

        /* we got the lock to the slot, let's verify the sanity of the slot 
        ** we need to enter CS and lock SRN again */
        ENTER_CS_AND_LOCK_SRN(pNS->pHDS, (dwMode & (~eDRM_HDS_LOCKEXCLUSIVE)));

        ChkDR(_HdsVerifySlotContext(pSlotCtx, &fValid));

        /* drop all the locks and CS again */
        UNLOCK_SRN_AND_LEAVE_CS(pNS->pHDS);

        if ( fValid )
        {
            break;
        }

        /* slot context verified failed, try again */
        (void)_HdsUnlockSlot(pSlotCtx);
        fSlotLocked = FALSE;        
    }    

ErrorExit:
    if ( pNS != NULL  &&  pNS->pHDS != NULL )
    {
        if ( DRM_FAILED(dr)  &&  fSlotLocked )
        {
            /* error occured during verifying slot context, return error */
            _HdsUnlockSlot(pSlotCtx);
        }

        UNLOCK_SRN_AND_LEAVE_CS(pNS->pHDS);
    }
    DRM_PROFILING_LEAVE_SCOPE(L"DRM_HDS_OpenSlot", g_pwszLeavingFunction);
    
    return dr;
}

/*
**
*/
DRM_RESULT DRM_API DRM_HDS_CloseSlot( 
    IN DRM_HDS_SLOT_CONTEXT *pcontextSlot)
{
    DRM_RESULT    dr=DRM_SUCCESS;
    _SlotContext *pSlotCtx=(_SlotContext*)pcontextSlot;
    _HdsContext  *pHDS=NULL;
    DEFINE_LOCK_VARS

    ChkArg(pcontextSlot != NULL
        && ISVALIDCONTEXT(pSlotCtx, eSlotContextSignature));
    DRMASSERT(pcontextSlot != NULL
          && pSlotCtx->pNS 
          && pSlotCtx->pNS->pHDS 
          && pSlotCtx->pNS->pHDS->fp!=OEM_INVALID_HANDLE_VALUE);
    if (pSlotCtx->eStatus==eSlotCtxUninit)
    {
        ChkDR(DRM_E_HDSSLOTNOTFOUND);
    }    

    ChkDR(_HdsUnlockSlot(pSlotCtx));
    ChkDR(_HdsCloseSlot(pSlotCtx));

ErrorExit:
    return dr;
}


/*
**
*/
DRM_RESULT DRM_API DRM_HDS_SlotSeek( 
    IN       DRM_HDS_SLOT_CONTEXT *pcontextSlot,
    IN const DRM_LONG              lOffset,
    IN const DRM_HDS_SEEKMODE      eOrigin,         /* as defined above */
    OUT      DRM_DWORD            *pdwSeekPointer)   /* optional parameter, can be NULL */
{
    DRM_RESULT    dr=DRM_SUCCESS;
    _SlotContext *pSlotCtx=(_SlotContext*)pcontextSlot;

    ChkArg(pcontextSlot != NULL
        && ISVALIDCONTEXT(pSlotCtx, eSlotContextSignature));
    DRMASSERT(pcontextSlot != NULL
          && pSlotCtx->pNS != NULL 
          && pSlotCtx->pNS->pHDS != NULL
          && pSlotCtx->pNS->pHDS->fp!=OEM_INVALID_HANDLE_VALUE);

    /* Note:
    ** - We do not need to enter CS because we do not need access to file handle
    **   and Local stack
    ** - this slot is either locked for SHARED or EXCLUSIVE. We do not need to 
    **   worry if it is changed after it is opened 
    */
    if (pSlotCtx->eStatus!=eSlotCtxReady)
    {
        ChkDR(DRM_E_HDSSLOTNOTFOUND);
    }
    switch ( eOrigin )
    {
        case eDRM_HDS_SEEKCUR:
            if ( lOffset > 0 )
            {
                if ( (pSlotCtx->dwSeekPointer+lOffset) > pSlotCtx->oSlotHeader.dwSlotSize )
                {
                    ChkDR(DRM_E_HDSSEEKERROR);
                }
                pSlotCtx->dwSeekPointer += lOffset;
            }
            else
            {
                if ( pSlotCtx->dwSeekPointer < (DRM_UINT)DRM_abs(lOffset) )
                {
                    ChkDR(DRM_E_HDSSEEKERROR);
                }
                pSlotCtx->dwSeekPointer -= DRM_abs(lOffset);
            }
            break;
        case eDRM_HDS_SEEKEND:
            if ( lOffset > 0 || (DRM_UINT)DRM_abs(lOffset) > pSlotCtx->oSlotHeader.dwSlotSize )
            {
                ChkDR(DRM_E_HDSSEEKERROR);
            }
            pSlotCtx->dwSeekPointer = pSlotCtx->oSlotHeader.dwSlotSize - DRM_abs(lOffset);
            break;
        case eDRM_HDS_SEEKSET:
            if ( lOffset < 0 || lOffset > (DRM_LONG) pSlotCtx->oSlotHeader.dwSlotSize )
            {
                ChkDR(DRM_E_HDSSEEKERROR);
            }
            pSlotCtx->dwSeekPointer = lOffset;
            break;
        default:
            ChkDR(DRM_E_INVALIDARG);
    }

ErrorExit:
    if ( DRM_SUCCEEDED(dr) && pdwSeekPointer)
    {
        *pdwSeekPointer = pSlotCtx->dwSeekPointer;
    }
    return dr;
}



/*
**
*/
DRM_RESULT DRM_API DRM_HDS_SlotRead( 
    IN  DRM_HDS_SLOT_CONTEXT *pcontextSlot,
    IN  DRM_DWORD             cbData,
    OUT DRM_BYTE             *pbData,
    OUT DRM_DWORD            *pcbRead)
{
    DRM_RESULT    dr=DRM_SUCCESS;
    _SlotContext *pSlotCtx=(_SlotContext*)pcontextSlot;
    DEFINE_LOCK_VARS

    ChkArg(ISVALIDCONTEXT(pSlotCtx, eSlotContextSignature));
    DRMASSERT(pcontextSlot != NULL
          &&  pSlotCtx->pNS != NULL
          &&  pSlotCtx->pNS->pHDS != NULL
          &&  pSlotCtx->pNS->pHDS->fp!=OEM_INVALID_HANDLE_VALUE);

    /* Note:
    ** - this slot is either locked for SHARED or EXCLUSIVE. We do not need to 
    **   worry if it is changed after it is opened 
    */

    /* Lock Order: 
    ** - CS:  to guard exclusive access to file handle
    */
    ENTER_HDS_CS(pSlotCtx->pNS->pHDS);  

    ChkDR(_HdsReadWriteSlot(pSlotCtx, cbData, pbData, pcbRead, TRUE));

ErrorExit:

    LEAVE_HDS_CS(pSlotCtx->pNS->pHDS);
    return dr;
}



/*
**
*/
DRM_RESULT DRM_API DRM_HDS_SlotWrite( 
    IN       DRM_HDS_SLOT_CONTEXT *pcontextSlot,
    IN       DRM_DWORD             cbData,
    IN const DRM_BYTE             *pbData,
    OUT      DRM_DWORD            *pcbWritten)
{
    DRM_RESULT    dr=DRM_SUCCESS;
    _SlotContext *pSlotCtx=(_SlotContext*)pcontextSlot;
    DEFINE_LOCK_VARS

    ChkArg(ISVALIDCONTEXT(pSlotCtx, eSlotContextSignature));
    DRMASSERT(pcontextSlot != NULL
          &&  pSlotCtx->pNS != NULL
          &&  pSlotCtx->pNS->pHDS != NULL
          &&  pSlotCtx->pNS->pHDS->fp!=OEM_INVALID_HANDLE_VALUE);

    /* Note:
    ** - this slot is locked for EXCLUSIVE. We do not need to 
    **   worry if it is changed after it is opened 
    */
    
    /* slot must be locked EXCLUSIVE */
    if ( (pSlotCtx->eLockMode & eDRM_HDS_LOCKEXCLUSIVE) == 0 )
    {
        ChkDR(DRM_E_HDSNOTLOCKEDEXCLUSIVE);
    }

    /* Lock Order: 
    ** - CS:  to guard exclusive access to file handle
    */
    ENTER_HDS_CS(pSlotCtx->pNS->pHDS);  

    /* NOTE: We cast away the const-ness here because we know _HdsReadWriteSlot 
    ** is safe when the operation is Write 
    */
    ChkDR(_HdsReadWriteSlot(pSlotCtx, cbData, (DRM_BYTE*)pbData, pcbWritten, FALSE));

    /* 
    ** Note:
    ** we do not need to flush if _DATASTORE_WRITE_THRU is defined. file buffers
    ** are already flushed.
    ** if _DATASTORE_WRITE_THRU is not defined, caller should call DRM_HDS_Commit* 
    ** to flush file buffer at higher level
    */
    
ErrorExit:

    LEAVE_HDS_CS(pSlotCtx->pNS->pHDS);
    return dr;
}


DRM_RESULT DRM_API DRM_HDS_SlotResize( 
    IN DRM_HDS_SLOT_CONTEXT *pcontextSlot, 
    IN DRM_DWORD             cbSize)
{
    DRM_RESULT     dr=DRM_SUCCESS;
    _SlotContext  *pSlotCtx=(_SlotContext*)pcontextSlot;
    DEFINE_LOCK_VARS

    ChkArg(ISVALIDCONTEXT(pSlotCtx, eSlotContextSignature));
    DRMASSERT(pcontextSlot != NULL
          &&  pSlotCtx->pNS != NULL 
          &&  pSlotCtx->pNS->pHDS != NULL
          &&  pSlotCtx->pNS->pHDS->fp != OEM_INVALID_HANDLE_VALUE 
          &&  cbSize > 0);

    /* we do not need to verify slot content since we must have either shared
    ** or exclusive lock on the slot 
    */
    if (pSlotCtx->eStatus!=eSlotCtxReady)
    {
        ChkDR(DRM_E_HDSSLOTNOTFOUND);
    }

    /* Lock SRN EXCLUSIVE to prevent other process from change the file*/
    ENTER_CS_AND_LOCK_SRN(pSlotCtx->pNS->pHDS, (pSlotCtx->eLockMode | eDRM_HDS_LOCKEXCLUSIVE));

    ChkDR(_HdsSlotResize(pSlotCtx, cbSize));

ErrorExit:
    if ( pSlotCtx!=NULL  &&  pSlotCtx->pNS!=NULL )
    {
        UNLOCK_SRN_AND_LEAVE_CS(pSlotCtx->pNS->pHDS);
    }
    return dr;
}



DRM_RESULT DRM_API DRM_HDS_InitSlotEnum(
    IN  DRM_HDS_NAMESPACE_CONTEXT *pcontextNS,     /* Namespace context returned from DRM_HDS_OpenNamespace */
    IN  const DRM_HDS_HASHKEY     *pHashKey,       /* if NULL, all slots will be enum'ed */
    IN  DRM_HDS_LOCKMODE           eLockMode,
    OUT DRM_HDS_ENUM_CONTEXT      *pcontextEnum)
{
    DRM_RESULT    dr    = DRM_SUCCESS;
    _NsContext   *pNS   = (_NsContext   *) pcontextNS;
    _EnumContext *pEnum = (_EnumContext *) pcontextEnum;
    DEFINE_LOCK_VARS

    DRM_PROFILING_ENTER_SCOPE(L"DRM_HDS_InitSlotEnum", g_pwszEnteringFunction, DRM_PROFILING_DONT_START);
    DRMASSERT(pcontextNS && pcontextEnum);
    DRMSIZEASSERT( SIZEOF(DRM_HDS_ENUM_CONTEXT), CALC_MAXENUMCONTEXTLEN );

    ChkArg(pNS->pHDS != NULL
       &&  pNS->pHDS->fp != OEM_INVALID_HANDLE_VALUE 
       &&  ISVALIDCONTEXT(pNS, eCfgContextSignature) 
       &&  ISVALIDCONTEXT(pNS->pHDS, eHdsContextSignature));

    /* Lock SRN SHARED among other processes. */
    ENTER_CS_AND_LOCK_SRN(pNS->pHDS, (eLockMode & (~eDRM_HDS_LOCKEXCLUSIVE)));

    /* _HdsInitSlotEnum prepares the enum context. It will not block so we are okay */
    ChkDR(_HdsInitSlotEnum(pNS, pHashKey, eLockMode, pEnum));

ErrorExit:
    if ( pNS != NULL )
    {
        UNLOCK_SRN_AND_LEAVE_CS(pNS->pHDS);  /* unlock SRN */
    }
    DRM_PROFILING_LEAVE_SCOPE(L"DRM_HDS_InitSlotEnum", g_pwszLeavingFunction);
    return dr;
}

DRM_RESULT DRM_API DRM_HDS_SlotEnumReloadCurrent( 
    IN  DRM_HDS_ENUM_CONTEXT *pcontextEnum,       /* setup by DRM_HDS_InitSlotEnum() */
    OUT DRM_HDS_SLOT_CONTEXT *pcontextSlot,       /* user given Dataslot context buffer */
    OUT DRM_HDS_HASHKEY      *pHashKey,           /* optional parameter */
    OUT DRM_HDS_UNIQUEKEY    *pUniqueKey,         /* optional parameter */
    OUT DRM_DWORD            *pcbSize)            /* current dataslot size, optional parameter */
{
    DRM_RESULT     dr         = DRM_SUCCESS;    
    _EnumContext  *pEnum      = (_EnumContext *)pcontextEnum;
    _CommBlockHDR *pCurrBlock = NULL;

    ChkArg(pEnum != NULL
       &&  pEnum->pNS != NULL
       &&  ISVALIDCONTEXT(pEnum,      eEnumContextSignature)
       &&  ISVALIDCONTEXT(pEnum->pNS, eCfgContextSignature));
    DRMASSERT(pEnum->pNS->fInited 
          &&  pEnum->pNS->pHDS  != NULL
          &&  pEnum->pNS->pHDS->fp != OEM_INVALID_HANDLE_VALUE 
          &&  pcontextSlot != NULL);

    if ( !pEnum->fCurrBlockIsValid )
    {
        dr = DRM_E_NOMORE;
        goto ErrorExit;
    }
    pCurrBlock = &pEnum->oCurrBlock;

    /* use next positon as current position */
    ((_ChildBlockHDR*)pCurrBlock)->nNextSlotPos = ((_ChildBlockHDR*)pCurrBlock)->nCurrSlotPos;

    dr = DRM_HDS_SlotEnumNext(pcontextEnum, pcontextSlot, pHashKey, pUniqueKey, pcbSize);

ErrorExit:
    return dr;
}



/******************************************************************************
** Function:   DRM_HDS_SlotEnumDeleteCurrent
** Synopsis:   delete the current enumerated slot
** Arguments:  pcontextEnum - HDS enum context
**             pcontextSlot - HDS slot context of the current enumerated slot
** Returns:    
** Notes:      
******************************************************************************/
DRM_RESULT DRM_API DRM_HDS_SlotEnumDeleteCurrent( 
    IN  DRM_HDS_ENUM_CONTEXT *pcontextEnum,       /* setup by DRM_HDS_InitSlotEnum() */
    IN  DRM_HDS_SLOT_CONTEXT *pcontextSlot)       /* user given Dataslot context buffer containing the current open slot*/
{
    DRM_RESULT    dr = DRM_SUCCESS;    
    _EnumContext *pEnum    = (_EnumContext *) pcontextEnum;
    _SlotContext *pSlotCtx = (_SlotContext *) pcontextSlot;
    DEFINE_LOCK_VARS

    ChkArg(pEnum != NULL
       &&  pEnum->pNS != NULL
       &&  ISVALIDCONTEXT(pEnum,      eEnumContextSignature)
       &&  ISVALIDCONTEXT(pEnum->pNS, eCfgContextSignature)
       &&  pcontextSlot != NULL
       &&  ISVALIDCONTEXT(pSlotCtx, eSlotContextSignature));
    
    DRMASSERT(pEnum->pNS->fInited 
          &&  pEnum->pNS->pHDS  != NULL
          &&  pEnum->pNS->pHDS->fp != OEM_INVALID_HANDLE_VALUE 
          &&  pcontextSlot != NULL
          &&  pSlotCtx->pNS 
          &&  pSlotCtx->pNS->pHDS 
          &&  pSlotCtx->pNS->pHDS->fp!=OEM_INVALID_HANDLE_VALUE);

    if (pSlotCtx->eStatus != eSlotCtxReady
     || !pEnum->fCurrBlockIsValid)
    {
        ChkDR(DRM_E_HDSSLOTNOTFOUND);
    }    

    /* Lock SRN SHARED among other processes. */
    ENTER_CS_AND_LOCK_SRN(pEnum->pNS->pHDS, (pEnum->eLockMode & (~eDRM_HDS_LOCKEXCLUSIVE)));
    
    dr = _HdsSlotEnumDeleteCurrent(pEnum, pSlotCtx);

ErrorExit:

    UNLOCK_SRN_AND_LEAVE_CS(pEnum->pNS->pHDS);
    return dr;
}



DRM_RESULT DRM_API DRM_HDS_SlotEnumNext( 
    IN  DRM_HDS_ENUM_CONTEXT *pcontextEnum,       /* setup by DRM_HDS_InitSlotEnum() */
    OUT DRM_HDS_SLOT_CONTEXT *pcontextSlot,       /* user given Dataslot context buffer */
    OUT DRM_HDS_HASHKEY      *pHashKey,           /* optional parameter */
    OUT DRM_HDS_UNIQUEKEY    *pUniqueKey,         /* optional parameter */
    OUT DRM_DWORD            *pcbSize)            /* current dataslot size, optional parameter */
{
    DRM_RESULT    dr = DRM_SUCCESS;    
    _EnumContext *pEnum    = (_EnumContext *) pcontextEnum;
    _SlotContext *pSlotCtx = (_SlotContext *) pcontextSlot;
    DRM_BOOL      fValid   = FALSE;
    DRM_BOOL      fSlotLocked = FALSE;    
    DEFINE_LOCK_VARS

    DRM_PROFILING_ENTER_SCOPE(L"DRM_HDS_SlotEnumNext", g_pwszEnteringFunction, DRM_PROFILING_DONT_START);
    ChkArg(pEnum != NULL
       &&  pEnum->pNS != NULL
       &&  ISVALIDCONTEXT(pEnum,      eEnumContextSignature)
       &&  ISVALIDCONTEXT(pEnum->pNS, eCfgContextSignature));
    DRMASSERT(pEnum->pNS->fInited 
          &&  pEnum->pNS->pHDS  != NULL
          &&  pEnum->pNS->pHDS->fp != OEM_INVALID_HANDLE_VALUE 
          &&  pcontextSlot != NULL);

    if ( !pEnum->fCurrBlockIsValid )
    {
        dr = DRM_E_NOMORE;
        goto ErrorExit;
    }

    while (TRUE)
    {
        /* Lock SRN SHARED among other processes. */
        ENTER_CS_AND_LOCK_SRN(pEnum->pNS->pHDS, (pEnum->eLockMode & (~eDRM_HDS_LOCKEXCLUSIVE)));
        
        /* locate and open the slot */
        ChkDR(_HdsSlotEnumNext(pEnum, pSlotCtx, pHashKey, pUniqueKey, pcbSize));

        /* drop all the locks and CS to allow concurrency to occur 
        ** while waiting for the slot lock */
        UNLOCK_SRN_AND_LEAVE_CS(pEnum->pNS->pHDS);
        
        /* issue specific slot lock */
        ChkDR(_HdsLockSlot(pSlotCtx, pEnum->eLockMode));
        fSlotLocked = TRUE;
            
        /* we got the lock to the slot, let's verify the sanity of the slot 
        ** we need to enter CS and lock SRN again */
        ENTER_CS_AND_LOCK_SRN(pEnum->pNS->pHDS, (pEnum->eLockMode & (~eDRM_HDS_LOCKEXCLUSIVE)));
        
        ChkDR(_HdsVerifySlotContext(pSlotCtx, &fValid));
        
        /* drop all the locks and CS again */
        UNLOCK_SRN_AND_LEAVE_CS(pEnum->pNS->pHDS);
       
        if ( fValid )
        {
            break;
        }
        
        /* slot context verified failed, try again */
        (void)_HdsUnlockSlot(pSlotCtx);
        fSlotLocked = FALSE;
    }

ErrorExit:

    if ( pEnum != NULL  && pEnum->pNS )
    {
        if ( DRM_FAILED(dr)  &&  fSlotLocked )
        {
            /* error occured during verifying slot context, return error */
            _HdsUnlockSlot(pSlotCtx);
        }
        UNLOCK_SRN_AND_LEAVE_CS(pEnum->pNS->pHDS);
    }

    if (DRM_FAILED(dr))            
    {
        /* trash the slot context and return */
        ZEROMEM(pSlotCtx, pSlotCtx->dwContextSize);
    }
    DRM_PROFILING_LEAVE_SCOPE(L"DRM_HDS_SlotEnumNext", g_pwszLeavingFunction);
        
    return dr;
}

DRM_RESULT DRM_API DRM_HDS_BlockScanDelete(
    IN       DRM_HDS_NAMESPACE_CONTEXT *f_pcontextNS,
    IN const DRM_HDS_HASHKEY           *f_pkeyHash, 
    IN const DRM_HDS_UNIQUEKEY          f_rgkeyUnique [],
    IN const DRM_DWORD                  f_cKeysUnique,
    IN const DRM_BOOL                   f_fWait,
       OUT   DRM_HDS_BLOCKSCAN_CONTEXT *f_pcontextBlockScan)
{
    DRM_RESULT dr = DRM_SUCCESS;
    _NsContext        *pcontextNS = (_NsContext   *)      f_pcontextNS;
    _BlockScanContext *pcontextBS = (_BlockScanContext *) f_pcontextBlockScan;
    DRM_HDS_LOCKMODE   lockmode   = eDRM_HDS_LOCKEXCLUSIVE;
    DRM_BOOL           fOK               = FALSE;
    DRM_BOOL           fLocked           = FALSE;
    DRM_HDS_HASHKEY   *pkeyHash          = NULL;
    DRM_HDS_UNIQUEKEY *pkeyUnique        = NULL;
    _CommBlockHDR     *pblockheaderNext  = NULL;
    _CommBlockHDR     *pblockheaderTemp  = NULL;
    _CommBlockHDR     *pBlock            = NULL;
    _CommBlockHDR     *pCurrBlock        = NULL;
    _ChildBlockHDR    *pblockheaderChild = NULL;
    _SlotContext      *pcontextSlot      = NULL;
    _SlotContext      *pcontextSlotLock  = NULL;
    DEFINE_LOCK_VARS;

    ChkArg(f_pcontextNS        != NULL
        && f_pcontextBlockScan != NULL);

    ChkArg(pcontextNS->pHDS     != NULL
       &&  pcontextNS->pHDS->fp != OEM_INVALID_HANDLE_VALUE);

    ChkArg(f_pkeyHash    != NULL
        && f_rgkeyUnique != NULL
        && f_cKeysUnique  > 0);

    ChkArg(ISVALIDCONTEXT(pcontextNS,       eCfgContextSignature) 
       &&  ISVALIDCONTEXT(pcontextNS->pHDS, eHdsContextSignature));

    if (f_fWait)
    {
        lockmode |= eDRM_HDS_LOCKWAIT;
    }

    /* Lock SRN SHARED among other processes. */

    ENTER_CS_AND_LOCK_SRN(pcontextNS->pHDS, eDRM_HDS_LOCKSHARED);

    ChkDR(_HdsBlockScanInit  (pcontextNS, f_pkeyHash, f_rgkeyUnique, f_cKeysUnique, lockmode, pcontextBS));

    /* 
    ** Verify validity of the current block of the slot context
    */

    pBlock            = &pcontextBS->blockheader;
    pCurrBlock        = &pcontextBS->blockheaderCurrent;
    pblockheaderChild = (_ChildBlockHDR *) pCurrBlock;

    /* load generic block header */
    ChkDR(_HdsGetPutBlockHDR(pcontextBS->pcontextNS, 
                             pcontextBS->blockheader.nBlockNum, 
                            &pBlock, 
                             GPBH_GENERIC_ONLY, 
                             GPBH_OPERATION_READ));

    pkeyHash = &pcontextBS->keyHash;

    ChkDR(_Hds_malloc(pcontextBS->pcontextNS->pHDS, 
  CALC_SLOTCONTEXTLEN(pcontextBS->pcontextNS), 
       (DRM_VOID **) &pcontextSlot));

    ChkDR(_Hds_malloc(pcontextBS->pcontextNS->pHDS, 
  CALC_SLOTCONTEXTLEN(pcontextBS->pcontextNS), 
       (DRM_VOID **) &pcontextSlotLock));

    ChkDR(_HdsAllocBlockBuffer(pcontextBS->pcontextNS, eCHILDBLOCK, &pblockheaderNext));

    while (dr != DRM_E_NOMORE)
    {
        DRM_BOOL fAnyDeletedFromBlock = FALSE;
        DRM_BOOL fFound               = TRUE;
        
        fLocked = FALSE;

        /* unlock the SHARED from above or the EXCLUSIVE (below) upon loop */

        UNLOCK_SRN_AND_LEAVE_CS(pcontextNS->pHDS);

        /* Lock SRN exclusively among other processes. */

        ENTER_CS_AND_LOCK_SRN(pcontextNS->pHDS, eDRM_HDS_LOCKEXCLUSIVE | eDRM_HDS_LOCKWAIT);

        while (fFound)
        {
            ChkDR(_HdsInitSlotContext(pcontextBS->pcontextNS, 
                         (DRM_BYTE *) pcontextSlot, 
                                      CALC_MAXSLOTCONTEXTLEN));

            /* search current block for any matching UNIQUEs under the HASH */

            ChkDR(_HdsSearchSlotInBlock(&pcontextBS->blockheaderCurrent,
                                         pkeyHash,
                                         NULL,
                                         eSearchSlotDontCare,
                                         pcontextSlot,
                                        &fFound));
            if (fFound)
            {
                if (_IsMatchingKey(&pcontextSlot->oSlotHeader.oUniquekey,
                                    pcontextBS->pkeyUnique,
                                    pcontextBS->cKeysUnique,
                                    pcontextBS->iKeyCurrent))
                {
                    /* mark the slot as hidden and record that we dirtied this block */

                    ChkDR(_HdsRemoveSlot(pcontextSlot, 
                                         eRemoveSlotSetHidden));

                    fAnyDeletedFromBlock = TRUE;
                    pcontextBS->iKeyCurrent++;
                    pcontextBS->cKeysFound++;

                    /* if we've already found all the keys we can complete this loop and exit both loops */

                    if (pcontextBS->cKeysFound == pcontextBS->cKeysUnique)
                    {
                        fFound = FALSE;
                        dr     = DRM_E_NOMORE;
                    }
                }
            }
        }

        /* if any hidden slots were found for this HASKEY then remove those slots */

        if (fAnyDeletedFromBlock)
        {
            DRM_RESULT drCoalesce = _CoalesceBlock(&pcontextBS->blockheaderCurrent, 
                                                    pcontextSlotLock,
                                                    pkeyHash,
                                                    pcontextBS->lockmode,
                                                   &fLocked);

            if (DRM_FAILED(drCoalesce))
            {
                ChkDR(drCoalesce);
            }
        }

        if (dr != DRM_E_NOMORE)
        {
            /* open the next block and continue */

            ChkDR(_HdsHashToChildBlock(pcontextBS->pcontextNS,
                                      &pcontextBS->blockheaderCurrent,
                                       pcontextBS->keyHash.rgb,
                                      &pblockheaderNext,
                                      &fOK,
                                       NULL));

            /* reload the next block and make sure it has the same parent as before */

            pBlock = &pcontextBS->blockheader;

            ChkDR(_HdsGetPutBlockHDR(pcontextBS->pcontextNS, 
                                     pblockheaderNext->nBlockNum, 
                                    &pBlock, 
                                    GPBH_GENERIC_ONLY, 
                                    GPBH_OPERATION_READ));

            if (fLocked)
            {
                ChkDR(_HdsUnlockBlock2DeleteSlot(pcontextSlotLock));
                fLocked = FALSE;
            }

            if (fOK)
            {
                /* the child block becomes the current block */

                ChkDR(_HdsCopyBlockBuffer(&pcontextBS->blockheaderCurrent, pblockheaderNext));

                MEMCPY(&pcontextBS->blockheader, &pcontextBS->blockheaderCurrent, SIZEOF(_CommBlockHDR));
                MEMCPY(&pcontextBS->slotheader,  &pcontextSlot->oSlotHeader,      SIZEOF(_SlotHeader));
            }
            else
            {
                UNLOCK_SRN_AND_LEAVE_CS(pcontextBS->pcontextNS->pHDS)

            /* no more block found */
                dr = DRM_E_NOMORE;
            }
        } /* end if dr != E_NOMORE */
    } /* end block loop */

    if (dr == DRM_E_NOMORE)
    {
        dr = DRM_SUCCESS;
    }
ErrorExit:

    if (pblockheaderNext != NULL)
    {
        _Hds_free(pcontextBS->pcontextNS->pHDS, pblockheaderNext);
    }

    if (pcontextSlotLock != NULL)
    {
        if (fLocked)
        {
            _HdsUnlockBlock2DeleteSlot(pcontextSlotLock);
        }

        _Hds_free(pcontextBS->pcontextNS->pHDS, pcontextSlotLock);
    }

    if (pcontextSlot != NULL)
    {
        _Hds_free(pcontextBS->pcontextNS->pHDS, pcontextSlot);
    }

    if (pblockheaderTemp != NULL)
    {
        _Hds_free(pcontextBS->pcontextNS->pHDS, pblockheaderTemp);
    }

    if (pcontextNS != NULL)
    {
        UNLOCK_SRN_AND_LEAVE_CS(pcontextNS->pHDS);  /* unlock SRN */
    }

    return dr;
}

