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


/*
** BUGBUG:
** - init muti-access in _SlotContext and _ChildBlockHDR using ui64TimeStamp.
** - Delete a Namespace may conflict with an opened Namespace.
*/

/* 
** Rules:
** - parent of any FreeBlock is SRN (ROOTPARENTNUM, 0x00)
** - parent of any Namespace Root block is SRN (ROOTPARENTNUM, ie: 0x00)
** - The NamespaceStore's Root block will not be free'd even when it is empty.
** - The Namespace Root block of any Namespaces will not be free'd even when it is empty.
** - When deleting a namespace and its blocks, ALL of the blocks including the Root will be free'd.
** ? Each block header contains the namespace's HashKey that it belongs for error checking.
**
*/

#include <drmcommon.h>
#include <drmutilities.h>
#include <drmcrt.h>
#include <drmcontextsizes.h>
#include <oemimpl.h>
#include <drmsha1.h>
#include <drmhds.h>
#include "drmhds_impl.h"


/* undefine to print trace messages for lock/unlock functions */
/*#define _TRACELOCK */


#define ISOVERSIZESLOT(slotsize, cfg) \
    ((SIZEOF(_SlotHeader)+(slotsize))>(cfg)->nImagesize_ChildBlockPayload?TRUE:FALSE)

static const DRM_WORD  NsStoreNumofChildren=16;
static const DRM_BYTE  BLOCK_SIGNATURE [] = 
{
    TWO_BYTES(0xFF, 0xFE), TWO_BYTES(0xFD, 0xFC)
};
static const DRM_WORD  COPYBUFF_SIZE      = 1024;

static const DRM_CHAR  DEFAULTNS_HASHKEY1 [] = { TWO_BYTES( 'D', 'R'), TWO_BYTES( 'M', '_'),  TWO_BYTES( 'H', 'D'), TWO_BYTES( 'S', '_'), TWO_BYTES( 'H', 'A'), TWO_BYTES( 'S', 'H'), TWO_BYTES( 'K', 'E'), TWO_BYTES( 'Y', '1') };
static const DRM_CHAR  DEFAULTNS_HASHKEY2 [] = { TWO_BYTES( 'D', 'R'), TWO_BYTES( 'M', '_'),  TWO_BYTES( 'H', 'D'), TWO_BYTES( 'S', '_'), TWO_BYTES( 'H', 'A'), TWO_BYTES( 'S', 'H'), TWO_BYTES( 'K', 'E'), TWO_BYTES( 'Y', '2') };


#define DRM_HDS_FILE_CURR_VERSION   0x000A0000
#define HDS_FILEHEADERSIZE          4096



/* forward declaration */
static DRM_RESULT _HdsRemoveSlot( 
    _SlotContext    *pSlotCtx,
    _ERemoveSlotMode eMode);

static DRM_RESULT _HdsBlockNum2FilePos(
    IN  _HdsContext *pHDS,
    IN  DRM_DWORD    nBlkNum, 
    OUT DRM_DWORD   *pnFilePos);

static DRM_RESULT _HdsPreAlloc( 
    IN  _HdsContext *f_pHDS,
    IN  DRM_DWORD    f_PreAllocFileSizeInKB,
    IN  DRM_BOOL     f_fUpToSpecifiedSize,
    OUT DRM_DWORD   *f_pnNextBlockNum);



/*
** Validate an _NSEntry from disk
*/
static DRM_RESULT _HdsValidateNSEntryFromDisk( 
    _HdsContext *f_pHDS,
    _NSEntry    *f_pNsEntry )
{
    DRM_RESULT dr = DRM_SUCCESS;
    DRM_DWORD dwFilePos  = 0;
    DRM_DWORD dwFileSize = 0;
    
    DRMASSERT( f_pHDS     != NULL
        &&     f_pNsEntry != NULL );

    /* verify # of child blocks in Alloc table */
    if(  f_pNsEntry->wMaxNumChildren == 0 
      || f_pNsEntry->wMaxNumChildren > DRM_HDS_MAXIMUM_CHILDREN 
      || f_pNsEntry->nNSRBlockNum    == 0 )
    {
        ChkDR( DRM_E_HDSFILECORRUPTED );
    }

    /* validate root block# */
    ChkDR(_HdsBlockNum2FilePos(f_pHDS, f_pNsEntry->nNSRBlockNum, &dwFilePos));
    ChkBOOL(OEM_GetFileSize(f_pHDS->fp, &dwFileSize), DRM_E_HDSFILECORRUPTED);
    ChkBOOL((dwFilePos < dwFileSize), DRM_E_HDSFILECORRUPTED);
    
ErrorExit:   

    return dr;
}

/*
** Compute the block# from a block's given file pos.
*/
static DRM_RESULT _HdsFilePos2BlockNum(
    IN  _HdsContext *pHDS,
    IN  DRM_DWORD    dwFilePos,
    OUT DRM_DWORD   *pnBlockNum)
{
    DRM_RESULT dr=DRM_SUCCESS;
    DRM_DWORD  nBlockNum=0;

    ChkArg(ISVALIDCONTEXT(pHDS, eHdsContextSignature));
    DRMASSERT(pHDS && pHDS->fInited &&  pnBlockNum);
    if ( dwFilePos < HDS_FILEHEADERSIZE )
    {
        DRMASSERT(FALSE);
    }

    nBlockNum = (dwFilePos - HDS_FILEHEADERSIZE) 
        / pHDS->oSRN.dwBlockSize;
        
    ++nBlockNum;    /* any block other than SRN starts from 1 */

    /* mask the block# according the BlockNumType */
    switch(pHDS->oSRN.eBlockNumType)
    {
        case eDRM_HDSBLKNUM_WORD:
            nBlockNum &= 0x0000FFFF;
            break;
        default:
            break;
    }
    
    *pnBlockNum = nBlockNum;

ErrorExit:
    
    return dr;
}

/*
** Compute the file pos of a given block
*/
static DRM_RESULT _HdsBlockNum2FilePos(
    IN  _HdsContext *pHDS,
    IN  DRM_DWORD    nBlkNum, 
    OUT DRM_DWORD   *pnFilePos)
{
    DRM_RESULT dr = DRM_SUCCESS;
    DRM_DWORD dwFilePos = 0;

    ChkArg(ISVALIDCONTEXT(pHDS, eHdsContextSignature));
    DRMASSERT(pHDS && pHDS->fInited && pnFilePos);

    dwFilePos = HDS_FILEHEADERSIZE; 
    dwFilePos += (nBlkNum-1) * pHDS->oSRN.dwBlockSize;
    *pnFilePos = dwFilePos;

ErrorExit:    
    return dr;
}


/**********************************************************************
** Function:    _HdsLockSRN
** Synopsis:    Accquire lock to the Super Root Node 
** Arguments:   [pHDS] -- 
**              [eMode] --
** Returns:     DRM_SUCCESS on success
** Notes:       
***********************************************************************
*/
static DRM_RESULT _HdsLockSRN(
    _HdsContext *pHDS,
    DRM_DWORD    eMode)
{
#if _MULTI_THREADING_SUPPORT==1

    DRM_RESULT dr=DRM_SUCCESS;

#ifdef _TRACELOCK
    TRACE(("LockSRN issued: %c%c\n", 
           (eMode & eDRM_HDS_LOCKEXCLUSIVE)? 'X' : 'S',
           (eMode & eDRM_HDS_LOCKWAIT)? 'W' : ' '));
#endif
    /*
    **  All calls to lock the SRN should wait regardless of the wait mode passed
    **  into the various API.  It makes sense that the caller wants control over
    **  whether to wait for a slot lock since those locks are long lasting (and
    **  under control of the calling application).  But the user doesn’t want 
    **  control over a frequently grabbed, internal, short-term lock like the 
    **  SRN.  Doing so will result is random failures that the caller won’t be 
    **  able to understand.
    */
    if ( !OEM_LockFile( pHDS->fp, 
                        ((eMode & eDRM_HDS_LOCKEXCLUSIVE) != 0), 
                        0, 
                        HDS_FILEHEADERSIZE,
                        TRUE ) )
    {
        ChkDR(DRM_E_HDSLOCKFAILED);
    }
#ifdef _TRACELOCK
    TRACE(("LockSRN obtained\n"));
#endif    
ErrorExit:
    return dr;
    
#else
    return DRM_SUCCESS;
#endif
}


/**********************************************************************
** Function:    _HdsUnlockSRN
** Synopsis:    Release lock to a specific store block 
** Arguments:   [pHDS] -- 
** Returns:     DRM_SUCCESS on success
** Notes:       
***********************************************************************
*/
static DRM_RESULT _HdsUnlockSRN(
    _HdsContext *pHDS)
{
#if _MULTI_THREADING_SUPPORT==1

    DRM_RESULT dr=DRM_SUCCESS;

    if ( pHDS->fp == OEM_INVALID_HANDLE_VALUE )
    {
        ChkDR(DRM_E_HDSLOCKFAILED);
    }

#ifdef _TRACELOCK
    TRACE(("\tUnlockSRN issued \n"));
#endif
    if (!OEM_UnlockFile(pHDS->fp, 0, HDS_FILEHEADERSIZE))
    {
        ChkDR(DRM_E_HDSLOCKFAILED);
    }

ErrorExit:
    return dr;
    
#else
    return DRM_SUCCESS;
#endif
}

/**********************************************************************
** Function:    _HdsLockStore
** Synopsis:    Accquire lock for the store 
** Arguments:   [pHDS] -- 
**              [fExclusive] --
** Returns:     DRM_SUCCESS on success
** Notes:       It is essentially the same as _HdsLockSlot where we are
**              grabbing a lock on a bigger slot (all the blocks)
***********************************************************************
*/
static DRM_RESULT _HdsLockStore(
    _HdsContext *pHDS,
    DRM_DWORD    eMode)
{
#if _MULTI_THREADING_SUPPORT==1

    DRM_RESULT dr=DRM_SUCCESS;
    DRM_DWORD  dwSRN = HDS_FILEHEADERSIZE;

#ifdef _TRACELOCK
    TRACE(("LockStore issued: %c\n", (eMode & eDRM_HDS_LOCKEXCLUSIVE) != 0 ? 'X' : 'S'));
#endif
    
    /* lock the whole file from SRN */
    if (!OEM_LockFile(
            pHDS->fp, 
            ((eMode & eDRM_HDS_LOCKEXCLUSIVE) != 0),
            dwSRN, 
            0xFFFFFFFF-dwSRN,
            ((eMode & eDRM_HDS_LOCKWAIT) != 0) ) )
    {
        ChkDR(DRM_E_HDSLOCKFAILED);
    }
#ifdef _TRACELOCK
    TRACE(("LockStore obtained\n"));
#endif    
    
ErrorExit:
    return dr;
#else
    return DRM_SUCCESS;
#endif
}


/**********************************************************************
** Function:    _HdsUnlockStore
** Synopsis:    Release lock for the store 
** Arguments:   [pHDS] -- 
**              
** Returns:     DRM_SUCCESS on success
** Notes:       
***********************************************************************
*/
static DRM_RESULT _HdsUnlockStore(
    _HdsContext *pHDS )
{
#if _MULTI_THREADING_SUPPORT==1
    DRM_RESULT dr=DRM_SUCCESS;
    DRM_DWORD  dwSRN = HDS_FILEHEADERSIZE;

    /* unlock the whole file from SRN */
#ifdef _TRACELOCK
    TRACE(("\tUnlockStore issued\n"));
#endif
    if (!OEM_UnlockFile(pHDS->fp, dwSRN, 0xFFFFFFFF-dwSRN))
    {
        ChkDR(DRM_E_HDSLOCKFAILED);
    }

ErrorExit:
    return dr;
#else
    return DRM_SUCCESS;
#endif
}

/**********************************************************************
** Function:    _HdsLockSlot
** Synopsis:    Accquire lock to a specific slot 
** Arguments:   [pSlotCtx] -- 
**              [eMode] --
** Returns:     DRM_SUCCESS on success
** Notes:       The slot header will only be locked for SHARED. THis is
**              necessary for making Search to work.
***********************************************************************
*/
static DRM_RESULT _HdsLockSlot(
    _SlotContext *pSlotCtx,
    DRM_DWORD     eMode)
{
#if _MULTI_THREADING_SUPPORT==1

    DRM_RESULT dr           = DRM_SUCCESS;
    DRM_DWORD  dwFilePos    = 0;
    DRM_LONG   dwLockSize   = 0;

    /* compute filepos of the block */
    ChkDR(_HdsBlockNum2FilePos(pSlotCtx->pNS->pHDS, 
        pSlotCtx->pCurrChildBlock->nBlockNum, &dwFilePos));

    /* adjust filepos relative to the block */
    dwFilePos += pSlotCtx->pNS->nChildBlockPayloadPos;
    dwFilePos += pSlotCtx->dwSlotPosInBlock;
    dwFilePos += SIZEOF(_SlotHeader);
    
    if ( ISOVERSIZESLOT(pSlotCtx->oSlotHeader.dwSlotSize, pSlotCtx->pNS) )
    {
        dwLockSize = SIZEOF(DRM_DWORD);
    }
    else
    {
        dwLockSize = pSlotCtx->oSlotHeader.dwSlotSize;
    }

#ifdef _TRACELOCK
    TRACE(("LockSlot issued:  %c%c - pos %d,  size %d\n", 
           (eMode & eDRM_HDS_LOCKEXCLUSIVE)? 'X' : 'S', 
           (eMode & eDRM_HDS_LOCKWAIT)? 'W' : ' ', 
           dwFilePos, 
           dwLockSize));
#endif

    /* lock slot content */
    if ( !OEM_LockFile( pSlotCtx->pNS->pHDS->fp, 
                        ((eMode & eDRM_HDS_LOCKEXCLUSIVE) != 0), 
                        dwFilePos, 
                        dwLockSize,
                        ( (eMode & eDRM_HDS_LOCKWAIT) != 0 ) ) )
    {
        ChkDR(DRM_E_HDSLOCKFAILED);
    }
    pSlotCtx->eLockMode = eMode;
    
#ifdef _TRACELOCK
    TRACE(("LockSlot obtained\n"));
#endif    

ErrorExit:
    return dr;
    
#else

    pSlotCtx->eLockMode = eMode;
    return DRM_SUCCESS;
    
#endif
}


/**********************************************************************
** Function:    _HdsUnlockSlot
** Synopsis:    release lock to a specific slot 
** Arguments:   [pSlotCtx] -- 
**              [fExclusive] --
** Returns:     DRM_SUCCESS on success
** Notes:       
***********************************************************************
*/
static DRM_RESULT _HdsUnlockSlot(
    _SlotContext *pSlotCtx)
{
#if _MULTI_THREADING_SUPPORT==1

    DRM_RESULT dr=DRM_SUCCESS;
    DRM_DWORD  dwFilePos=0;
    DRM_LONG   dwLockSize=0;
    DRM_BOOL   fResult = FALSE;
    
    DRMASSERT(pSlotCtx!=NULL);

    /* compute filepos of the block */
    ChkDR(_HdsBlockNum2FilePos(pSlotCtx->pNS->pHDS, 
        pSlotCtx->pCurrChildBlock->nBlockNum, &dwFilePos));

    /* adjust filepos relative to the block */
    dwFilePos += pSlotCtx->pNS->nChildBlockPayloadPos;
    dwFilePos += pSlotCtx->dwSlotPosInBlock;
    dwFilePos += SIZEOF(_SlotHeader);
    
    if ( ISOVERSIZESLOT(pSlotCtx->oSlotHeader.dwSlotSize, pSlotCtx->pNS) )
    {
        dwLockSize = SIZEOF(DRM_DWORD);
    }
    else
    {
        dwLockSize = pSlotCtx->oSlotHeader.dwSlotSize;
    }

#ifdef _TRACELOCK
    TRACE(("\tUnlockSlot issued:  pos %d,  size %d\n", dwFilePos, dwLockSize));
#endif
   
    if ( !OEM_UnlockFile( pSlotCtx->pNS->pHDS->fp,
                          dwFilePos, 
                          dwLockSize ) )
    {
        ChkDR(DRM_E_HDSLOCKFAILED);
    }
    
ErrorExit:
    
    return dr;
    
#else
    return DRM_SUCCESS;
#endif
}


/**********************************************************************
** Function:    _HdsLockBlock2DeleteSlot
** Synopsis:    lock block payload from the given slot to end of block.
**              It is essentially the same as _HdsLockSlot where we are
**              grabbing a lock on a bigger slot.
** Arguments:   [pSlotCtx] -- 
**              [fExclusive] --
** Returns:     DRM_SUCCESS on success
** Notes:       To remove a slot from a block, it is necessary to lock
**              the slot to end of block (to do adjustments).
***********************************************************************
*/
static DRM_RESULT _HdsLockBlock2DeleteSlot(
    _SlotContext *pSlotCtx,
    DRM_DWORD     eMode)
{
#if _MULTI_THREADING_SUPPORT==1

    DRM_RESULT dr=DRM_SUCCESS;
    DRM_DWORD  dwFilePos=0;
    DRM_LONG   dwLockSize=0;

    /* compute filepos of the block */
    ChkDR(_HdsBlockNum2FilePos(pSlotCtx->pNS->pHDS, 
        pSlotCtx->pCurrChildBlock->nBlockNum, &dwFilePos));

    /* adjust filepos relative to the block */
    dwLockSize = dwFilePos + pSlotCtx->pNS->pHDS->oSRN.dwBlockSize;
    dwFilePos += (pSlotCtx->pNS->nChildBlockPayloadPos + pSlotCtx->dwSlotPosInBlock);
    dwLockSize -= dwFilePos;

#ifdef _TRACELOCK
    TRACE(("LockSlot2RestOfBlock issued:  %c%c - pos %d,  size %d\n", 
          (eMode & eDRM_HDS_LOCKEXCLUSIVE)? 'X' : 'S', 
          (eMode & eDRM_HDS_LOCKWAIT)? 'W' : ' ', 
          dwFilePos, 
          dwLockSize));
#endif

    /* lock it */
    if ( !OEM_LockFile(
            pSlotCtx->pNS->pHDS->fp, 
            ((eMode & eDRM_HDS_LOCKEXCLUSIVE) != 0), 
            dwFilePos, 
            dwLockSize,
            ((eMode & eDRM_HDS_LOCKWAIT) != 0) ) )
    {
        ChkDR(DRM_E_HDSLOCKFAILED);
    }
#ifdef _TRACELOCK
    TRACE(("LockSlot2RestOfBlock obtained\n"));
#endif    

ErrorExit:
    return dr;
    
#else

    return DRM_SUCCESS;
    
#endif
}


static DRM_RESULT _HdsUnlockBlock2DeleteSlot(
    _SlotContext *pSlotCtx)
{
#if _MULTI_THREADING_SUPPORT==1

    DRM_RESULT dr=DRM_SUCCESS;
    DRM_DWORD  dwFilePos=0;
    DRM_LONG   dwLockSize=0;
    DRM_BOOL   fResult = FALSE;

    /* compute filepos of the block */
    ChkDR(_HdsBlockNum2FilePos(pSlotCtx->pNS->pHDS, 
        pSlotCtx->pCurrChildBlock->nBlockNum, &dwFilePos));

    /* adjust filepos relative to the block */
    dwLockSize = dwFilePos + pSlotCtx->pNS->pHDS->oSRN.dwBlockSize;
    dwFilePos += (pSlotCtx->pNS->nChildBlockPayloadPos+pSlotCtx->dwSlotPosInBlock);
    dwLockSize -= dwFilePos;

#ifdef _TRACELOCK
    TRACE(("_HdsUnlockBlock2DeleteSlot issued:  - pos %d,  size %d\n", dwFilePos, 
        dwLockSize));
#endif

    /* unlock it, do not return even if this fail */
    fResult = OEM_UnlockFile(pSlotCtx->pNS->pHDS->fp, dwFilePos, dwLockSize);

    if ( !fResult )
    {
        dr = DRM_E_HDSLOCKFAILED;
    }

ErrorExit:
    return dr;
    
#else

    return DRM_SUCCESS;
    
#endif
}


/**********************************************************************
** Function:    _Hds_malloc
** Synopsis:    HDS Component memory allocation redirector
** Arguments:   [pHDS] -- 
**              [cbBuffer] --
**              [ppbBuffer] --
** Returns:     DRM_SUCCESS on success
** Notes:       
***********************************************************************
*/
static DRM_RESULT _Hds_malloc(
    _HdsContext *pHDS,
    DRM_DWORD    cbBuffer,
    DRM_VOID   **ppbBuffer)
{
    DRM_RESULT dr=DRM_SUCCESS;
    DRMASSERT(pHDS && ppbBuffer && cbBuffer);
    
    ChkDR(DRM_STK_Alloc(&pHDS->oHeap, cbBuffer, ppbBuffer));

ErrorExit:
    return dr;
}


/**********************************************************************
** Function:    _Hds_free
** Synopsis:    HDS Component memory allocation redirector
** Arguments:   [pHDS] -- 
**              [pBuffer] --
** Returns:     DRM_SUCCESS on success
** Notes:       
***********************************************************************
*/
static DRM_RESULT _Hds_free(
    _HdsContext *pHDS,
    DRM_VOID    *pBuffer)
{
    DRMASSERT(pHDS!=NULL);

    if (pBuffer==NULL)
    {
        return DRM_SUCCESS;
    }
        
    return DRM_STK_Free(&pHDS->oHeap, pBuffer);
}



/**********************************************************************
** Function:    _GetTimeStamp
** Synopsis:    Generate 8-byte timestamp
** Arguments:   [pdwStamp] -- 
** Returns:     DRM_SUCCESS on success
** Notes:       
***********************************************************************
*/
static DRM_UINT64 _GetTimeStamp()
{
    DRM_UINT64  u64      = { 0 };
    DRMFILETIME filetime = { 0, 0 };

    OEM_GetDeviceTime( &filetime );

    FILETIME_TO_UI64( filetime, u64 );

    return u64;
}


/**********************************************************************
** Function:    _IsNULL
** Synopsis:    check if the given byte array contains all NULL bytes.
** Arguments:   [pbBuff] -- byte array to be checked
**              [cbBuff[ -- size of pbBuff
** Returns:     TRUE if all NULL
** Notes:       
***********************************************************************
*/
static DRM_BOOL _IsNULL(
    const DRM_BYTE *pbBuff,
          DRM_DWORD cbBuff)
{
    DRM_DWORD i=0;

    for (i=0; i<cbBuff; i++)
    {
        if (GET_BYTE(pbBuff,i) != 0)
        {
            return FALSE;
        }
    }

    return TRUE;
}

/*
** generate hash keys for namespace name
*/
static DRM_VOID _GenNamespaceKeys(
    IN DRM_MD5_CTX *pcontextMD5,
    IN const DRM_CHAR* pbNamespace,
    IN DRM_WORD cbNamespace,
    OUT DRM_BYTE bNsHashKey   [__CB_DECL(DRM_HDS_HASHKEY_SIZE)], 
    OUT DRM_BYTE bNsUniqueKey [__CB_DECL(DRM_HDS_UNIQUEKEY_SIZE)])
{
    DRM_MD5_Init(pcontextMD5 );
    DRM_MD5_Update(pcontextMD5, (DRM_VOID*)pbNamespace, cbNamespace);
    DRM_MD5_Update(pcontextMD5, (DRM_VOID*)DEFAULTNS_HASHKEY1, SIZEOF(DEFAULTNS_HASHKEY1) );
    DRM_MD5_Final( pcontextMD5 );

	MEMCPY(bNsHashKey, pcontextMD5->digest, DRM_HDS_HASHKEY_SIZE);

    DRM_MD5_Init(pcontextMD5 );
    DRM_MD5_Update(pcontextMD5, (DRM_VOID*)pbNamespace, cbNamespace);
    DRM_MD5_Update(pcontextMD5, (DRM_VOID*)DEFAULTNS_HASHKEY2, SIZEOF(DEFAULTNS_HASHKEY2) );
    DRM_MD5_Final( pcontextMD5 );
	
    MEMCPY(bNsUniqueKey, pcontextMD5->digest, DRM_HDS_HASHKEY_SIZE);
}



/**********************************************************************
** Function:    _GenSRNHash
** Synopsis:    Generate hash of the Super Root Node 
** Arguments:   [pLSContext] -- 
**              [hash] --
** Returns:     DRM_SUCCESS on success
** Notes:       
***********************************************************************
*/
static DRM_RESULT _HdsGenSRNHash(
    IN  _HdsContext *pHDS,
    OUT DRM_BYTE     hash [__CB_DECL(MD5DIGESTLEN)])
{
    DRM_RESULT dr=DRM_SUCCESS;
    DRM_DWORD dwByte;

    ChkArg(ISVALIDCONTEXT(pHDS, eHdsContextSignature));
    DRMASSERT(pHDS && pHDS->fInited);
    DRM_MD5_Init( &pHDS->contextMD5 );

    BYTES_TO_DWORD( dwByte, ((DRM_BYTE*)&(pHDS->oSRN.dwSRNSize)) );
    DRM_MD5_Update(&pHDS->contextMD5, (DRM_VOID*)&dwByte, SIZEOF(DRM_DWORD));

    BYTES_TO_DWORD( dwByte, ((DRM_BYTE*)&(pHDS->oSRN.dwBlockSize)) );
    DRM_MD5_Update(&pHDS->contextMD5, (DRM_VOID*)&dwByte, SIZEOF(DRM_DWORD));

    BYTES_TO_DWORD( dwByte, ((DRM_BYTE*)&(pHDS->oSRN.eBlockNumType)) );
    DRM_MD5_Update(&pHDS->contextMD5, (DRM_VOID*)&dwByte, SIZEOF(DRM_DWORD));

    BYTES_TO_DWORD( dwByte, ((DRM_BYTE*)&(pHDS->oSRN.nFreeListHead)) );
    DRM_MD5_Update(&pHDS->contextMD5, (DRM_VOID*)&dwByte, SIZEOF(DRM_DWORD));

    BYTES_TO_DWORD( dwByte, ((DRM_BYTE*)&(pHDS->oSRN.nNsStoreRootBlockNum)) );
    DRM_MD5_Update(&pHDS->contextMD5, (DRM_VOID*)&dwByte, SIZEOF(DRM_DWORD));

    DRM_MD5_Final( &pHDS->contextMD5 );
    MEMCPY( hash, pHDS->contextMD5.digest, SIZEOF( pHDS->contextMD5.digest ) );

ErrorExit:
    return dr;
}

/*
**
*/
static DRM_RESULT _HdsInitBlockBuffer(
    IN  _NsContext    *pNS,
    OUT _CommBlockHDR *pBlock,
    IN  DRM_DWORD      nParentBlockNum,
    IN  _EBlockType    eBlockType)
{
    DRM_RESULT dr=DRM_SUCCESS;
    
    ChkArg(ISVALIDCONTEXT(pNS, eCfgContextSignature));
    DRMASSERT(pNS && pNS->fInited && pBlock);

    /* set up the block */
    pBlock->File._image.nParentBlockNum = nParentBlockNum;
    pBlock->File._image.ui64TimeStamp   = _GetTimeStamp();

    PUT_BYTE( &pBlock->File._image.bBlockType, 0, eBlockType );

    if (eBlockType==eCHILDBLOCK)
    {
        _ChildBlockHDR *pChild = (_ChildBlockHDR*)pBlock;
        pChild->nCurrSlotPos = MAXSLOTPOS;
        pChild->nNextSlotPos = MAXSLOTPOS;
        pChild->nPayloadSize = pNS->nImagesize_ChildBlockPayload;
        pChild->File._image.nFreeMem = pNS->nImagesize_ChildBlockPayload;
        ZEROMEM(pChild->File._image.bChildAllocTable, pNS->nImagesize_ChildAllocTable);
    }
    else   /* eDATABLOCK or eFREEBLOCK */
    {
        _DataBlockHDR *pData = (_DataBlockHDR*)pBlock;
        pData->nPayloadSize = pNS->pHDS->nImagesize_DataBlockPayload;
        pData->File._image.nCascadingBlockNum = 0;
    }

ErrorExit:
    return dr;
}

/*
** create a buffer big enough for a store block
*/
static DRM_RESULT _HdsAllocBlockBuffer(
    IN  _NsContext     *pNS,
    IN  _EBlockType     eBlockType,
    OUT _CommBlockHDR **ppBlock)
{
    DRM_RESULT dr=DRM_SUCCESS;
    DRM_DWORD cbBlocksize=0;

    ChkArg(ISVALIDCONTEXT(pNS, eCfgContextSignature));
    DRMASSERT( pNS 
            && pNS->fInited 
            && ppBlock 
            && ( eBlockType==eCHILDBLOCK || eBlockType==eDATABLOCK ) );

    /* allocate buffer for the block */
    if ( eBlockType==eCHILDBLOCK )
    {
        cbBlocksize = GetMemsize_ChildBlock(pNS);
    }
    else /* eDATABLOCK */
    {
        cbBlocksize = GetMemsize_DataBlock();
    }

    ChkDR(_Hds_malloc(pNS->pHDS, cbBlocksize, (DRM_VOID**)ppBlock));
    ChkDR(_HdsInitBlockBuffer(pNS, *ppBlock, 0, eBlockType));

ErrorExit:
    return dr;
}


#define _MEMBER_ENCODE(pb, ib, x) \
    DRM_BYT_CopyBytes(pb, ib, (DRM_BYTE*)&x, 0, SIZEOF(x)); \
    ib += SIZEOF(x)

#define _MEMBER_DECODE(pb, ib, x) \
    DRM_BYT_CopyBytes((DRM_BYTE*)&x, 0, pb, ib, SIZEOF(x)); \
    ib += SIZEOF(x)

static DRM_BOOL _WriteSRN( 
    _HdsContext *pHDS)
{
    DRM_RESULT dr = DRM_SUCCESS;
    DRM_DWORD  cbWritten = 0;
    DRM_BOOL   fOK       = TRUE;
    DRM_BYTE  *pbBuffer  = NULL;
    DRM_DWORD  cbBuffer  = 0;
    DRM_DWORD  ibBuffer  = 0;
    
    FIX_ENDIAN_QWORD( pHDS->oSRN.ui64Timestamp );
    FIX_ENDIAN_DWORD( pHDS->oSRN.dwSRNSize );
    FIX_ENDIAN_DWORD( pHDS->oSRN.dwBlockSize );
    FIX_ENDIAN_DWORD( pHDS->oSRN.nFreeListHead );
    FIX_ENDIAN_DWORD( pHDS->oSRN.nNsStoreRootBlockNum );
    FIX_ENDIAN_DWORD( pHDS->oSRN.nHighestFormattedBlockNum );
    FIX_ENDIAN_DWORD( pHDS->oSRN.eBlockNumType );

    /* this must be updated when the structure of SRN is modified */
    cbBuffer = SIZEOF(DRM_UINT64) + 6 * SIZEOF(DRM_DWORD) + MD5DIGESTLEN;
    dr = _Hds_malloc(pHDS, cbBuffer, (DRM_VOID **) &pbBuffer);
    if ( DRM_FAILED(dr) )
    {
        fOK = FALSE;
        goto ErrorExit;
    }

    /* encode structure to byte array */
    _MEMBER_ENCODE(pbBuffer, ibBuffer, pHDS->oSRN.bSRNHash);
    _MEMBER_ENCODE(pbBuffer, ibBuffer, pHDS->oSRN.dwSRNSize);
    _MEMBER_ENCODE(pbBuffer, ibBuffer, pHDS->oSRN.dwBlockSize);
    _MEMBER_ENCODE(pbBuffer, ibBuffer, pHDS->oSRN.eBlockNumType);
    _MEMBER_ENCODE(pbBuffer, ibBuffer, pHDS->oSRN.ui64Timestamp);
    _MEMBER_ENCODE(pbBuffer, ibBuffer, pHDS->oSRN.nFreeListHead);
    _MEMBER_ENCODE(pbBuffer, ibBuffer, pHDS->oSRN.nNsStoreRootBlockNum);
    _MEMBER_ENCODE(pbBuffer, ibBuffer, pHDS->oSRN.nHighestFormattedBlockNum);

    /* write to file */
    if ( !OEM_WriteFile( pHDS->fp, pbBuffer, cbBuffer, &cbWritten ) 
      || cbWritten != cbBuffer)
    {
        fOK = FALSE;
    }
    
ErrorExit:
    (void)_Hds_free(pHDS, pbBuffer);
    
    FIX_ENDIAN_QWORD( pHDS->oSRN.ui64Timestamp );
    FIX_ENDIAN_DWORD( pHDS->oSRN.dwSRNSize );
    FIX_ENDIAN_DWORD( pHDS->oSRN.dwBlockSize );
    FIX_ENDIAN_DWORD( pHDS->oSRN.nFreeListHead );
    FIX_ENDIAN_DWORD( pHDS->oSRN.nNsStoreRootBlockNum );
    FIX_ENDIAN_DWORD( pHDS->oSRN.nHighestFormattedBlockNum );
    FIX_ENDIAN_DWORD( pHDS->oSRN.eBlockNumType );
    
    return fOK;
}


static DRM_BOOL _ReadSRN( 
    _HdsContext *pHDS)
{
    DRM_RESULT dr = DRM_SUCCESS;
    DRM_DWORD  cbRead = 0;
    DRM_BOOL   fOK = TRUE;
    DRM_BYTE  *pbBuffer  = NULL;
    DRM_DWORD  cbBuffer  = 0;
    DRM_DWORD  ibBuffer  = 0;

    /* this must be updated when the structure of SRN is modified */
    cbBuffer = SIZEOF(DRM_UINT64) + 6 * SIZEOF(DRM_DWORD) + MD5DIGESTLEN;
    dr = _Hds_malloc(pHDS, cbBuffer, (DRM_VOID **) &pbBuffer);
    if ( DRM_FAILED(dr) )
    {
        fOK = FALSE;
        goto ErrorExit;
    }

    /* read byte array from file */
    if ( !OEM_ReadFile( pHDS->fp, pbBuffer, cbBuffer, &cbRead ) 
      || cbRead != cbBuffer)
    {
        fOK = FALSE;
        goto ErrorExit;
    }

    /* decode structure members from byte array */
    _MEMBER_DECODE(pbBuffer, ibBuffer, pHDS->oSRN.bSRNHash);
    _MEMBER_DECODE(pbBuffer, ibBuffer, pHDS->oSRN.dwSRNSize);
    _MEMBER_DECODE(pbBuffer, ibBuffer, pHDS->oSRN.dwBlockSize);
    _MEMBER_DECODE(pbBuffer, ibBuffer, pHDS->oSRN.eBlockNumType);
    _MEMBER_DECODE(pbBuffer, ibBuffer, pHDS->oSRN.ui64Timestamp);
    _MEMBER_DECODE(pbBuffer, ibBuffer, pHDS->oSRN.nFreeListHead);
    _MEMBER_DECODE(pbBuffer, ibBuffer, pHDS->oSRN.nNsStoreRootBlockNum);
    _MEMBER_DECODE(pbBuffer, ibBuffer, pHDS->oSRN.nHighestFormattedBlockNum);

ErrorExit:
    (void)_Hds_free(pHDS, pbBuffer);
    FIX_ENDIAN_QWORD( pHDS->oSRN.ui64Timestamp );
    FIX_ENDIAN_DWORD( pHDS->oSRN.dwSRNSize );
    FIX_ENDIAN_DWORD( pHDS->oSRN.dwBlockSize );
    FIX_ENDIAN_DWORD( pHDS->oSRN.nFreeListHead );
    FIX_ENDIAN_DWORD( pHDS->oSRN.nNsStoreRootBlockNum );
    FIX_ENDIAN_DWORD( pHDS->oSRN.nHighestFormattedBlockNum );
    FIX_ENDIAN_DWORD( pHDS->oSRN.eBlockNumType );
    
    return fOK;
}

static DRM_BOOL _WriteCommonBlockHeader( 
    _HdsContext   *pHDS,
    _CommBlockHDR *pBlock, 
    DRM_DWORD      cbToWrite,
    DRM_DWORD     *pcbWritten)
{
    DRM_RESULT dr = DRM_SUCCESS;
    DRM_DWORD  cbWritten = 0;
    DRM_BOOL   fOK = TRUE;
    DRM_DWORD  ibBuffer = 0;
    DRM_DWORD  cbBuffer = 0;
    DRM_BYTE  *pbBuffer = NULL;
    
    DRMASSERT( pBlock != NULL );

    FIX_ENDIAN_QWORD( pBlock->File._image.ui64TimeStamp );
    FIX_ENDIAN_DWORD( pBlock->File._image.nParentBlockNum );

    /* this must be updated when the structure of _CommBlockHDR is modified */
    cbBuffer = SIZEOF(DRM_UINT64) + MD5DIGESTLEN + SIZEOF(DRM_DWORD) + 1;
    dr = _Hds_malloc(pHDS, cbBuffer, (DRM_VOID **) &pbBuffer);
    if ( DRM_FAILED(dr) )
    {
        fOK = FALSE;
        goto ErrorExit;
    }

    /* encode structure to byte array */
    _MEMBER_ENCODE(pbBuffer, ibBuffer, pBlock->File._image.ui64TimeStamp);
    _MEMBER_ENCODE(pbBuffer, ibBuffer, pBlock->File._image.bBlockHash );
    _MEMBER_ENCODE(pbBuffer, ibBuffer, pBlock->File._image.nParentBlockNum );
    DRM_BYT_CopyBytes(pbBuffer, ibBuffer, &(pBlock->File._image.bBlockType), 0, 1);
    ibBuffer += 1;

    /* write byte array to file */
    if ( !OEM_WriteFile( pHDS->fp, pbBuffer, cbBuffer, &cbWritten ) 
      || cbWritten != cbBuffer)
    {
        fOK = FALSE;
    }

ErrorExit:
    (void)_Hds_free(pHDS, pbBuffer);
    FIX_ENDIAN_QWORD( pBlock->File._image.ui64TimeStamp );
    FIX_ENDIAN_DWORD( pBlock->File._image.nParentBlockNum );

    if ( pcbWritten!= NULL )
    {
        *pcbWritten = cbWritten;
    }
    return fOK;
}

static DRM_BOOL _ReadCommonBlockHeader( 
    _HdsContext   *pHDS,
    _CommBlockHDR *pBlock, 
    DRM_DWORD      cbToRead, 
    DRM_DWORD     *pcbRead )
{
    DRM_RESULT dr  = DRM_SUCCESS;
    DRM_BOOL   fOK = TRUE;
    DRM_DWORD  cbRead   = 0;
    DRM_DWORD  ibBuffer = 0;
    DRM_DWORD  cbBuffer = 0;
    DRM_BYTE  *pbBuffer = NULL;

    DRMASSERT( pBlock != NULL );

    /* this must be updated when the structure of _CommBlockHDR is modified */
    cbBuffer = SIZEOF(DRM_UINT64) + MD5DIGESTLEN + SIZEOF(DRM_DWORD) + 1;
    dr = _Hds_malloc(pHDS, cbBuffer, (DRM_VOID **) &pbBuffer);
    if ( DRM_FAILED(dr) )
    {
        fOK = FALSE;
        goto ErrorExit;
    }

    /* read byte array from file */
    if ( !OEM_ReadFile( pHDS->fp, pbBuffer, cbBuffer, &cbRead ) 
      || cbRead != cbBuffer)
    {
        fOK = FALSE;
        goto ErrorExit;
    }

    /* decode structur member from file */
    _MEMBER_DECODE(pbBuffer, ibBuffer, pBlock->File._image.ui64TimeStamp);
    _MEMBER_DECODE(pbBuffer, ibBuffer, pBlock->File._image.bBlockHash );
    _MEMBER_DECODE(pbBuffer, ibBuffer, pBlock->File._image.nParentBlockNum );
    DRM_BYT_CopyBytes(&(pBlock->File._image.bBlockType), 0, pbBuffer, ibBuffer, 1);

    ibBuffer += 1;

ErrorExit:
    (void)_Hds_free(pHDS, pbBuffer);
    FIX_ENDIAN_QWORD( pBlock->File._image.ui64TimeStamp );
    FIX_ENDIAN_DWORD( pBlock->File._image.nParentBlockNum );

    if ( pcbRead!= NULL )
    {
        *pcbRead = cbRead;
    }
    return fOK;
}

static DRM_BOOL _ReadChildBlockHeader( 
    _HdsContext    *pHDS,
    _ChildBlockHDR *pChild, 
    DRM_DWORD       dwSize )
{
    DRM_RESULT dr  = DRM_SUCCESS;
    DRM_BOOL   fOK = TRUE;
    DRM_DWORD  cbRead   = 0;
    DRM_DWORD  ibBuffer = 0;
    DRM_DWORD  cbBuffer = 0;
    DRM_BYTE  *pbBuffer = NULL;

    /* this must be updated when the structure of _ChildBlockHDR is modified */
    cbBuffer = dwSize;
    dr = _Hds_malloc(pHDS, cbBuffer, (DRM_VOID **) &pbBuffer);
    if ( DRM_FAILED(dr) )
    {
        fOK = FALSE;
        goto ErrorExit;
    }

    /* read byte array from file */
    if ( !OEM_ReadFile( pHDS->fp, pbBuffer, cbBuffer, &cbRead ) 
      || cbRead != cbBuffer)
    {
        fOK = FALSE;
        goto ErrorExit;
    }

    ChkOverflow( dwSize, dwSize - SIZEOF(pChild->File._image.nFreeMem) );

    /* decode structure member from byte array */
    _MEMBER_DECODE(pbBuffer, ibBuffer, pChild->File._image.nFreeMem);
    DRM_BYT_CopyBytes(&(pChild->File._image.bChildAllocTable), 
                      0, 
                      pbBuffer, 
                      ibBuffer, 
                      dwSize - SIZEOF(pChild->File._image.nFreeMem));
    ibBuffer += dwSize - SIZEOF( pChild->File._image.nFreeMem );
    
ErrorExit:
    (void)_Hds_free(pHDS, pbBuffer);
    FIX_ENDIAN_DWORD( pChild->File._image.nFreeMem );
    return fOK;
}

static DRM_BOOL _WriteChildBlockHeader( 
    _HdsContext    *pHDS,
    _ChildBlockHDR *pChild, 
    DRM_DWORD       dwSize )
{
    DRM_RESULT dr  = DRM_SUCCESS;
    DRM_BOOL   fOK       = FALSE;
    DRM_DWORD  cbWritten = 0;
    DRM_DWORD  ibBuffer  = 0;
    DRM_DWORD  cbBuffer  = 0;
    DRM_BYTE  *pbBuffer  = NULL;
    
    FIX_ENDIAN_DWORD( pChild->File._image.nFreeMem );

    if( dwSize < SIZEOF(pChild->File._image.nFreeMem) )
    {
        goto ErrorExit;
    }

    /* this must be updated when the structure of _ChildBlockHDR is modified */
    cbBuffer = dwSize;
    ChkDR( _Hds_malloc(pHDS, cbBuffer, (DRM_VOID **) &pbBuffer) );

    /* encode structure member to byte array */
    _MEMBER_ENCODE(pbBuffer, ibBuffer, pChild->File._image.nFreeMem);
    DRM_BYT_CopyBytes(pbBuffer, 
                      ibBuffer, 
                      &(pChild->File._image.bChildAllocTable), 
                      0, 
                      dwSize - SIZEOF(pChild->File._image.nFreeMem));
    
    ibBuffer += dwSize - SIZEOF( pChild->File._image.nFreeMem );

    /* write byte array to file */    
    if ( !OEM_WriteFile( pHDS->fp, pbBuffer, cbBuffer, &cbWritten ) 
      || cbWritten != cbBuffer)
    {
        goto ErrorExit;
    }
    fOK = TRUE;

ErrorExit:
    (void)_Hds_free(pHDS, pbBuffer);
    FIX_ENDIAN_DWORD( pChild->File._image.nFreeMem );
    return fOK;
}

static DRM_BOOL _ReadDataBlockHeader( 
    _HdsContext   *pHDS,
    _DataBlockHDR *pBlock )
{
    DRM_RESULT dr  = DRM_SUCCESS;
    DRM_BOOL   fOK = TRUE;
    DRM_DWORD  cbRead    = 0;
    DRM_DWORD  ibBuffer  = 0;
    DRM_DWORD  cbBuffer  = 0;
    DRM_BYTE  *pbBuffer  = NULL;
   
    DRMASSERT( pBlock != NULL );

    /* this must be updated when the structure of _DataBlockHDR is modified */
    cbBuffer = SIZEOF(pBlock->File._image.nCascadingBlockNum);
    dr = _Hds_malloc(pHDS, cbBuffer, (DRM_VOID **) &pbBuffer);
    if ( DRM_FAILED(dr) )
    {
        fOK = FALSE;
        goto ErrorExit;
    }

    /* read byte array from file */
    if ( !OEM_ReadFile( pHDS->fp, pbBuffer, cbBuffer, &cbRead ) 
      || cbRead != cbBuffer)
    {
        fOK = FALSE;
        goto ErrorExit;
    }

    /* decode structure member from byte array */
    _MEMBER_DECODE(pbBuffer, ibBuffer, pBlock->File._image.nCascadingBlockNum);
    
ErrorExit:
    (void)_Hds_free(pHDS, pbBuffer);
    FIX_ENDIAN_DWORD( pBlock->File._image.nCascadingBlockNum );
    return fOK;
}

static DRM_BOOL _WriteDataBlockHeader( 
    _HdsContext   *pHDS,
    _DataBlockHDR *pBlock )
{
    DRM_BOOL   fOK = FALSE;
    DRM_RESULT dr  = DRM_SUCCESS;
    DRM_DWORD  cbWritten = 0;
    DRM_DWORD  ibBuffer  = 0;
    DRM_DWORD  cbBuffer  = 0;
    DRM_BYTE  *pbBuffer  = NULL;
    
    ChkArg( pHDS != NULL
       && pBlock != NULL );

    /* this must be updated when the structure of _DataBlockHDR is modified */
    cbBuffer = SIZEOF(pBlock->File._image.nCascadingBlockNum);
    ChkDR( _Hds_malloc(pHDS, cbBuffer, (DRM_VOID **) &pbBuffer) );

    FIX_ENDIAN_DWORD( pBlock->File._image.nCascadingBlockNum );
    _MEMBER_ENCODE(pbBuffer, ibBuffer, pBlock->File._image.nCascadingBlockNum);
    if ( !OEM_WriteFile( pHDS->fp, pbBuffer, cbBuffer, &cbWritten ) 
      || cbWritten != cbBuffer)
    {
        goto ErrorExit;
    }

    fOK = TRUE;
    
ErrorExit:
    (void)_Hds_free(pHDS, pbBuffer);
    FIX_ENDIAN_DWORD( pBlock->File._image.nCascadingBlockNum );
    return fOK;
}

/**********************************************************************
** Function:    GetPutBlock
** Synopsis:    
** Arguments:   [pHDS] -- pointer to LicenseStore Context structure
**              [nBlkNum] --block number 
**              [ppBlock] --buffer to receive the new block, if *ppBlock is NULL, 
**                         a new buffer will be allocated from the context. 
** Returns:     DRM_SUCCESS on success
** Notes:       
***********************************************************************
*/

#define GPBH_GENERIC_ONLY   TRUE
#define GPBH_ANY_HEADER     FALSE

#define GPBH_OPERATION_READ  TRUE
#define GPBH_OPERATION_WRITE  FALSE

static DRM_RESULT
_HdsGetPutBlockHDR(
    IN _NsContext *pNS,
    IN DRM_DWORD nBlkNum,
    IN OUT _CommBlockHDR **ppBlock,
    IN DRM_BOOL fGenericHeaderOnly,
    IN DRM_BOOL fGet)
{
    DRM_RESULT dr=DRM_SUCCESS;
    DRM_DWORD dwFilePos = 0;
    _HdsContext *pHDS=NULL;

#if DRM_PROFILE_HDSGETPUT
    DRM_PROFILING_ENTER_SCOPE(L"_HdsGetPutBlockHDR", g_pwszEnteringFunction, DRM_PROFILING_DONT_START);
#endif

    /* Seek to the begining of the block and read the data. */
    ChkArg(ISVALIDCONTEXT(pNS, eCfgContextSignature));
    DRMASSERT(pNS&& pNS->fInited && ppBlock && nBlkNum);
    pHDS = pNS->pHDS;

    ChkDR(_HdsBlockNum2FilePos(pHDS, nBlkNum, &dwFilePos));

    /* read/write the file block header */
    if ( fGet )
    {
        _CommBlockHDR tmpBlock;
        DRM_DWORD     dwFileSize = 0;

        /* verify the file position: 
        ** file header must be less than or equal to file size - block size 
        */
        ChkBOOL(OEM_GetFileSize(pHDS->fp, &dwFileSize), DRM_E_HDSFILECORRUPTED);
        ChkBOOL((dwFilePos <= (dwFileSize - pHDS->oSRN.dwBlockSize)), DRM_E_HDSFILECORRUPTED);

        if (!OEM_SetFilePointer(pHDS->fp, dwFilePos, OEM_FILE_BEGIN, NULL))
        {
            ChkDR(DRM_E_FILEREADERROR);
        }

        if( !_ReadCommonBlockHeader( pHDS, &tmpBlock, pHDS->nImagesize_FileBlockHDR, NULL ) )
        {
            ChkDR(DRM_E_FILEREADERROR);
        }
      
        /* allocate new buffer if not given */
        if ( *ppBlock == NULL )
        {
            ChkDR(_HdsAllocBlockBuffer(pNS, tmpBlock.File._image.bBlockType, ppBlock));
        }
        /* verify if the given buffer is the correct type */
        else if ( GET_BYTE( &(*ppBlock)->File._image.bBlockType, 0 ) != GET_BYTE( &tmpBlock.File._image.bBlockType, 0 )
               &&  ! ( (*ppBlock)->File._image.bBlockType == eDATABLOCK 
                    && tmpBlock.File._image.bBlockType    == eFREEBLOCK 
                    || (*ppBlock)->File._image.bBlockType == eFREEBLOCK 
                    && tmpBlock.File._image.bBlockType    == eDATABLOCK ) )
        {
            ChkDR(DRM_E_HDSBLOCKMISMATCH);
        }

        /* copy block header to new buffer */
        MEMCPY((*ppBlock)->File.bFileImage, tmpBlock.File.bFileImage, pHDS->nImagesize_FileBlockHDR);
    }
    else
    {
        _CommBlockHDR tmpBlock;

        if (!OEM_SetFilePointer(pHDS->fp, dwFilePos, OEM_FILE_BEGIN, NULL))
        {
            ChkDR(DRM_E_FILEREADERROR);
        }
        
        MEMCPY(tmpBlock.File.bFileImage, (*ppBlock)->File.bFileImage, SIZEOF(tmpBlock.File.bFileImage));
        
        if(!_WriteCommonBlockHeader(pHDS, &tmpBlock, pHDS->nImagesize_FileBlockHDR, NULL ) )
        {
            ChkDR(DRM_E_FILEWRITEERROR);
        }
    }

    if ( fGenericHeaderOnly )
    {
        goto ErrorExit;
    }
    
    switch ( GET_BYTE( &(*ppBlock)->File._image.bBlockType, 0 ) )
    {
        case eCHILDBLOCK:
            {
                /* read/write _ChildBlockHDR */
                _ChildBlockHDR *pChild = (_ChildBlockHDR*)(*ppBlock);
                if ( fGet )
                {
                    if (!_ReadChildBlockHeader(pHDS, pChild, pNS->nImagesize_ChildBlockHDR))
                    {
                        ChkDR(DRM_E_FILEREADERROR);
                    }
                    pChild->nCurrSlotPos = MAXSLOTPOS;
                    pChild->nNextSlotPos = MAXSLOTPOS;
                    pChild->nPayloadSize = pNS->nImagesize_ChildBlockPayload;
                }                        
                else
                {
                    if (!_WriteChildBlockHeader(pHDS, pChild, pNS->nImagesize_ChildBlockHDR) )
                    {
                        ChkDR(DRM_E_FILEWRITEERROR);
                    }
                }
            }
            break;

        case eDATABLOCK:
        case eFREEBLOCK:
            {
                /* read the HDSData */
                _DataBlockHDR *pData = (_DataBlockHDR*)(*ppBlock);
                if ( fGet )
                {
                    if (!_ReadDataBlockHeader(pHDS, pData ) )
                    {
                        ChkDR(DRM_E_FILEREADERROR);
                    }
                    pData->nPayloadSize = pHDS->nImagesize_DataBlockPayload;
                }                        
                else
                {
                    if (!_WriteDataBlockHeader(pHDS, pData ) )
                    {
                        ChkDR(DRM_E_FILEWRITEERROR);
                    }
                }
            }
            break;

        case eINVALIDBLOCK:
        default:
            ChkDR(DRM_E_HDSFILECORRUPTED);
    }
    
    /* everything is alright */
    if ( fGet )
    {
        (*ppBlock)->nBlockNum = nBlkNum;
        (*ppBlock)->pNS = pNS;
    }

ErrorExit:

#if _DATASTORE_WRITE_THRU
    if ( DRM_SUCCEEDED(dr) && !fGet )
    {
        if ( !OEM_FlushFileBuffers(pHDS->fp) )
        {
            dr = DRM_E_FILEWRITEERROR;
        }
    }
#endif

#if DRM_PROFILE_HDSGETPUT
    DRM_PROFILING_LEAVE_SCOPE(L"_HdsGetPutBlockHDR", g_pwszLeavingFunction);
#endif
    return dr;
}


/**********************************************************************
** Function:    _GenBlockHash
** Synopsis:    Generate hash of the given block
** Arguments:   [pLSContext] -- 
**              [pBlock] --
**              [blockHash] --
** Returns:     DRM_SUCCESS on success
** Notes:       
***********************************************************************
*/
static DRM_RESULT 
_HdsGenBlockHash( 
    IN _HdsContext   *pHDS,
    IN _NsContext    *pNS,
    IN _CommBlockHDR *pBlock,
    OUT DRM_BYTE      Hash [__CB_DECL(MD5DIGESTLEN)])
{
    DRM_RESULT dr = DRM_SUCCESS;
    DRM_BYTE rgbBlockType[__CB_DECL(SIZEOF(DRM_DWORD))] = {0};

    if ( GET_BYTE(&pBlock->File._image.bBlockType, 0 ) == eCHILDBLOCK )
    {
        ChkArg(ISVALIDCONTEXT(pNS, eCfgContextSignature));
        DRMASSERT(pNS && pNS->fInited);
    }

    DRMASSERT(pBlock!=NULL);
    DRM_MD5_Init(&pHDS->contextMD5 );

    /* _CommBlockHDR header */
    FIX_ENDIAN_DWORD( pBlock->File._image.nParentBlockNum );    
    FIX_ENDIAN_QWORD( pBlock->File._image.ui64TimeStamp );
    PUT_BYTE( rgbBlockType, 0, GET_BYTE( &pBlock->File._image.bBlockType, 0 ) );
    DRM_MD5_Update( &pHDS->contextMD5, (DRM_VOID*)&pBlock->File._image.nParentBlockNum, SIZEOF(DRM_DWORD));
    DRM_MD5_Update( &pHDS->contextMD5, (DRM_VOID*)&pBlock->File._image.ui64TimeStamp,   SIZEOF(DRM_UINT64));
    DRM_MD5_Update( &pHDS->contextMD5, (DRM_VOID*)rgbBlockType,                         SIZEOF(DRM_DWORD));
    FIX_ENDIAN_DWORD( pBlock->File._image.nParentBlockNum );
    FIX_ENDIAN_QWORD( pBlock->File._image.ui64TimeStamp );

    switch (GET_BYTE(&pBlock->File._image.bBlockType, 0 ))
    {
        case eCHILDBLOCK:
            {
                /* _ChildBlockHDR header */
                _ChildBlockHDR *pChild = (_ChildBlockHDR*)pBlock;
                FIX_ENDIAN_DWORD( pChild->File._image.nFreeMem );
                DRM_MD5_Update(&pHDS->contextMD5, (DRM_VOID*)&pChild->File._image.nFreeMem, SIZEOF(DRM_DWORD));
                DRM_MD5_Update(&pHDS->contextMD5, (DRM_VOID*)pChild->File._image.bChildAllocTable, pNS->nImagesize_ChildAllocTable);
                FIX_ENDIAN_DWORD( pChild->File._image.nFreeMem );
            }
            break;
        case eDATABLOCK:
        case eFREEBLOCK:
            {
                /* _DataBlockHDR header */
                _DataBlockHDR *pData = (_DataBlockHDR*)pBlock;
                FIX_ENDIAN_DWORD( pData->File._image.nCascadingBlockNum );
                DRM_MD5_Update(&pHDS->contextMD5, (DRM_VOID*)&pData->File._image.nCascadingBlockNum, SIZEOF(DRM_DWORD));
                FIX_ENDIAN_DWORD( pData->File._image.nCascadingBlockNum );
            }
            break;
        default:
            ChkDR(DRM_E_HDSFILECORRUPTED);
    }
    DRM_MD5_Final( &pHDS->contextMD5 );
    
    MEMCPY( Hash, pHDS->contextMD5.digest, MD5DIGESTLEN );

ErrorExit:
    return dr;
}

/**********************************************************************
** Function:    _LoadFileBlock
** Synopsis:    Load/read a specific block from file
** Arguments:   [pHDS] -- pointer to LicenseStore Context structure
**              [nBlkNum] --block number 
**              [ppBlock] --buffer to receive the new block
** Returns:     DRM_SUCCESS on success
** Notes:       Caller is responsible to call OEM_free(*ppBlock);
***********************************************************************
*/
static DRM_RESULT _HdsLoadBlockHDR(
    IN _NsContext *pNS,
    IN DRM_DWORD nBlkNum,
    OUT _CommBlockHDR **ppBlock)
{
    DRM_RESULT dr=DRM_SUCCESS;
    DRM_BYTE  rgbHash [__CB_DECL(MD5DIGESTLEN)];
    DRM_BOOL fAllocBuff=TRUE;

    ChkArg(ISVALIDCONTEXT(pNS, eCfgContextSignature));
    DRMASSERT(pNS && pNS->fInited && ppBlock);
    if ( *ppBlock!= NULL)
    {
        fAllocBuff = FALSE;
    }

    ChkDR(_HdsGetPutBlockHDR(pNS, nBlkNum, ppBlock, GPBH_ANY_HEADER, GPBH_OPERATION_READ));
    ChkDR(_HdsGenBlockHash(pNS->pHDS, pNS, *ppBlock, rgbHash));
    if (MEMCMP((*ppBlock)->File._image.bBlockHash, rgbHash, MD5DIGESTLEN ) != 0)
    {
        ChkDR(DRM_E_HASHMISMATCH);
    }

ErrorExit:
    if ( DRM_FAILED(dr) && fAllocBuff && *ppBlock != NULL )
    {
        _Hds_free(pNS->pHDS, *ppBlock);
        *ppBlock = NULL;
    }
    return dr;
}


/**********************************************************************
** Function:    _WriteFileBlock
** Synopsis:    write the given block to file
** Arguments:   [pLSContext] -- pointer to LicenseStore Context structure
**              [pBlock] --buffer containing the file block content.
** Returns:     DRM_SUCCESS on success
** Notes:       
***********************************************************************
*/
static DRM_RESULT _HdsWriteBlockHDR(
    IN OUT _CommBlockHDR *pBlock)
{
    DRM_RESULT dr=DRM_SUCCESS;

    ChkBOOL( pBlock != NULL, DRM_E_FILEWRITEERROR );
    
    pBlock->File._image.ui64TimeStamp = _GetTimeStamp();    /* update timestamp */
    ChkDR(_HdsGenBlockHash(pBlock->pNS->pHDS, pBlock->pNS, pBlock, pBlock->File._image.bBlockHash));
    ChkDR(_HdsGetPutBlockHDR(pBlock->pNS, pBlock->nBlockNum, &pBlock, GPBH_ANY_HEADER, GPBH_OPERATION_WRITE));
    
ErrorExit:    
    return dr;
}

/*
** Copy block
*/
static DRM_RESULT _HdsCopyBlockBuffer(
    OUT _CommBlockHDR *pDestBlock,
    IN _CommBlockHDR *pSrcBlock)
{
    DRM_RESULT dr=DRM_SUCCESS;
    
    if (pDestBlock == pSrcBlock)
    {
       goto ErrorExit;
    }
    
    DRMASSERT( pDestBlock != NULL
            && pSrcBlock  != NULL
            && GET_BYTE( &pDestBlock->File._image.bBlockType, 0 ) == GET_BYTE( &pSrcBlock->File._image.bBlockType, 0 ) );

    MEMCPY(pDestBlock, pSrcBlock, SIZEOF(_CommBlockHDR));
    if ( GET_BYTE( &pSrcBlock->File._image.bBlockType, 0 ) == eCHILDBLOCK )
    {
        _ChildBlockHDR *pChildSrc = (_ChildBlockHDR*)pSrcBlock;
        _ChildBlockHDR *pChildDest = (_ChildBlockHDR*)pDestBlock;

        pChildDest->nCurrSlotPos = pChildSrc->nCurrSlotPos;
        pChildDest->nNextSlotPos = pChildSrc->nNextSlotPos;
        pChildDest->nPayloadSize = pChildSrc->nPayloadSize;
        MEMCPY(pChildDest->File.bFileImage, 
               pChildSrc->File.bFileImage, 
               pSrcBlock->pNS->nImagesize_ChildBlockHDR);
    }
    else if ( GET_BYTE( &pSrcBlock->File._image.bBlockType, 0 ) == eDATABLOCK )
    {
        _DataBlockHDR *pDataSrc = (_DataBlockHDR*)pSrcBlock;
        _DataBlockHDR *pDataDest = (_DataBlockHDR*)pDestBlock;

        pDataDest->nPayloadSize = pDataSrc->nPayloadSize;
        MEMCPY(pDataDest->File.bFileImage, 
               pDataSrc->File.bFileImage, 
               pSrcBlock->pNS->pHDS->nImagesize_DataBlockHDR);
    }

ErrorExit:
    return dr;
}

/*
**
*/
static DRM_RESULT _HdsGetPutChildBlockNum(
    IN     _NsContext *pNS,
    IN     DRM_BYTE   *pbChildAllocTable,
    IN     DRM_DWORD   nIndex,
    IN OUT DRM_DWORD  *pnChildBlkNum,
    IN     DRM_BOOL    fGet)
{
    DRM_RESULT dr = DRM_SUCCESS;

    DRMASSERT(pNS && pNS->fInited && pbChildAllocTable && pnChildBlkNum 
        && nIndex < pNS->wMaxNumChildren);	
	
    switch (pNS->pHDS->oSRN.eBlockNumType)
    {
        case eDRM_HDSBLKNUM_DWORD:
            if ( fGet )
            {
                *pnChildBlkNum = ((DRM_DWORD*)pbChildAllocTable)[nIndex];
                FIX_ENDIAN_DWORD( *pnChildBlkNum );
            }
            else
            {
                DRM_DWORD *rgdwTab = (DRM_DWORD*)pbChildAllocTable;
                DRM_DWORD dwIndex  = *pnChildBlkNum;
                FIX_ENDIAN_DWORD( dwIndex );
                rgdwTab[nIndex] = dwIndex;
            }
            break;
        case eDRM_HDSBLKNUM_WORD:
            if ( fGet )
            {
                DRM_WORD wNum=((DRM_WORD*)pbChildAllocTable)[nIndex];
                FIX_ENDIAN_WORD( wNum );
                *pnChildBlkNum = (DRM_DWORD)wNum;
            }
            else
            {
                DRM_WORD *rgwTab = (DRM_WORD*)pbChildAllocTable;
                DRM_WORD wNum    = (DRM_WORD)*pnChildBlkNum;
                
                DRMASSERT( ((*pnChildBlkNum) & 0xFFFF0000) == 0 );
                
                FIX_ENDIAN_WORD( wNum );
                rgwTab[nIndex] = wNum;
            }
            break;
        default:
            dr = DRM_E_HDSBLOCKMISMATCH;
            break;
    }
    
    return dr;
}

/**********************************************************************
** Function:    HashKeyToIndex
** Synopsis:    
** Arguments:   [pbDRMData] -- 
** Returns:     DRM_SUCCESS on success
** Notes:       
***********************************************************************
*/
static DRM_RESULT
_HdsHashKeyToIndex(
    IN _NsContext *pNS,
    IN const DRM_BYTE HashKey [__CB_DECL(DRM_HDS_HASHKEY_SIZE)], 
    IN DRM_DWORD nBlkNum,
    OUT DRM_DWORD *pnIndex)
{
    DRM_DWORD nFilePos;

    DRMASSERT(pNS && pNS->fInited && pnIndex);
    _HdsBlockNum2FilePos(pNS->pHDS, nBlkNum, &nFilePos);

    DRM_MD5_Init( &pNS->pHDS->contextMD5 );
    DRM_MD5_Update( &pNS->pHDS->contextMD5, HashKey, DRM_HDS_HASHKEY_SIZE);
    FIX_ENDIAN_DWORD( nFilePos );
    DRM_MD5_Update( &pNS->pHDS->contextMD5, (DRM_VOID*)&nFilePos, SIZEOF(DRM_DWORD) );
    DRM_MD5_Final( &pNS->pHDS->contextMD5 );

    /* mod the first 4 bytes of the hash value with number of children per parent block */    
    BYTES_TO_DWORD( *pnIndex, pNS->pHDS->contextMD5.digest );
    *pnIndex %= (DRM_DWORD)pNS->wMaxNumChildren;

/*  TRACE(("HashKeyToIndex %d, blocknum %d\n", *pnIndex, nBlkNum)); */

    return DRM_SUCCESS;
}


/**********************************************************************
** Function:    GoDownOneLevel
** Synopsis:    Traverse to a child block according to the given CID and SKUID.
** Arguments:   [pHDS] -- 
**              [pCurrBlock] -- NULL indicates no parent "child" node. the RootNode will 
**                 used as the parent.
**              [rgbCID]
**              [rgbSKUID]
**              [pBlock] -- retrieved child block
** Returns:     DRM_SUCCESS on success
** Notes:       
***********************************************************************
*/
static DRM_RESULT
_HdsHashToChildBlock(
    IN  _NsContext     *pNS,
    IN  _CommBlockHDR  *pParentBlock,  /* NULL if start from NSRoot of current Namespace */
    IN  const DRM_BYTE  bHashKey [__CB_DECL(DRM_HDS_HASHKEY_SIZE)], 
    OUT _CommBlockHDR **ppChildBlock, 
    OUT DRM_BOOL       *pfResult,
    OUT DRM_DWORD      *pdwChildIndex)
{
    DRM_RESULT dr=DRM_SUCCESS;
    DRM_DWORD  nBlockNum=0;
    DRM_DWORD  nIndex=0;

    DRMASSERT(pNS && bHashKey && pNS->fInited && ppChildBlock && pfResult);
    *pfResult=FALSE;

    if ( pParentBlock == NULL )   /* start from Root of current Namespace */
    {
        nBlockNum = pNS->nCfgRootBlockNum;
    }
    else
    {
        /* compute the child's hash index */
        ChkDR(_HdsHashKeyToIndex(pNS, bHashKey, pParentBlock->nBlockNum, &nIndex));
        ChkDR(_HdsGetPutChildBlockNum(pNS, ((_ChildBlockHDR*)pParentBlock)->File._image.bChildAllocTable,
            nIndex, &nBlockNum, TRUE));
        if ( nBlockNum == 0 )
        {
            goto ErrorExit;  /* not found */
        }
    }

    ChkDR(_HdsLoadBlockHDR(pNS, nBlockNum, ppChildBlock));
    if ( GET_BYTE( &(*ppChildBlock)->File._image.bBlockType, 0) == eCHILDBLOCK )
    {
        *pfResult = TRUE;
        if ( pdwChildIndex )
        {
            *pdwChildIndex = nIndex;
        }
    }
    else
    {
        dr = DRM_E_HDSFILECORRUPTED;
    }
    
ErrorExit:
    
    return dr;
}

/*
**
*/
static DRM_RESULT
_HdsExpandStore(
    IN  _HdsContext *pHDS,
    IN  DRM_DWORD    nGrowNumofBlocks,
    OUT DRM_DWORD   *pnNextBlockNum)
{
    DRM_RESULT dr=DRM_SUCCESS;
    DRM_DWORD  dwFilePos=0, dwTmpPos=0, nBlockNum=0;
    DRM_DWORD  dwNumberOfBytesIO=0;

    ChkArg(pnNextBlockNum != NULL);

    if (!OEM_GetFileSize(pHDS->fp, &dwFilePos))
    {
        ChkDR(DRM_E_FILESEEKERROR);
    }
        
    ChkDR(_HdsFilePos2BlockNum(pHDS, dwFilePos, &nBlockNum));

    /* verify block num */
    ChkDR(_HdsBlockNum2FilePos(pHDS, nBlockNum, &dwTmpPos));
    if ( dwTmpPos != dwFilePos )
    {
        /* max block number reached */
        TRACE( ("_HdsExpandStore(): Data store is full.\n" ));
        ChkDR(DRM_E_HDSSTOREFULL);
    }

    /* expand the file:
    ** - seek to block-size -SIZEOF(BLOCK_SIGNATURE) bytes after current EOF,
    ** - write the BLOCK_SIGNATURE at the position,
    ** - flush the file
    */ 
    if (!OEM_SetFilePointer(
                pHDS->fp, 
                (nGrowNumofBlocks * pHDS->oSRN.dwBlockSize) - SIZEOF(BLOCK_SIGNATURE), 
                OEM_FILE_END, 
                NULL))
    {
        ChkDR(DRM_E_HDSSTOREFULL);
    }
    if (!OEM_WriteFile(pHDS->fp, (DRM_VOID*)BLOCK_SIGNATURE, SIZEOF(BLOCK_SIGNATURE), 
        &dwNumberOfBytesIO) || dwNumberOfBytesIO!=SIZEOF(BLOCK_SIGNATURE))
    {
        ChkDR(DRM_E_FILEWRITEERROR);
    }
    
    *pnNextBlockNum = nBlockNum;

ErrorExit:
    return dr;
}

#define GPBP_OPERATION_READ  TRUE
#define GPBP_OPERATION_WRITE FALSE

/*
** Get the specified size of payload at the specified pos of the block's child payload
*/
static DRM_RESULT _HdsGetPutBlockPayload(
    IN _CommBlockHDR *pBlock,
    IN DRM_DWORD nPos,   /* position relative to given block's payload, starts from 0 */
    IN DRM_DWORD nSize,
    IN OUT DRM_VOID *pBuff,
    IN DRM_BOOL fGet)
{
    DRM_RESULT dr=DRM_SUCCESS;
    DRM_DWORD dwFilePos=0;
    DRM_DWORD ioCount=0;    

#if DRM_PROFILE_HDSGETPUT
    DRM_PROFILING_ENTER_SCOPE(L"_HdsGetPutBlockPayload", g_pwszEnteringFunction, DRM_PROFILING_DONT_START);
#endif

    DRMASSERT( pBlock && pBuff );
    if( nSize == 0 )
    {
        /* No data read or written */
        goto ErrorExit;
    }

    /* compute filepos of the block */
    ChkDR(_HdsBlockNum2FilePos(pBlock->pNS->pHDS, pBlock->nBlockNum, &dwFilePos));

    /* adjust filepos relative to the block */
    switch (GET_BYTE( &pBlock->File._image.bBlockType, 0 ))
    {
        case eCHILDBLOCK:
            {
                _ChildBlockHDR *pChild=(_ChildBlockHDR*)pBlock;
                DRMASSERT( (nPos+nSize)<=pChild->nPayloadSize );
                dwFilePos += pBlock->pNS->nChildBlockPayloadPos + nPos;
            }
            break;
        case eDATABLOCK:
            {
                _DataBlockHDR *pData=(_DataBlockHDR*)pBlock;
                DRMASSERT( (nPos+nSize)<=pData->nPayloadSize );
                dwFilePos += pBlock->pNS->pHDS->nDataBlockPayloadPos + nPos;
            }
            break;
        case eFREEBLOCK:
            break;
        default:
            DRMASSERT(FALSE);                
    }

    /* seek to the appropriate file position */
    if (OEM_SetFilePointer(pBlock->pNS->pHDS->fp, dwFilePos, OEM_FILE_BEGIN, NULL))
    {
        DRM_BOOL fIO;
        
        if ( fGet )
        {
            /* Note: we do not verify file position here because we already
            **       verified the block header position is at least 
            **       filesize - block size.
            */
            fIO = OEM_ReadFile(pBlock->pNS->pHDS->fp, pBuff, nSize, &ioCount);
        }
        else
        {
            fIO = OEM_WriteFile(pBlock->pNS->pHDS->fp, pBuff, nSize, &ioCount);
        }

        if (fIO)
        {

#if _DATASTORE_WRITE_THRU
            if ( !fGet )
            {
                ChkBOOL( OEM_FlushFileBuffers(pBlock->pNS->pHDS->fp), DRM_E_FILEWRITEERROR );
            }
#endif
            goto ErrorExit;
        }
    }

    if ( fGet )
    {
        ChkDR(DRM_E_FILEREADERROR);
    }
    else
    {
        ChkDR(DRM_E_FILEWRITEERROR);
    }

ErrorExit:
#if DRM_PROFILE_HDSGETPUT
    DRM_PROFILING_LEAVE_SCOPE(L"_HdsGetPutBlockPayload", g_pwszLeavingFunction);
#endif
    return dr;
}


/**********************************************************************
** Function:    _HdsUpdateSRN
** Synopsis:    
** Arguments:   [pbHdsContext] -- 
** Returns:     DRM_SUCCESS on success
** Notes:       
***********************************************************************
*/
static DRM_RESULT _HdsUpdateSRN( 
    IN _HdsContext *pHDS)
{
    DRM_RESULT dr = DRM_SUCCESS;
    DRM_BYTE   bSRNHash [__CB_DECL(MD5DIGESTLEN)];
    DRM_DWORD  dwFileSize=0;
    DRM_DWORD  dwNumberOfBytesIO=0;

    /* update hash of the root node */
    ChkArg(ISVALIDCONTEXT(pHDS, eHdsContextSignature));
    DRMASSERT( pHDS != NULL
            && pHDS->fInited 
            && pHDS->fp != OEM_INVALID_HANDLE_VALUE );

    /* read the SRN Hash in file and make sure it is the same as we have now
    ** if not, someone has been updated the SRN and we should not update ours to the file.
    */
    if (OEM_GetFileSize(pHDS->fp, &dwFileSize))
    {
        if ( dwFileSize < HDS_FILEHEADERSIZE )
        {
            /* expand the file to accomodate file header */
            if (!OEM_SetFilePointer(pHDS->fp, HDS_FILEHEADERSIZE-SIZEOF(BLOCK_SIGNATURE), OEM_FILE_BEGIN, NULL))
            {
                ChkDR(DRM_E_FILESEEKERROR);
            }
            if (!OEM_WriteFile(pHDS->fp, (DRM_VOID*)BLOCK_SIGNATURE, SIZEOF(BLOCK_SIGNATURE), &dwNumberOfBytesIO) 
                || dwNumberOfBytesIO!=SIZEOF(BLOCK_SIGNATURE))
            {
                ChkDR(DRM_E_FILEWRITEERROR);
            }
           
            goto CommitStore;  /* this is a new file */
        }

        if (OEM_SetFilePointer(pHDS->fp, SIZEOF(DRM_DWORD), OEM_FILE_BEGIN, NULL))
        {
            /* verify hash value of SRN */
            if (OEM_ReadFile(pHDS->fp, bSRNHash, MD5DIGESTLEN, &dwNumberOfBytesIO) &&
                dwNumberOfBytesIO == MD5DIGESTLEN)
            {
                if (MEMCMP(bSRNHash, pHDS->oSRN.bSRNHash, MD5DIGESTLEN) == 0 )
                {
                    goto CommitStore;
                }
                else
                {
                    TRACE(("DRM_HDS_UpdateSRN(): Super root node's hash value mismatched\n"));
                    ChkDR(DRM_E_HDSSRNCORRUPTED);
                }
            }
        }
    }
    ChkDR(DRM_E_FILESEEKERROR);

CommitStore:
    
    /* update the SRN in the file */
    pHDS->oSRN.ui64Timestamp = _GetTimeStamp();
    ChkDR(_HdsGenSRNHash(pHDS, pHDS->oSRN.bSRNHash));

    /* Seek to the begining of the file and write out updated SRN data. */
    if (OEM_SetFilePointer(pHDS->fp, 0, OEM_FILE_BEGIN, NULL))
    {
        DRM_DWORD dwStoreVersion = DRM_HDS_FILE_CURR_VERSION;
        FIX_ENDIAN_DWORD( dwStoreVersion );
        if (OEM_WriteFile(pHDS->fp, (DRM_VOID*)&dwStoreVersion, SIZEOF(DRM_DWORD), 
            &dwNumberOfBytesIO) && dwNumberOfBytesIO == SIZEOF(DRM_DWORD))
        {
            if (_WriteSRN(pHDS))
            {
#if _DATASTORE_WRITE_THRU
                ChkBOOL( OEM_FlushFileBuffers(pHDS->fp), DRM_E_FILEWRITEERROR );
#endif
                goto ErrorExit;
            }
        }        
    }
    ChkDR(DRM_E_FILEWRITEERROR);

ErrorExit:
    return dr;
}


/*
**
*/
static DRM_RESULT _HdsLoadSRN(
    IN _HdsContext *pHDS)
{
    DRM_RESULT dr = DRM_SUCCESS;
    DRM_DWORD  dwStoreVersion = 0;
    DRM_DWORD  nFileSize=0;
    DRM_BYTE   rgbHash [__CB_DECL(MD5DIGESTLEN)];
    DRM_DWORD  dwNumberOfBytesIO;
    DRMASSERT(pHDS!=NULL);

    /* read store version # */
    if (!OEM_SetFilePointer(pHDS->fp, 0, OEM_FILE_BEGIN, NULL) ||
        !OEM_ReadFile(pHDS->fp, &dwStoreVersion, SIZEOF(DRM_DWORD), &dwNumberOfBytesIO) ||
        dwNumberOfBytesIO!=SIZEOF(DRM_DWORD))
    {
        ChkDR(DRM_E_FILEREADERROR);
    }
    FIX_ENDIAN_DWORD( dwStoreVersion );

    /* verify the version # and set file header size appropriately */
    switch (dwStoreVersion)
    {
        case DRM_HDS_FILE_CURR_VERSION:
            break;
            
        default:
            TRACE( ("HDS file version incorrect. Can't handle this file.\n" ));
            ChkDR(DRM_E_HDSINVALIDSTORE);
    }

    /* Read the SRN */
    ZEROMEM(&pHDS->oSRN, SIZEOF(_SRN));
    if (!OEM_SetFilePointer(pHDS->fp, SIZEOF(DRM_DWORD), OEM_FILE_BEGIN, NULL) || /* skip size of version DWORD */
        !_ReadSRN(pHDS) ) /* read the SRN data from file */
    {
        ChkDR(DRM_E_FILEREADERROR);
    }

    /* check SRN and block size */
    if( pHDS->oSRN.dwSRNSize   != SIZEOF(_SRN) 
     || pHDS->oSRN.dwBlockSize == 0 )
    {
        /* Incorrect Super root node size */
        ChkDR(DRM_E_HDSINVALIDSTORE);
    }            

    if (!OEM_GetFileSize(pHDS->fp, &nFileSize))
    {
        ChkDR(DRM_E_FILESEEKERROR);
    }

    if ( nFileSize < HDS_FILEHEADERSIZE 
        || ((nFileSize - HDS_FILEHEADERSIZE) % pHDS->oSRN.dwBlockSize) != 0 )
    {
        ChkDR(DRM_E_HDSFILECORRUPTED);
    }

    if ( pHDS->oSRN.eBlockNumType != eDRM_HDSBLKNUM_DWORD 
      && pHDS->oSRN.eBlockNumType != eDRM_HDSBLKNUM_WORD )
    {
        ChkDR(DRM_E_HDSFILECORRUPTED);
    }

    /* verify the hash value */
    ChkDR(_HdsGenSRNHash(pHDS, rgbHash));
    if (MEMCMP(rgbHash, pHDS->oSRN.bSRNHash, SIZEOF( pHDS->oSRN.bSRNHash ) ) != 0)
    {
        ChkDR(DRM_E_HASHMISMATCH);      /* SRN Hash failed! */
    }

ErrorExit:
    return dr;
}


/**********************************************************************
** Function:    _HdsAllocBlock
** Synopsis:    Allocate a File Block. 
** Arguments:   [pHDS] -- 
**              [nParentBlockNum] --parent block num of of the new block
**              [pBlock] --buffer to receive the new block
**              [eBlockType] --type of the block
** Returns:     DRM_SUCCESS on success
** Notes:       A recycle free block list is kept in the root node. _AllocFileBlock will first
**              check if any free block is available.
***********************************************************************
*/
static DRM_RESULT
_HdsAllocBlock(
    IN     _NsContext     *pNS,
    IN     DRM_DWORD       nParentBlockNum,
       OUT _CommBlockHDR **ppBlock,
    IN     _EBlockType     eBlockType)
{
    DRM_RESULT    dr          = DRM_SUCCESS;
    DRM_BOOL      fAllocBuff  = FALSE;
    _HdsContext  *pHDS        = NULL;
    DRM_BYTE     *pCopyBuffer = NULL;
    DRM_DWORD     dwFilePos   = 0;
    DRM_BYTE      rgbHash [__CB_DECL(MD5DIGESTLEN)];
    _CommBlockHDR tmpBlock;

    ChkArg(ISVALIDCONTEXT(pNS, eCfgContextSignature));
    DRMASSERT(pNS && pNS->fInited && ppBlock && 
        (eBlockType==eCHILDBLOCK || eBlockType==eDATABLOCK));

    pHDS = pNS->pHDS;
    if ( *ppBlock == NULL )
    {
        fAllocBuff = TRUE;
        ChkDR(_HdsAllocBlockBuffer(pNS, eBlockType, ppBlock));
    }

    /* flush SRN in context */
    ChkDR(_HdsLoadSRN(pHDS));

    /* check if there is any recycle blocks kept in RootNode */
    if ( pHDS->oSRN.nFreeListHead == 0 )     
    {
        ChkDR(_HdsPreAlloc(pHDS, pHDS->nGrowbySizeinKB, FALSE, NULL));
    }


    /* Free list in SRN must have some free block(s) now 
    */
    
    /* Seek to the begining of the free block and read the generic header. */
    ChkDR(_HdsBlockNum2FilePos(pHDS, pHDS->oSRN.nFreeListHead, &dwFilePos));
    
    if (!OEM_SetFilePointer(pHDS->fp, dwFilePos, OEM_FILE_BEGIN, NULL))
    {
        ChkDR(DRM_E_FILEREADERROR);
    }
    if( !_ReadCommonBlockHeader( pHDS, &tmpBlock, pHDS->nImagesize_FileBlockHDR, NULL ) )
    {
        ChkDR(DRM_E_FILEREADERROR);
    }
    
    if ( GET_BYTE( &(tmpBlock.File._image.bBlockType), 0 ) != eFREEBLOCK )  /* verify if the given buffer is the correct type */
    {
        ChkDR(DRM_E_HDSBLOCKMISMATCH);
    }
    
    /* copy block header to new buffer */
    MEMCPY((*ppBlock)->File.bFileImage, tmpBlock.File.bFileImage, pHDS->nImagesize_FileBlockHDR);

    /* read the rest of the free block */
    if (!_ReadDataBlockHeader(pHDS, ((_DataBlockHDR*)(*ppBlock)) ) )
    {
        ChkDR(DRM_E_FILEREADERROR);
    }
    
    /* everything is alright */
    ((_DataBlockHDR*)(*ppBlock))->nPayloadSize = pHDS->nImagesize_DataBlockPayload;
    (*ppBlock)->nBlockNum = pHDS->oSRN.nFreeListHead;
    (*ppBlock)->pNS = pNS;

    /* validate the block's hash value */
    ChkDR(_HdsGenBlockHash(pNS->pHDS, pNS, *ppBlock, rgbHash));
    if (MEMCMP((*ppBlock)->File._image.bBlockHash, rgbHash, SIZEOF( rgbHash ) ) != 0)
    {
        ChkDR(DRM_E_HASHMISMATCH);
    }
    
    /* update free list and commit changes of root node to file */
    pHDS->oSRN.nFreeListHead = ((_DataBlockHDR*)(*ppBlock))->File._image.nCascadingBlockNum;
    ChkDR(_HdsUpdateSRN(pHDS));

    /* set up the block */
    ChkDR(_HdsInitBlockBuffer(pNS, *ppBlock, nParentBlockNum, eBlockType));

    /* wipe the whole block with NULL char */
    {
        DRM_DWORD nIndex = 0;
        DRM_DWORD dwAmount = 0;

        if ( eBlockType == eCHILDBLOCK ) 
        {
            dwAmount = (*ppBlock)->pNS->nImagesize_ChildBlockPayload - SIZEOF(BLOCK_SIGNATURE);
        }
        else  /* eBlockType == eDATABLOCK */
        {
            dwAmount = (*ppBlock)->pNS->pHDS->nImagesize_DataBlockPayload - SIZEOF(BLOCK_SIGNATURE);
        }
        
        ChkDR(_Hds_malloc((*ppBlock)->pNS->pHDS, COPYBUFF_SIZE, (DRM_VOID**)&pCopyBuffer));

        ZEROMEM( pCopyBuffer, COPYBUFF_SIZE );
        
        for (nIndex=0; nIndex<dwAmount;)
        {
            ChkDR(_HdsGetPutBlockPayload((*ppBlock), nIndex, min(COPYBUFF_SIZE, dwAmount-nIndex),
                pCopyBuffer, GPBP_OPERATION_WRITE));
            nIndex += min(COPYBUFF_SIZE, dwAmount-nIndex);
        }

        /* write the block signature at end of block */
        ChkDR(_HdsGetPutBlockPayload((*ppBlock), dwAmount, SIZEOF(BLOCK_SIGNATURE), 
            (DRM_VOID*)BLOCK_SIGNATURE, GPBP_OPERATION_WRITE));
    }
    
    /* update the new block */
    ChkDR(_HdsWriteBlockHDR(*ppBlock));

    /* make sure the new block is written to disk before the parent block */
    ChkBOOL( OEM_FlushFileBuffers(pHDS->fp), DRM_E_FILEWRITEERROR );

ErrorExit:    
    if ( pCopyBuffer != NULL )
    {
        _Hds_free((*ppBlock)->pNS->pHDS, pCopyBuffer);
    }
    
    if ( DRM_FAILED(dr) && fAllocBuff && *ppBlock!=NULL )
    {
        _Hds_free(pHDS, *ppBlock);
    }

    return dr;
}


/**********************************************************************
** Function:    _HdsFreeBlock
** Synopsis:    Free an allocated File Block. 
** Arguments:   [pHDS] -- 
**              [pBlock] --block to be free'd
** Returns:     DRM_SUCCESS on success
** Notes:       A recycle free block list is kept in the root node. All free'd blocks
**              are put in a linked list.
***********************************************************************
*/
static DRM_RESULT _HdsFreeBlock(
    IN OUT _CommBlockHDR *pBlock)
{
    DRM_RESULT dr=DRM_SUCCESS;
    _DataBlockHDR *pDataBlock=NULL;
    DRM_BYTE *pCopyBuffer=NULL;
    DRM_DWORD nIndex=0;

    DRMASSERT(pBlock && pBlock->pNS && pBlock->pNS->fInited);

    /* flush SRN in context */
    ChkDR(_HdsLoadSRN(pBlock->pNS->pHDS));

    /* wipe the block buffer */
    ZEROMEM(pBlock->File.bFileImage, pBlock->pNS->pHDS->nImagesize_FileBlockHDR);
    _HdsInitBlockBuffer(pBlock->pNS, pBlock, 0, eDATABLOCK);

    /* optional: wipe the whole block with NULL char */
    {
        DRM_DWORD dwAmount = pBlock->pNS->pHDS->nImagesize_DataBlockPayload - SIZEOF(BLOCK_SIGNATURE);
        
        ChkDR(_Hds_malloc(pBlock->pNS->pHDS, COPYBUFF_SIZE, (DRM_VOID**)&pCopyBuffer));
        ZEROMEM((DRM_BYTE *) pCopyBuffer, COPYBUFF_SIZE); 
        
        for (nIndex=0; nIndex<dwAmount;)
        {
            ChkDR(_HdsGetPutBlockPayload(pBlock, nIndex, min(COPYBUFF_SIZE, dwAmount-nIndex),
                pCopyBuffer, GPBP_OPERATION_WRITE));
            nIndex += min(COPYBUFF_SIZE, dwAmount-nIndex);
        }

        /* write the block signature at end of block */
        ChkDR(_HdsGetPutBlockPayload(pBlock, dwAmount, SIZEOF(BLOCK_SIGNATURE), 
            (DRM_VOID*)BLOCK_SIGNATURE, GPBP_OPERATION_WRITE));
    }
    
    PUT_BYTE( &pBlock->File._image.bBlockType, 0, eFREEBLOCK );

    /* chain to SRN's freelist */
    pDataBlock = (_DataBlockHDR*)pBlock;
    pDataBlock->File._image.nCascadingBlockNum = pBlock->pNS->pHDS->oSRN.nFreeListHead;
    pBlock->pNS->pHDS->oSRN.nFreeListHead = pBlock->nBlockNum;

    /* update block to file */
    ChkDR(_HdsWriteBlockHDR(pBlock));
    
    /* commit changes of root node to file */
    ChkDR(_HdsUpdateSRN(pBlock->pNS->pHDS));


ErrorExit:
    if ( pCopyBuffer )
    {
        _Hds_free(pBlock->pNS->pHDS, pCopyBuffer);
    }
    return dr;
}


/**********************************************************************
** Function:    _HdsLocateFreeBlockForSlot
** Synopsis:    Locate a block with timestamp >= given timestamp and have 
**              enough space to store the license.
** Arguments:   [pHDS] -- 
** Returns:     DRM_SUCCESS on success
** Notes:       
***********************************************************************
*/
static DRM_RESULT
_HdsLocateFreeBlockForSlot(
    IN  _NsContext    *pNS,
    IN  const DRM_BYTE bHashKey [__CB_DECL(DRM_HDS_HASHKEY_SIZE)], 
    IN  DRM_DWORD      cbSlot,
    OUT _SlotContext  *pSlotCtx,
    IN  DRM_UINT64     ui64TimeStamp)
{
    DRM_RESULT     dr=DRM_SUCCESS;
    DRM_BOOL       fResult=FALSE;
    _CommBlockHDR *pParentBlock=NULL;

#ifdef _DEBUG
    DRM_DWORD nNumLevels=0;
#endif

    DRMASSERT(pNS && bHashKey && pNS->fInited && pSlotCtx && 
        pSlotCtx->eStatus!=eSlotCtxUninit && cbSlot);

    /* pSlotCtx might have something we can start with */
    if ( pSlotCtx->eStatus==eSlotCtxReady &&   /* pSlotCtx has info we can use */
        MEMCMP(bHashKey, pSlotCtx->oSlotHeader.oHashkey.rgb, DRM_HDS_HASHKEY_SIZE)==0 )   /* make sure key1 is the same */
    {
        ChkDR(_HdsAllocBlockBuffer(pNS, eCHILDBLOCK, &pParentBlock));
        if ( pSlotCtx->nBlockWithLargestFreeSpace!=0 )
        {
            if ( pSlotCtx->dwLargestSpace >= cbSlot )
            {
                ChkDR(_HdsLoadBlockHDR(pNS, pSlotCtx->nBlockWithLargestFreeSpace, 
                    &pSlotCtx->pCurrChildBlock));
                goto ErrorExit;
            }
            else
            {
                /* let's start with this block */
                ChkDR(_HdsLoadBlockHDR(pNS, pSlotCtx->nBlockWithLargestFreeSpace, &pParentBlock));
            }
        }
        else
        {
            /* start with current child block in pSlotCtx */
            ChkDR(_HdsCopyBlockBuffer(pParentBlock, pSlotCtx->pCurrChildBlock));
        }
    }

    while (TRUE)
    {
        /* find a child block according to bHashKey and parentBlockNum */
        ChkDR(_HdsHashToChildBlock(pNS, pParentBlock, bHashKey, &pSlotCtx->pCurrChildBlock, 
            &fResult, NULL));
#ifdef _DEBUG
        ++nNumLevels;
#endif
        if ( !fResult )
        {
            /* no children block found, let's create one */
            DRM_DWORD nIndex=0;

            ChkDR(_HdsHashKeyToIndex(pNS, bHashKey, pParentBlock->nBlockNum, &nIndex));
            ChkDR(_HdsAllocBlock(pNS, pParentBlock->nBlockNum, &pSlotCtx->pCurrChildBlock, eCHILDBLOCK));
            pSlotCtx->eStatus = eSlotCtxReady;

            /* assign new block num to parent block and update the parent block to file */
            ChkDR(_HdsGetPutChildBlockNum(pNS, ((_ChildBlockHDR*)pParentBlock)->File._image.bChildAllocTable,
                nIndex, &(pSlotCtx->pCurrChildBlock->nBlockNum), FALSE));
            
            ChkDR(_HdsWriteBlockHDR(pParentBlock));                         /* write Block HDR */
            break;
        }

        /* found one, check if we can use it */
        else if ( ((_ChildBlockHDR*)(pSlotCtx->pCurrChildBlock))->File._image.nFreeMem >= cbSlot &&   /* check avail space */
                  DRM_UI64Les( ui64TimeStamp, pSlotCtx->pCurrChildBlock->File._image.ui64TimeStamp ) )    /* time stamp later than what we need */
        {
            pSlotCtx->eStatus = eSlotCtxReady;
            break;
        }

        /* keep digging along the path */
        if ( pParentBlock == NULL )
        {
            ChkDR(_HdsAllocBlockBuffer(pNS, eCHILDBLOCK, &pParentBlock));
        }
        
        ChkDR(_HdsCopyBlockBuffer(pParentBlock, (pSlotCtx->pCurrChildBlock)));
    }

#ifdef _DEBUG
    /*TRACE(("[Levels traversed down=%d]\n", nNumLevels)); */
#endif
    
ErrorExit:
    if (pParentBlock)
    {
        _Hds_free(pNS->pHDS, pParentBlock);
    }
    
    return dr;
}


/**********************************************************************
** Function:    CreateOversizedSlot
** Synopsis:    store an oversized license over 1+ cascading _DATANODE's
** Arguments:   [pHDS] -- 
**              [pBlock] --
**              [pbLicense] --
**              [cbLicense] --
** Returns:     DRM_SUCCESS on success
** Notes:       
***********************************************************************
*/
static DRM_RESULT
_HdsCreateOversizedSlot(
    IN _NsContext *pNS,
    IN DRM_DWORD nParentBlockNum,
    IN DRM_DWORD cbSlot,
    OUT _CommBlockHDR *pFirstDataBlock)
{
    DRM_RESULT dr=DRM_SUCCESS;
    DRM_DWORD nBytesLeft=0;
    _CommBlockHDR *pDataBlock=NULL;
    _CommBlockHDR *pTmpBlock=NULL;
    DRM_BOOL fCopied=FALSE;

    DRMASSERT(pNS && pNS->fInited && cbSlot>0 && pFirstDataBlock);

    /* allocate working buffer */
    ChkDR(_HdsAllocBlockBuffer(pNS, eDATABLOCK, &pDataBlock));
    ChkDR(_HdsAllocBlockBuffer(pNS, eDATABLOCK, &pTmpBlock));

    /* allocate the first _DataBlockHDR */
    ChkDR(_HdsAllocBlock(pNS, nParentBlockNum, &pDataBlock, eDATABLOCK));

    /* divide the slot content into packets and store the packets in _DataBlockHDR's */
    nBytesLeft = cbSlot;
    while (nBytesLeft > 0)
    {
        if ( nBytesLeft <= pNS->pHDS->nImagesize_DataBlockPayload )
        {
            /* we are done */
            ((_DataBlockHDR*)pDataBlock)->File._image.nCascadingBlockNum = 0; 
            ChkDR(_HdsWriteBlockHDR(pDataBlock));
            if ( !fCopied )
            {
                ChkDR(_HdsCopyBlockBuffer(pFirstDataBlock, pDataBlock));
            }
            break;
        }
        else
        {
            /* switch buffer and allocate a new block, update the block to file and keep on */
            ChkDR(_HdsAllocBlock(pNS, pDataBlock->nBlockNum, &pTmpBlock, eDATABLOCK));
            ((_DataBlockHDR*)pDataBlock)->File._image.nCascadingBlockNum = pTmpBlock->nBlockNum;
            ChkDR(_HdsWriteBlockHDR(pDataBlock));
            if ( !fCopied )
            {
                fCopied = TRUE;
                ChkDR(_HdsCopyBlockBuffer(pFirstDataBlock, pDataBlock));
            }

            nBytesLeft -= pNS->pHDS->nImagesize_DataBlockPayload;
            _HdsCopyBlockBuffer(pDataBlock, pTmpBlock);
        }
    }

    ChkBOOL( OEM_FlushFileBuffers(pNS->pHDS->fp), DRM_E_FILEWRITEERROR );

ErrorExit:
    /* free these have to be in reverse order of alloc */
    if (pTmpBlock)
    {
        _Hds_free(pNS->pHDS, pTmpBlock);
    }
    if (pDataBlock)
    {
        _Hds_free(pNS->pHDS, pDataBlock);
    }
    return dr;
}

/**********************************************************************
** Function:    OpenSlotFromHint
** Synopsis:    
** Arguments:   [pCurrBlock] -- block to search in
**              [bHashkey] --hashkey to be searched
**              [pUniquekey] --uniquekey to be searched
**              [pSlotHint] --hint to be used for finding slot
**              [pcbSlotSize] -- slot size
**              [pSlotCtx] -- slot context to be returned
** Returns:     DRM_SUCCESS on success
** Notes:       Slot content is preceded with a _SlotHeader structure in the payload
**              of _ChildBlockHDR. _SlotHeader.dwSlotSize indicates the actual size of the
**              slot.
***********************************************************************
*/
static DRM_RESULT _HdsOpenSlotFromHint(
    IN       _NsContext        *pNS,
    IN const DRM_HDS_HASHKEY   *pHashkey,
    IN const DRM_HDS_UNIQUEKEY *pUniquekey,
    IN       _SlotHint         *pSlotHint,
    IN       DRM_BOOL           fIsDataSlot,
    IN OUT   DRM_DWORD         *pcbSlotSize,
       OUT   _SlotContext      *pSlotCtx)
{
    DRM_RESULT          dr              = DRM_SUCCESS;
    DRM_BOOL            fResult         = FALSE;
    _CommBlockHDR      *pBlock          = NULL;
    DRM_DWORD           dwChildIndex    = 0;
    _SlotHeader         oHeader;
    _ESlotSearchMode    eSearchMode     = fIsDataSlot? eSearchDataSlot : eSearchNamespace;
    DRM_BOOL            fAcceptSlot     = FALSE;

    ChkArg(ISVALIDCONTEXT(pNS, eCfgContextSignature));
    ChkArg(!_IsNULL(pHashkey->rgb, DRM_HDS_HASHKEY_SIZE));
    ChkArg( pSlotHint != NULL );
    DRMASSERT(pNS && pNS->fInited && pNS->pHDS && pNS->pHDS->fp!=OEM_INVALID_HANDLE_VALUE
        && pSlotCtx && pHashkey && pUniquekey);
    DRMASSERT(pNS->nCfgRootBlockNum>0);

    ChkArg( !(pSlotHint->nBlockNum == 0 && pSlotHint->nSlotPosInBlock == 0) );

    pSlotCtx->nBlockWithLargestFreeSpace = 0;
    pSlotCtx->dwLargestSpace = 0;
    *pcbSlotSize = 0;

    ChkDR(_HdsLoadBlockHDR(pNS, pSlotHint->nBlockNum, &pBlock));

    ChkDR(_HdsGetPutBlockPayload(pBlock, pSlotHint->nSlotPosInBlock, 
        SIZEOF(_SlotHeader), (DRM_BYTE*)&oHeader, GPBP_OPERATION_READ));
    FIX_ENDIAN_DWORD( oHeader.dwSlotSize );
    FIX_ENDIAN_DWORD( oHeader.dwAttributes );

    if ((oHeader.dwAttributes & eSlotIsHidden) != 0)
    {
        fAcceptSlot = (eSearchMode == eSearchHiddenOnly);
    }
    else
    {
        fAcceptSlot = (eSearchMode == eSearchSlotDontCare 
                || (eSearchMode == eSearchNamespace &&  (oHeader.dwAttributes & eSlotIsNamespace))
                || (eSearchMode == eSearchDataSlot  && !(oHeader.dwAttributes & eSlotIsNamespace)));

    }

    if ( !fAcceptSlot )
    {
        ChkDR(DRM_E_HDSSLOTNOTFOUND);
    }

    /* check the keys */
    if (!((pHashkey   == NULL || MEMCMP(oHeader.oHashkey.rgb,   pHashkey->rgb,   DRM_HDS_HASHKEY_SIZE)   == 0)
    &&  (pUniquekey == NULL || MEMCMP(oHeader.oUniquekey.rgb, pUniquekey->rgb, DRM_HDS_UNIQUEKEY_SIZE) == 0)))
    {
        ChkDR(DRM_E_HDSSLOTNOTFOUND);
    }


    /* prepare for return */

    /* fill in the fields in _SlotContext */
    MEMCPY(&pSlotCtx->oFileBlock, pBlock, SIZEOF(_CommBlockHDR));
    ChkDR(_HdsCopyBlockBuffer(pSlotCtx->pCurrChildBlock, pBlock));  /* copy curr block to slot context */

    MEMCPY(&pSlotCtx->oSlotHeader, &oHeader, SIZEOF(_SlotHeader));
    pSlotCtx->dwSlotPosInBlock = pSlotHint->nSlotPosInBlock;
    pSlotCtx->dwSeekPointer = 0;

    /* check if we need to load _DataBlockHDR */
    if ( !ISOVERSIZESLOT(oHeader.dwSlotSize, pSlotCtx->pNS) )
    {
        pSlotCtx->pCurrDataBlock = NULL;
    }
    else
    {
        /* get _DataBlockHDR block # */
        ChkDR(_HdsGetPutBlockPayload(pBlock, pSlotHint->nSlotPosInBlock+SIZEOF(_SlotHeader), 
            SIZEOF(DRM_DWORD), (DRM_BYTE*)&(pSlotCtx->dwFirstDataBlockNum), GPBP_OPERATION_READ));
        FIX_ENDIAN_DWORD( pSlotCtx->dwFirstDataBlockNum );
        /* load the first _DataBlockHDR */
        ChkDR(_HdsLoadBlockHDR(pSlotCtx->pNS, pSlotCtx->dwFirstDataBlockNum, &pSlotCtx->pCurrDataBlock));
        pSlotCtx->dwNumofDataBlockVisited = 0;
    }
 
    *pcbSlotSize = oHeader.dwSlotSize;
    pSlotCtx->eStatus = eSlotCtxReady;

ErrorExit:

    if ( pBlock != NULL )
    {
        _Hds_free(pNS->pHDS, pBlock);
    }

    return dr;
}


/**********************************************************************
** Function:    SearchSlotInBlock
** Synopsis:    search a license in the block at next searchable position.
** Arguments:   [pCurrBlock] -- block to search in
**              [bHashKey] --hashkey to be searched
**              [pbUniqueKey] --uniquekey to be searched
**              [pcbSlotSize] -- slot size
**              [pSlotCtx] -- slot context to be returned
**              [pfResult] --TRUE if a matching slot is found
** Returns:     DRM_SUCCESS on success
** Notes:       Slot content is preceded with a _SlotHeader structure in the payload
**              of _ChildBlockHDR. _SlotHeader.dwSlotSize indicates the actual size of the
**              slot.
***********************************************************************
*/
static DRM_RESULT _HdsSearchSlotInBlock(
    IN  _CommBlockHDR           *pCurrBlock,
    IN  const DRM_HDS_HASHKEY   *pHashkey,  /* if Hashkey is NULL, Uniquekey MUST be NULL */
    IN  const DRM_HDS_UNIQUEKEY *pUniquekey,
    IN  _ESlotSearchMode         eSearchMode,
    OUT _SlotContext            *pSlotCtx,  /* user given Dataslot context buffer */
    OUT DRM_BOOL                *pfResult)
{
    DRM_RESULT dr=DRM_SUCCESS;
    DRM_DWORD nBytesToSearch=0;
    DRM_DWORD nBytesToSkip=0;
    _ChildBlockHDR *pChild=(_ChildBlockHDR*)pCurrBlock;
    _SlotHeader oHeader = {0};

    /* Note: It is safe not to issue any lock when search each slot header in
    ** the block. It is because the caller function should already lock the SRN
    ** either shared or exclusive before calling this function. Functions which
    ** writing to the slot header(s) must issued exclusive lock to the SRN. So
    ** if we reach this point, there are no thread(s) or process(es) writing to
    ** the slot header(s).
    */

    DRMASSERT( pCurrBlock && pSlotCtx && pfResult 
        && pSlotCtx->eStatus>=eSlotCtxInited && pSlotCtx->pCurrChildBlock
        && GET_BYTE( &pSlotCtx->pCurrChildBlock->File._image.bBlockType, 0 ) == eCHILDBLOCK);
    if ( pHashkey==NULL )
    {
        DRMASSERT(pUniquekey==NULL);   /* if Hashkey is NULL, Uniquekey MUST be NULL */
    }

    *pfResult = FALSE;
    ZEROMEM(&oHeader, SIZEOF(_SlotHeader));
    nBytesToSearch = pSlotCtx->pNS->nImagesize_ChildBlockPayload - pChild->File._image.nFreeMem;
    if ( nBytesToSearch == 0 )
    {
        goto ErrorExit;
    }
    
    /* walk the payload of child node to find matching HashKey and UniqueKey. Each license are stored 
    ** one by one in the payload ... 
    */
    if ( pChild->nNextSlotPos == MAXSLOTPOS )
    {
        pChild->nNextSlotPos = 0;
    }

    for (; pChild->nNextSlotPos<nBytesToSearch; pChild->nNextSlotPos+=nBytesToSkip)
    {
        DRM_BOOL fAcceptSlot = FALSE;

        /* read the _SlotHeader structure */

        ChkDR(_HdsGetPutBlockPayload((_CommBlockHDR*)pChild, pChild->nNextSlotPos, 
            SIZEOF(_SlotHeader), (DRM_BYTE*)&oHeader, GPBP_OPERATION_READ));
        FIX_ENDIAN_DWORD( oHeader.dwSlotSize );
        FIX_ENDIAN_DWORD( oHeader.dwAttributes );

        if ((oHeader.dwAttributes & eSlotIsHidden) != 0)
        {
            fAcceptSlot = (eSearchMode == eSearchHiddenOnly);
        }
        else
        {
            fAcceptSlot = (eSearchMode == eSearchSlotDontCare 
                       || (eSearchMode == eSearchNamespace &&  (oHeader.dwAttributes & eSlotIsNamespace))
                       || (eSearchMode == eSearchDataSlot  && !(oHeader.dwAttributes & eSlotIsNamespace)));

        }

        if (fAcceptSlot)
        {
            /* check the keys */
            if ((pHashkey   == NULL || MEMCMP(oHeader.oHashkey.rgb,   pHashkey->rgb,   DRM_HDS_HASHKEY_SIZE)   == 0)
            &&  (pUniquekey == NULL || MEMCMP(oHeader.oUniquekey.rgb, pUniquekey->rgb, DRM_HDS_UNIQUEKEY_SIZE) == 0))
            {
                goto PrepareSlot;  /* we get a match */
            }
        }
        
        /* skip this slot */
        if ( ISOVERSIZESLOT(oHeader.dwSlotSize, pSlotCtx->pNS) )
        {
            nBytesToSkip = SIZEOF(_SlotHeader) + SIZEOF(DRM_DWORD);
        }
        else
        {
            nBytesToSkip = SIZEOF(_SlotHeader) + oHeader.dwSlotSize;
        }
    }

    /* slot not found !! */
    goto ErrorExit; 

PrepareSlot:
    /* prepare for return */

    /* fill in the fields in _SlotContext */
    MEMCPY(&pSlotCtx->oFileBlock, &pChild->oBlockHeader, SIZEOF(_CommBlockHDR));
    ChkDR(_HdsCopyBlockBuffer(pSlotCtx->pCurrChildBlock, pCurrBlock));  /* copy curr block to slot context */

    MEMCPY(&pSlotCtx->oSlotHeader, &oHeader, SIZEOF(_SlotHeader));
    pSlotCtx->dwSlotPosInBlock = pChild->nNextSlotPos;
    pSlotCtx->dwSeekPointer = 0;

    /* check if we need to load _DataBlockHDR */
    if ( !ISOVERSIZESLOT(oHeader.dwSlotSize, pSlotCtx->pNS) )
    {
        pSlotCtx->pCurrDataBlock = NULL;
    }
    else
    {
        /* data block # is initialized when the slot is first read / written */
        pSlotCtx->dwNumofDataBlockVisited = 0;
    }

    /* update slot pointer in child block */       
    pChild->nCurrSlotPos = pChild->nNextSlotPos; /* set pointer to current Slot */

    /* move next pointer to next slot */
    if ( ISOVERSIZESLOT(oHeader.dwSlotSize, pSlotCtx->pNS) )
    {
        pChild->nNextSlotPos += SIZEOF(_SlotHeader) + SIZEOF(DRM_DWORD);
    }
    else
    {
        pChild->nNextSlotPos += SIZEOF(_SlotHeader) + oHeader.dwSlotSize;
    }

    pSlotCtx->eStatus = eSlotCtxReady;
    *pfResult = TRUE;
ErrorExit:
    return dr;
}

/**********************************************************************
** Function:    SearchSlotInFile
** Synopsis:    
** Arguments:   [pHDS] -- 
** Returns:     DRM_SUCCESS on success
** Notes:       
***********************************************************************
*/
static DRM_RESULT
_HdsSearchSlotInFile(
    IN  _NsContext              *pNS,
    IN  const DRM_HDS_HASHKEY   *pHashkey, 
    IN  const DRM_HDS_UNIQUEKEY *pUniquekey, 
    IN  _ESlotSearchMode         eSearchMode,
    OUT DRM_DWORD                *pcbSlotSize, /* current dataslot size, can be NULL */
    OUT _SlotContext            *pSlotCtx,    /* user given Dataslot context buffer */
    OUT DRM_BOOL                *pfResult)
{
    DRM_RESULT     dr=DRM_SUCCESS;
    _CommBlockHDR *pParentBlock=NULL;
    DRM_DWORD      dwChildIndex=0;    

    DRM_PROFILING_ENTER_SCOPE(L"_HdsSearchSlotInFile", g_pwszEnteringFunction, DRM_PROFILING_DONT_START);
    DRMASSERT( pNS && pHashkey && pSlotCtx && pfResult );

    pSlotCtx->nBlockWithLargestFreeSpace = 0;
    pSlotCtx->dwLargestSpace = 0;
    while (TRUE)
    {
        /* locate a child block which might contain the slot */
        *pfResult = FALSE;
        ChkDR(_HdsHashToChildBlock(pNS, pParentBlock, pHashkey->rgb, 
            &pSlotCtx->pCurrChildBlock, pfResult, &dwChildIndex));
        pSlotCtx->eStatus = eSlotCtxInited;
        if ( !*pfResult )
        {
            break;
        }

#ifdef _CHKHDS_    
        if ( pParentBlock!=NULL )
        {
            printf("\t[Hashing]\tBlock%5d - Alloctable[%d]\n", 
                pParentBlock->nBlockNum, dwChildIndex);
        }
#endif
        /* cache the result of this search path for later use */
        if ( ((_ChildBlockHDR*)pSlotCtx->pCurrChildBlock)->File._image.nFreeMem > pSlotCtx->dwLargestSpace )
        {
            pSlotCtx->nBlockWithLargestFreeSpace = pSlotCtx->pCurrChildBlock->nBlockNum;
            pSlotCtx->dwLargestSpace = ((_ChildBlockHDR*)pSlotCtx->pCurrChildBlock)->File._image.nFreeMem;
        }

        /* search the possible slot in the block */
        ChkDR(_HdsSearchSlotInBlock(pSlotCtx->pCurrChildBlock, pHashkey, pUniquekey, 
            eSearchMode, pSlotCtx, pfResult));
        
        if ( *pfResult )
        {
            if ( pcbSlotSize )
            {
                *pcbSlotSize = pSlotCtx->oSlotHeader.dwSlotSize;
            }
#ifdef _CHKHDS_    
            printf("\t[Found in]\tBlock%5d\n", pSlotCtx->pCurrChildBlock->nBlockNum);
#endif            
            break;
        }
        
        /* keep digging along the hash path */
        if ( pParentBlock == NULL )
        {
            /* allocate buffer for the process */
            ChkDR(_HdsAllocBlockBuffer(pNS, eCHILDBLOCK, &pParentBlock));
        }
        _HdsCopyBlockBuffer(pParentBlock, pSlotCtx->pCurrChildBlock);
    }

ErrorExit:
    if ( pParentBlock )
    {
        _Hds_free(pNS->pHDS, pParentBlock);
    }
    DRM_PROFILING_LEAVE_SCOPE(L"_HdsSearchSlotInFile", g_pwszLeavingFunction);
        
    return dr;
}

/**********************************************************************
** Function:    _HdsAllocSlotInFile
** Synopsis:    Allocate a slot of specified size in the file.
** Arguments:   [pHDS] -- 
**              [pbLicense] --
**              [cbLicense] --size must be at least the size of DRM_LICENSE_V1
** Returns:     DRM_SUCCESS on success
** Notes:       
***********************************************************************
*/
static DRM_RESULT _HdsAllocSlotInFile( 
    IN  _NsContext     *pNS,
    IN  const DRM_BYTE  bHashKey   [__CB_DECL(DRM_HDS_HASHKEY_SIZE)], 
    IN  const DRM_BYTE  bUniqueKey [__CB_DECL(DRM_HDS_UNIQUEKEY_SIZE)], 
    IN  DRM_BOOL        fIsDataSlot,
    OUT DRM_DWORD       cbSlot,   /* current dataslot size */
    OUT _SlotContext   *pSlotCtx)  /* user given Dataslot context buffer */
{
    DRM_RESULT      dr=DRM_SUCCESS;
    DRM_DWORD       dwPhysicalSize=0, dwSlotPos=0;
    _ChildBlockHDR *pChild=NULL;
    _SlotHeader     oHeader = { 0 };
    
    DRMASSERT(pNS && bHashKey && bUniqueKey && pNS->fInited && cbSlot && pSlotCtx);
    
    /* compute the space needed in _ChildBlockHDR */
    if ( ISOVERSIZESLOT(cbSlot, pNS) )
    {
        dwPhysicalSize = SIZEOF(_SlotHeader)+SIZEOF(DRM_DWORD);
    }
    else
    {
        dwPhysicalSize = SIZEOF(_SlotHeader)+cbSlot;
    }

    /***************************************************************************
    **** Note: exclusive access to SRN guard the concurrency of this action. ***
    **** Caller of this function is expect to lock SRN for exclusive access. ***
    ****************************************************************************
    */

    /* locate a _ChildBlock for this slot */
    ChkDR(_HdsLocateFreeBlockForSlot(pNS, bHashKey, dwPhysicalSize, pSlotCtx, DRM_UI64( 0 ) ));
    pChild = (_ChildBlockHDR*)(pSlotCtx->pCurrChildBlock);

    /* init slot content */
    dwSlotPos = pNS->nImagesize_ChildBlockPayload -pChild->File._image.nFreeMem;
    oHeader.dwSlotSize = cbSlot;
    MEMCPY(oHeader.oHashkey.rgb, bHashKey, DRM_HDS_HASHKEY_SIZE);
    MEMCPY(oHeader.oUniquekey.rgb, bUniqueKey, DRM_HDS_UNIQUEKEY_SIZE);
    if( !fIsDataSlot )
    {
        oHeader.dwAttributes |= eSlotIsNamespace;
    }

    /* store _SlotHeader of the slot */
    FIX_ENDIAN_DWORD( oHeader.dwSlotSize );
    FIX_ENDIAN_DWORD( oHeader.dwAttributes );
    dr = _HdsGetPutBlockPayload(pSlotCtx->pCurrChildBlock, dwSlotPos, SIZEOF(_SlotHeader), 
        (DRM_BYTE *)&oHeader, GPBP_OPERATION_WRITE);
    FIX_ENDIAN_DWORD( oHeader.dwSlotSize );
    FIX_ENDIAN_DWORD( oHeader.dwAttributes );
    ChkDR( dr );

    /* allocate _DataBlock if necessary */
    if ( !ISOVERSIZESLOT(cbSlot, pNS) )
    {
        pSlotCtx->pCurrDataBlock = NULL;
    }
    else
    {
        ChkDR(_HdsCreateOversizedSlot(pNS, pSlotCtx->pCurrChildBlock->nBlockNum, cbSlot, 
            pSlotCtx->pCurrDataBlock));
        pSlotCtx->dwNumofDataBlockVisited = 0;
        pSlotCtx->dwFirstDataBlockNum = pSlotCtx->pCurrDataBlock->nBlockNum;

        /* store the datablock# to this slot */
        FIX_ENDIAN_DWORD( pSlotCtx->pCurrDataBlock->nBlockNum );
        dr = _HdsGetPutBlockPayload(pSlotCtx->pCurrChildBlock, dwSlotPos+SIZEOF(_SlotHeader), 
            SIZEOF(DRM_DWORD), (DRM_BYTE *)&(pSlotCtx->pCurrDataBlock->nBlockNum), GPBP_OPERATION_WRITE);
        FIX_ENDIAN_DWORD( pSlotCtx->pCurrDataBlock->nBlockNum );
        ChkDR( dr );
    }

    /* init pSlotCtx */
    MEMCPY(&pSlotCtx->oFileBlock, &pChild->oBlockHeader, SIZEOF(_CommBlockHDR));
    MEMCPY(&pSlotCtx->oSlotHeader, &oHeader, SIZEOF(_SlotHeader));
    pSlotCtx->dwSlotPosInBlock = dwSlotPos;
    pSlotCtx->dwSeekPointer = 0;
    pSlotCtx->eLockMode = eDRM_HDS_LOCKEXCLUSIVE;

    /* set the slot content to NULL. We are overloading the function here.  */
    /* oversize slot payload is always NULL by default */
    if ( !ISOVERSIZESLOT(cbSlot, pNS) )
    {
        _HdsRemoveSlot(pSlotCtx, eRemoveSlotSetNull);
        pSlotCtx->oSlotHeader.dwAttributes = 0;     /* _HdsRemoveSlot() sets dwAttributes to hidden, we have to revert it here. */
        pSlotCtx->dwSeekPointer = 0;                /* reset seek pointer */
    }

    /* update freemem in block and write the block to file */
    pChild->File._image.nFreeMem -= dwPhysicalSize;
    ChkDR(_HdsWriteBlockHDR(pSlotCtx->pCurrChildBlock));

ErrorExit:

    return dr;
}


/*
** Note: when pNamespace is NULL, it is the CFG of the namespace stoe.
*/
static DRM_RESULT _HdsInitNsContext(
    IN  _HdsContext             *pHDS,
    OUT _NsContext              *pCfg,
    IN  const DRM_HDS_NAMESPACE *pNamespace,
    IN  DRM_DWORD                nCfgRootBlockNum,
    IN  DRM_WORD                 wMaxNumChildren)
{
    DRM_RESULT dr = DRM_SUCCESS;
    
    _ChildBlockHDR *pChild = NULL; /* declared solely for SIZEOF below */

    DRMASSERT(pHDS && pHDS->fInited && pCfg && wMaxNumChildren > 0);

    
    /* initialize the computed info for this namespace in the context */
    ZEROMEM(pCfg, SIZEOF(_NsContext));
    pCfg->wContextSize = SIZEOF(_NsContext);
    pCfg->eContextSignature = eCfgContextSignature;
    pCfg->pHDS = pHDS;
    if ( pNamespace )
    {
        DRMSIZEASSERT( SIZEOF( pCfg->oNamespace ), SIZEOF( *pNamespace ) );
        MEMCPY( &pCfg->oNamespace, pNamespace, SIZEOF( *pNamespace ) );
    }
    
    pCfg->fInited                      = TRUE;
    pCfg->nCfgRootBlockNum             = nCfgRootBlockNum;
    pCfg->wMaxNumChildren              = wMaxNumChildren;
    pCfg->nImagesize_ChildAllocTable   = wMaxNumChildren * pHDS->oSRN.eBlockNumType;
    pCfg->nImagesize_ChildBlockHDR     = SIZEOF(pChild->File._image.nFreeMem) 
                                       + 1 /*SIZEOF(pChild->File._image.bChildAllocTable)  */
                                       + pCfg->nImagesize_ChildAllocTable;
    pCfg->nChildBlockPayloadPos        = pHDS->nImagesize_FileBlockHDR + pCfg->nImagesize_ChildBlockHDR;
    pCfg->nImagesize_ChildBlockPayload = pHDS->oSRN.dwBlockSize - pCfg->nChildBlockPayloadPos;

    /* Not enough room in each block for any data */
    ChkBOOL((pHDS->oSRN.dwBlockSize > pCfg->nChildBlockPayloadPos), DRM_E_HDSINVALIDSTORE);

ErrorExit:
    
    return dr;
}

/*
**
*/
static DRM_RESULT
_HdsInitSlotContext(
    _NsContext *pNS,
    OUT DRM_BYTE *pbSlotContext,      /* user given Dataslot context buffer */
    IN DRM_DWORD cbSlotContext)    /* Dataslot context size */
{
    DRM_RESULT dr=DRM_SUCCESS;
    _SlotContext *pSlotCtx=NULL;

    DRMASSERT(pNS && pbSlotContext && cbSlotContext);
           
    ZEROMEM(pbSlotContext, cbSlotContext);
    
    pSlotCtx = (_SlotContext*)pbSlotContext;
    
    pSlotCtx->eContextSignature = eSlotContextSignature;
    pSlotCtx->pNS = pNS;
    pSlotCtx->eStatus = eSlotCtxInited;
    pSlotCtx->dwContextSize = cbSlotContext;
    pSlotCtx->pCurrChildBlock = (_CommBlockHDR *)pSlotCtx->bBuff;
    pSlotCtx->pCurrDataBlock  = (_CommBlockHDR *) (pSlotCtx->bBuff + __CB_DECL(GetMemsize_ChildBlock(pNS)));
    ChkDR(_HdsInitBlockBuffer(pNS, pSlotCtx->pCurrChildBlock, 0, eCHILDBLOCK));
    ChkDR(_HdsInitBlockBuffer(pNS, pSlotCtx->pCurrDataBlock, 0, eDATABLOCK));

ErrorExit:
    return dr;
}


/* 
** Create or open dataslot using given key1 and key2 
*/
static DRM_RESULT _HdsCreateAndOpenSlot( 
    IN     _NsContext              *pNS, 
    IN     const DRM_HDS_HASHKEY   *pHashKey, 
    IN     const DRM_HDS_UNIQUEKEY *pUniqueKey, 
    IN     DRM_BOOL                 fIsDataSlot,
    IN OUT DRM_DWORD                cbSlot,
    OUT    _SlotContext            *pSlotCtx,
    IN     DRM_DWORD                eLockMode)
{
    DRM_RESULT dr=DRM_SUCCESS;
    DRM_BOOL   fResult=FALSE;
    
    ChkArg(ISVALIDCONTEXT(pNS, eCfgContextSignature));
    ChkArg(!_IsNULL(pHashKey->rgb, DRM_HDS_HASHKEY_SIZE));
    DRMASSERT(pNS        != NULL
           && pNS->pHDS  != NULL
           && pSlotCtx   != NULL
           && pHashKey   != NULL
           && pUniqueKey != NULL);

    DRMASSERT(pNS->fInited);
    DRMASSERT(pNS->pHDS->fp != OEM_INVALID_HANDLE_VALUE);
    DRMASSERT(pNS->nCfgRootBlockNum > 0);
    DRMASSERT(cbSlot                > 0);  /* new slot size must be > 0 */

    ChkDR(_HdsSearchSlotInFile(pNS, 
                               pHashKey, 
                               pUniqueKey, 
                              (fIsDataSlot ? eSearchDataSlot : eSearchNamespace), 
                               NULL, 
                               pSlotCtx, 
                              &fResult));
    if ( fResult )
    {
        ChkDR(DRM_E_HDSSLOTEXIST);
    }

    ChkDR(_HdsAllocSlotInFile(pNS, pHashKey->rgb, pUniqueKey->rgb, fIsDataSlot, cbSlot, pSlotCtx));
    pSlotCtx->eLockMode = eLockMode;

    /* this will not be blocked because this is a new slot and we have SRN exclusive lock */
    ChkDR(_HdsLockSlot(pSlotCtx, eLockMode));
    
ErrorExit:

    return dr;
}


/* 
** Crreate or open dataslot using given key1 and key2 
*/
static DRM_RESULT _HdsOpenExistingSlot( 
    IN       _NsContext        *pNS,
    IN const DRM_HDS_HASHKEY   *pHashKey,
    IN const DRM_HDS_UNIQUEKEY *pUniqueKey,
    IN       DRM_BOOL           fIsDataSlot,
    IN OUT   DRM_DWORD         *pcbSlotSize,
       OUT   _SlotContext      *pSlotCtx)
{
    DRM_RESULT dr=DRM_SUCCESS;
    DRM_BOOL   fResult=FALSE;

    ChkArg(ISVALIDCONTEXT(pNS, eCfgContextSignature));
    ChkArg(!_IsNULL(pHashKey->rgb, DRM_HDS_HASHKEY_SIZE));
    DRMASSERT(pNS && pNS->fInited && pNS->pHDS && pNS->pHDS->fp!=OEM_INVALID_HANDLE_VALUE
        &&  pSlotCtx && pHashKey && pUniqueKey);
    DRMASSERT(pNS->nCfgRootBlockNum>0);

    ChkDR(_HdsSearchSlotInFile(pNS, pHashKey, pUniqueKey, 
        fIsDataSlot? eSearchDataSlot : eSearchNamespace, pcbSlotSize, pSlotCtx, &fResult));
    if ( !fResult )
    {
        ChkDR(DRM_E_HDSSLOTNOTFOUND);
    }
    
ErrorExit:

    return dr;
}


/*
** Verify the given slot context to make sure the Child and Data block are consistent.
*/
static DRM_RESULT
_HdsVerifySlotContext(
    IN OUT _SlotContext *pSlotCtx,
    OUT DRM_BOOL *pfValid)
{
    DRM_RESULT dr=DRM_SUCCESS;
    _CommBlockHDR *pBlock=NULL;
    _CommBlockHDR *pCurrBlock = NULL;
    _SlotHeader slotHeader;
    _CommBlockHDR *pTmpBlock=NULL;
    
    DRMASSERT(pSlotCtx && pSlotCtx->eStatus==eSlotCtxReady && pfValid);
    *pfValid = FALSE;

    /* load generic block header */
    pBlock = &pSlotCtx->oFileBlock;
    pCurrBlock = pSlotCtx->pCurrChildBlock;

    /* load generic block header */
    ChkDR(_HdsGetPutBlockHDR(pSlotCtx->pNS, pSlotCtx->oFileBlock.nBlockNum, &pBlock, GPBH_GENERIC_ONLY, GPBH_OPERATION_READ));

    /* verify the generic header */
    if ( DRM_UI64Eql( pSlotCtx->oFileBlock.File._image.ui64TimeStamp, pCurrBlock->File._image.ui64TimeStamp )
      && MEMCMP(pSlotCtx->oFileBlock.File._image.bBlockHash, pCurrBlock->File._image.bBlockHash, SIZEOF( pCurrBlock->File._image.bBlockHash ))==0 )
    {
       *pfValid = TRUE;
        goto ErrorExit;
    }

    /* the current block has been updated/written since last read */

    /* check if it is still a CHILD block or belong to the same parent */
    if ( GET_BYTE( &pSlotCtx->oFileBlock.File._image.bBlockType, 0 ) != GET_BYTE( &pCurrBlock->File._image.bBlockType, 0 )
      || pSlotCtx->oFileBlock.File._image.nParentBlockNum            != pCurrBlock->File._image.nParentBlockNum )
    {
        goto ReloadChildBlock;
    }

    /* load the child block in a tmp buffer */
    ChkDR(_HdsAllocBlockBuffer(pSlotCtx->pNS, eCHILDBLOCK, &pTmpBlock));
    ChkDR(_HdsGetPutBlockHDR(pSlotCtx->pNS, pSlotCtx->oFileBlock.nBlockNum, &pTmpBlock, GPBH_ANY_HEADER, GPBH_OPERATION_READ));

    /* try to load the current slot header from file */
    dr = _HdsGetPutBlockPayload(pTmpBlock, pSlotCtx->dwSlotPosInBlock,
        SIZEOF(_SlotHeader), &slotHeader, GPBP_OPERATION_READ);
    FIX_ENDIAN_DWORD( slotHeader.dwSlotSize );
    FIX_ENDIAN_DWORD( slotHeader.dwAttributes );
    if ( DRM_SUCCEEDED(dr) )
    {
        if (MEMCMP(&slotHeader, &pSlotCtx->oSlotHeader, SIZEOF(_SlotHeader))==0 )
        {
            /* the current slot is intact, update the block buffer and continue */
            ChkDR(_HdsGetPutBlockHDR(pSlotCtx->pNS, pSlotCtx->oFileBlock.nBlockNum, &pCurrBlock, GPBH_ANY_HEADER, GPBH_OPERATION_READ));
            *pfValid = TRUE;
            goto ErrorExit;
        }
    }

ReloadChildBlock:       
{

#if _MULTI_THREADING_SUPPORT==1
    /* 
    ** This might happen if slot has changed since it was opened by this thread.
    */
    dr = DRM_S_FALSE;
    goto ErrorExit;
#else

    /* reload and initialize the slot context using the hashkey and uniquekey in 
       current slot context */

    /*
    _NsContext *pNS = pSlotCtx->pNS;
    _SlotHeader oSlotHeader = pSlotCtx->oSlotHeader;
    DRM_DWORD dwSeekPointer = pSlotCtx->dwSeekPointer;
    DRM_HDS_SLOT_CONTEXT *pSlotContext=NULL;
    
    ChkDR(_HdsOpenExistingSlot(pNS,&oSlotHeader.oHashkey, TRUE, &oSlotHeader.oUniquekey, NULL, pSlotCtx));

    pSlotCtx->dwSeekPointer = dwSeekPointer;
    *pfValid = TRUE;
    */
#endif
}

ErrorExit:
    if ( pTmpBlock )
    {
        _Hds_free(pSlotCtx->pNS->pHDS, pTmpBlock);
    }
    
    return dr;
}


/*
**
*/
static DRM_RESULT DRM_API 
_HdsReadWriteSlot( 
    IN _SlotContext *pSlotCtx,
    IN const DRM_DWORD cbData,  /* count in bytes to read/write */
    OUT DRM_BYTE  *pbData,      /* buffer for read/write */
    OUT DRM_DWORD *pcbIO,       /* actual amount of count in bytes read/written */
    IN DRM_BOOL fGet)
{
    DRM_RESULT dr=DRM_SUCCESS;
    DRM_DWORD cbIO=0;
    DRMASSERT(pSlotCtx && pSlotCtx->eStatus==eSlotCtxReady && pbData); 

    if ( !ISOVERSIZESLOT(pSlotCtx->oSlotHeader.dwSlotSize, pSlotCtx->pNS) )
    {
        /* calc the position relative to the _ChildBlockHDR */
        DRM_DWORD wPos = pSlotCtx->dwSlotPosInBlock + SIZEOF(_SlotHeader) + pSlotCtx->dwSeekPointer;
        if ( (pSlotCtx->dwSeekPointer + cbData) > pSlotCtx->oSlotHeader.dwSlotSize )
        {
            cbIO = pSlotCtx->oSlotHeader.dwSlotSize - pSlotCtx->dwSeekPointer;
        }
        else
        {
            cbIO = cbData;
        }

        ChkDR(_HdsGetPutBlockPayload(pSlotCtx->pCurrChildBlock, wPos, cbIO, pbData, fGet));
    }
    else    /* Slot data is contained in a chain of _DataBlockHDR's */
    {
        DRM_DWORD nRelativeSeekPtr=0;
        DRM_DWORD nBytesLeft=0;
        DRM_DWORD nNextBlockNum=0;
        DRM_BYTE *pBuff=NULL;
        
        /* calc which _DataBlockHDR we need to load that contains the position of seekpointer */
        DRM_DWORD nthBlock = pSlotCtx->dwSeekPointer / pSlotCtx->pNS->pHDS->nImagesize_DataBlockPayload;

        /* get first data block # */
        if ( pSlotCtx->dwFirstDataBlockNum == 0 )
        {
            _CommBlockHDR *pChild = pSlotCtx->pCurrChildBlock;

            ChkDR(_HdsGetPutBlockPayload((_CommBlockHDR*)pChild, 
                                         pSlotCtx->dwSlotPosInBlock + SIZEOF(_SlotHeader), 
                                         SIZEOF(DRM_DWORD), 
                                         (DRM_BYTE*)&(pSlotCtx->dwFirstDataBlockNum), TRUE));
            FIX_ENDIAN_DWORD( pSlotCtx->dwFirstDataBlockNum );

            /* load the first _DataBlockHDR */
            ChkDR(_HdsLoadBlockHDR(pSlotCtx->pNS, 
                                   pSlotCtx->dwFirstDataBlockNum, 
                                   &pSlotCtx->pCurrDataBlock));
            pSlotCtx->dwNumofDataBlockVisited = 0;
        }

        /* load the appropriate data block */
        if ( nthBlock > pSlotCtx->dwNumofDataBlockVisited )
        {
            while ( nthBlock > pSlotCtx->dwNumofDataBlockVisited )
            {
                nNextBlockNum = ((_DataBlockHDR*)pSlotCtx->pCurrDataBlock)->File._image.nCascadingBlockNum;
                ChkDR(_HdsLoadBlockHDR(pSlotCtx->pNS, nNextBlockNum, &pSlotCtx->pCurrDataBlock));
                ++pSlotCtx->dwNumofDataBlockVisited;
            }
        }
        else if ( nthBlock < pSlotCtx->dwNumofDataBlockVisited )
        {
            /* start from the first data block in the chain */
            nNextBlockNum = pSlotCtx->dwFirstDataBlockNum;            
            ChkDR(_HdsLoadBlockHDR(pSlotCtx->pNS, nNextBlockNum, &pSlotCtx->pCurrDataBlock));
            pSlotCtx->dwNumofDataBlockVisited = 0;

            while (pSlotCtx->dwNumofDataBlockVisited < nthBlock )
            {
                nNextBlockNum = ((_DataBlockHDR*)(pSlotCtx->pCurrDataBlock))->File._image.nCascadingBlockNum;
                ChkDR(_HdsLoadBlockHDR(pSlotCtx->pNS, nNextBlockNum, &pSlotCtx->pCurrDataBlock));
                ++pSlotCtx->dwNumofDataBlockVisited;
            }
        }

        /* calc # of bytes to read/write */
        if ( (pSlotCtx->dwSeekPointer + cbData) > pSlotCtx->oSlotHeader.dwSlotSize )
        {
            cbIO = pSlotCtx->oSlotHeader.dwSlotSize - pSlotCtx->dwSeekPointer;
        }
        else
        {
            cbIO = cbData;
        }

        /* calc relative pos of dwSeekPointer in this _DataBlockHDR */
        nRelativeSeekPtr = pSlotCtx->dwSeekPointer - (nthBlock * pSlotCtx->pNS->pHDS->nImagesize_DataBlockPayload);
        pBuff = pbData;
        nBytesLeft=cbIO;

        /* read/write slot content to buffer */
        ChkDR(_HdsGetPutBlockPayload(pSlotCtx->pCurrDataBlock, nRelativeSeekPtr, 
            min(nBytesLeft, pSlotCtx->pNS->pHDS->nImagesize_DataBlockPayload - nRelativeSeekPtr), pBuff, fGet));
        pBuff      += __CB_DECL(min(nBytesLeft, pSlotCtx->pNS->pHDS->nImagesize_DataBlockPayload - nRelativeSeekPtr));
        nBytesLeft -=           min(nBytesLeft, pSlotCtx->pNS->pHDS->nImagesize_DataBlockPayload - nRelativeSeekPtr);

        /* more data is contained along the _DataBlockHDR's chain */
        while (nBytesLeft > 0)
        {
            nNextBlockNum = ((_DataBlockHDR*)pSlotCtx->pCurrDataBlock)->File._image.nCascadingBlockNum;
            ChkDR(_HdsLoadBlockHDR(pSlotCtx->pNS, nNextBlockNum, &pSlotCtx->pCurrDataBlock));
            ++pSlotCtx->dwNumofDataBlockVisited;
            ChkDR(_HdsGetPutBlockPayload(pSlotCtx->pCurrDataBlock, 0, 
                min(nBytesLeft, pSlotCtx->pNS->pHDS->nImagesize_DataBlockPayload), pBuff, fGet));

            /* check if we are done */
            if ( nBytesLeft <= pSlotCtx->pNS->pHDS->nImagesize_DataBlockPayload )
            {
                break;
            }

            pBuff      += __CB_DECL(pSlotCtx->pNS->pHDS->nImagesize_DataBlockPayload);
            nBytesLeft -=           pSlotCtx->pNS->pHDS->nImagesize_DataBlockPayload;
        }
    }

    pSlotCtx->dwSeekPointer += cbIO;
	if ( pcbIO )
    {
		*pcbIO = cbIO;
    }

ErrorExit:
    return dr;
}

#define ADJUST_PAYLOAD_TO_END   0xFFFFFFFF

/*
** Adjust content of Child payload from SrcPos to DestPos,
** _ChildBlockHDR's nFreeMem is adjusted appropriately.
*/
static DRM_RESULT _HdsAdjustChildPayload(
    _CommBlockHDR *f_pblockheader, 
    DRM_DWORD      f_oFileDest, 
    DRM_DWORD      f_oFileSrc,
    DRM_DWORD      f_cbMove)
{
    DRM_RESULT      dr        = DRM_SUCCESS;
    DRM_DWORD       oFileCurr = 0;
    DRM_DWORD       cbMove    = 0;
    DRM_DWORD       cb        = 0;
    DRM_BYTE       *pbBuffer  = NULL;
    _ChildBlockHDR *pblockheaderChild = (_ChildBlockHDR *) f_pblockheader;

    DRMASSERT(f_pblockheader!=NULL);

    /* nothing to do */

    if (f_oFileDest == f_oFileSrc)
    {
        goto ErrorExit;
    }

    ChkDR(_Hds_malloc(f_pblockheader->pNS->pHDS, COPYBUFF_SIZE, (DRM_VOID **) &pbBuffer));

    if (f_cbMove == ADJUST_PAYLOAD_TO_END)
    {
        cbMove = pblockheaderChild->nPayloadSize 
               - pblockheaderChild->File._image.nFreeMem 
               - f_oFileSrc;
    }
    else
    {
        cbMove = f_cbMove;
    }

    if (f_oFileSrc > f_oFileDest)   /* Shrinking the payload */
    {
        /* copy it */
        for (oFileCurr  = 0; 
             oFileCurr  < cbMove;
             oFileCurr += min(cbMove - oFileCurr, COPYBUFF_SIZE))
        {
            cb = min(cbMove - oFileCurr, COPYBUFF_SIZE);

            ChkDR(_HdsGetPutBlockPayload(f_pblockheader, 
                                         oFileCurr + f_oFileSrc,  
                                         cb, 
                                         pbBuffer, 
                                         GPBP_OPERATION_READ));

            ChkDR(_HdsGetPutBlockPayload(f_pblockheader, 
                                         oFileCurr + f_oFileDest, 
                                         cb, 
                                         pbBuffer, 
                                         GPBP_OPERATION_WRITE));
        }

        /* wipe the space left behind */

        if (f_cbMove == ADJUST_PAYLOAD_TO_END)
        {
            DRM_DWORD oFileWipeEnd = pblockheaderChild->nPayloadSize
                                   - pblockheaderChild->File._image.nFreeMem;

            ZEROMEM(pbBuffer, COPYBUFF_SIZE);

            for (oFileCurr = f_oFileDest + cbMove; 
                 oFileCurr < oFileWipeEnd;
                 oFileCurr += min(oFileWipeEnd - oFileCurr, COPYBUFF_SIZE))
            {
                ChkDR(_HdsGetPutBlockPayload(f_pblockheader, oFileCurr, min(oFileWipeEnd-oFileCurr, COPYBUFF_SIZE), pbBuffer, FALSE));
            }

            /* update nFreeMem in block .. only valid if we are moving the tail*/

            pblockheaderChild->File._image.nFreeMem += f_oFileSrc - f_oFileDest;
        }
    }
    else    /* enlarging the payload (wSrcPos < wDestPos) */
    {
        cb = 0;

        ChkArg(f_cbMove != ADJUST_PAYLOAD_TO_END);

        if (cbMove > (pblockheaderChild->nPayloadSize - f_oFileDest))
        {
            TRACE(("_ChildBlockHDR does not have enough room for the move.\n"));
            DRMASSERT(FALSE);
        }

        /* copy from the end */
        for (oFileCurr  = cbMove; 
             oFileCurr  > 0;
             oFileCurr -= cb)
        {

            cb = min(oFileCurr, COPYBUFF_SIZE);

            ChkDR(_HdsGetPutBlockPayload(f_pblockheader, 
                                         oFileCurr + f_oFileSrc - cb,  
                                         cb, 
                                         pbBuffer, 
                                         GPBP_OPERATION_READ));

            ChkDR(_HdsGetPutBlockPayload(f_pblockheader,
                                         oFileCurr + f_oFileDest - cb,
                                         cb,
                                         pbBuffer,
                                         GPBP_OPERATION_WRITE));
        }

        /* wipe the enlarged area of the payload */
        cb = f_oFileDest - f_oFileSrc;

        ZEROMEM(pbBuffer, COPYBUFF_SIZE);

        for (oFileCurr = 0; 
             oFileCurr < cb;
             oFileCurr += min(cb - oFileCurr, COPYBUFF_SIZE))
        {
            ChkDR(_HdsGetPutBlockPayload(f_pblockheader, 
                                         oFileCurr + f_oFileSrc, 
                                         min(cb - oFileCurr, COPYBUFF_SIZE), 
                                         pbBuffer, 
                                         GPBP_OPERATION_WRITE));
        }

        /* update nFreeMem in block */
        if (pblockheaderChild->File._image.nFreeMem > (f_oFileDest - f_oFileSrc))
        {
            pblockheaderChild->File._image.nFreeMem -= f_oFileDest - f_oFileSrc;
        }
        else
        {
            pblockheaderChild->File._image.nFreeMem = 0;
        }
    }

ErrorExit:
    if (pbBuffer != NULL)
    {
        _Hds_free(f_pblockheader->pNS->pHDS, pbBuffer);
    }    
    return dr;
}

/**********************************************************************
** Function:    FindLeftmostLeafBlock
** Synopsis:    find the leftmost block which does any children blocks
** Returns:     DRM_SUCCESS on success
** Notes:       
***********************************************************************
*/
static DRM_RESULT
_HdsFindLeftmostLeafBlock(
    IN _CommBlockHDR *pRefBlock,
    OUT DRM_DWORD *pnLeafBlock)
{
    DRM_RESULT dr=DRM_SUCCESS;
    DRM_DWORD nChildBlkNum=0;
    DRM_DWORD nIndex=0;
    _CommBlockHDR *pTmpBlock=NULL;
    _CommBlockHDR *pCurrBlock=NULL;
    _NsContext *pNS=NULL;

    DRMASSERT(pRefBlock && pnLeafBlock);
    
    pNS = pRefBlock->pNS;
    *pnLeafBlock = pRefBlock->nBlockNum;  /* init to itself first */
    ChkDR(_HdsAllocBlockBuffer(pNS, eCHILDBLOCK, &pTmpBlock));
    pCurrBlock = pRefBlock;

    /* for loop for each level of the tree */
    while (TRUE)
    {
        /* find the first occupied slot at this level */
        for (nIndex=0; nIndex < pCurrBlock->pNS->wMaxNumChildren; nIndex++)
        {
            ChkDR(_HdsGetPutChildBlockNum(pNS, ((_ChildBlockHDR*)pCurrBlock)->File._image.bChildAllocTable,
                nIndex, &nChildBlkNum, TRUE));
            if ( nChildBlkNum != 0 )  /* child node exists */
            {
                if ( pCurrBlock == pRefBlock )
                {
                    pCurrBlock = pTmpBlock;
                }

                /* load the child block */
                ChkDR(_HdsLoadBlockHDR(pNS, nChildBlkNum, &pCurrBlock));
                *pnLeafBlock = pCurrBlock->nBlockNum;
                break;
            }
        }

        /* no occupied slot found in this level, that means this block is a leaf block */
        if ( nIndex >= pCurrBlock->pNS->wMaxNumChildren )
        {
            break;
        }
    }
    
ErrorExit:
    if ( pTmpBlock )
    {
        _Hds_free(pNS->pHDS, pTmpBlock);
    }

    return dr;
}

/*
**
*/
static DRM_RESULT _HdsCopySlot_Child2Child(
    IN _CommBlockHDR *pDestBlock, 
    IN DRM_DWORD dwPosInDestBlock,
    IN _CommBlockHDR *pSourceBlock, 
    IN DRM_DWORD dwPosInSrcBlock, 
    IN DRM_DWORD dwSize)
{
    DRM_RESULT dr=DRM_SUCCESS;
    _NsContext *pNS=NULL;
    DRM_DWORD nIndex;
    DRM_BYTE *pCopyBuffer=NULL;

    DRMASSERT(pDestBlock && pSourceBlock
        && GET_BYTE( &pDestBlock->File._image.bBlockType, 0 )   == (DRM_BYTE)eCHILDBLOCK
        && GET_BYTE( &pSourceBlock->File._image.bBlockType, 0 ) == (DRM_BYTE)eCHILDBLOCK);

    pNS = pSourceBlock->pNS;

    ChkDR(_Hds_malloc(pNS->pHDS, COPYBUFF_SIZE, (DRM_VOID**)&pCopyBuffer));
    for (nIndex=0; nIndex < dwSize; ) 
    {
        ChkDR(_HdsGetPutBlockPayload(pSourceBlock, nIndex+dwPosInSrcBlock,  min(COPYBUFF_SIZE, dwSize-nIndex), pCopyBuffer, GPBP_OPERATION_READ));
        ChkDR(_HdsGetPutBlockPayload(pDestBlock,   nIndex+dwPosInDestBlock, min(COPYBUFF_SIZE, dwSize-nIndex), pCopyBuffer, GPBP_OPERATION_WRITE));

        nIndex += min(COPYBUFF_SIZE, dwSize-nIndex);
    }

ErrorExit:
    if ( pCopyBuffer )
    {
        _Hds_free(pNS->pHDS, pCopyBuffer);
    }
    return dr;
}

/*
** Copy the payload of the source child block to the destination child block 
*/
static DRM_RESULT _HdsCopyChildPayload(
    OUT _CommBlockHDR *pDestBlock,
    IN _CommBlockHDR *pSourceBlock)
{
    DRM_RESULT dr=DRM_SUCCESS;
    DRM_DWORD nBytesToSkip=0;
    DRM_DWORD nNextSlotPos=0, nBytesToSearch=0;
    _SlotHeader oHeader;
    _CommBlockHDR *pDataBlock=NULL;
    
    DRMASSERT(pDestBlock && pSourceBlock);

    /* copy payload */
    ChkDR(_HdsCopySlot_Child2Child(pDestBlock, 0, pSourceBlock, 0, 
        pDestBlock->pNS->nImagesize_ChildBlockPayload));

    /* update freemem */    
    ((_ChildBlockHDR*)pDestBlock)->File._image.nFreeMem = ((_ChildBlockHDR*)pSourceBlock)->File._image.nFreeMem;

    /* walk each slot in the pDestBlock, adopt, if any, all data blocks to pDestBlock */
    nBytesToSearch = pDestBlock->pNS->nImagesize_ChildBlockPayload - ((_ChildBlockHDR*)pDestBlock)->File._image.nFreeMem;
    if ( nBytesToSearch == 0 )
    {
        goto ErrorExit;
    }
    
    /* walk the payload of child node to find matching HashKey and UniqueKey. Each license are stored 
    ** one by one in the payload ... 
    */
    ChkDR(_HdsAllocBlockBuffer(pSourceBlock->pNS, eDATABLOCK, &pDataBlock));
    for (; nNextSlotPos<nBytesToSearch; nNextSlotPos+=nBytesToSkip)
    {
        /* read the _SlotHeader structure */
        ChkDR(_HdsGetPutBlockPayload(pDestBlock, nNextSlotPos, 
            SIZEOF(_SlotHeader), (DRM_BYTE*)&oHeader, GPBP_OPERATION_READ));
        FIX_ENDIAN_DWORD( oHeader.dwSlotSize );
        FIX_ENDIAN_DWORD( oHeader.dwAttributes );

        /* skip this license if not oversized */
        if ( !ISOVERSIZESLOT(oHeader.dwSlotSize, pDestBlock->pNS) )
        {
            nBytesToSkip = SIZEOF(_SlotHeader) + oHeader.dwSlotSize;
        }
        else
        {
            /* load the data block, update its parent block # to pDestBlock's block # */
            DRM_DWORD dwDataBlockNum=0;
            ChkDR(_HdsGetPutBlockPayload(pDestBlock, nNextSlotPos+SIZEOF(_SlotHeader), 
                SIZEOF(DRM_DWORD), (DRM_BYTE*)&dwDataBlockNum, GPBP_OPERATION_READ));
            FIX_ENDIAN_DWORD( dwDataBlockNum );
            ChkDR(_HdsLoadBlockHDR(pSourceBlock->pNS, dwDataBlockNum, &pDataBlock));

            /* update parent block # */
            pDataBlock->File._image.nParentBlockNum = pDestBlock->nBlockNum;
            ChkDR(_HdsWriteBlockHDR(pDataBlock));

            nBytesToSkip = SIZEOF(_SlotHeader)+SIZEOF(DRM_DWORD);
        }
    }

ErrorExit:
    if (pDataBlock)
    {
        _Hds_free(pSourceBlock->pNS->pHDS, pDataBlock);
    }

    return dr;
}


/**********************************************************************
** Function:    _HdsDefragmentFile
** Synopsis:    Defragment the file from the given block
** Arguments:   [pRefBlock] -- 
** Returns:     DRM_SUCCESS on success
** Notes:       We cannot simply free a block even if it's empty. If we do so, the
**              structure of its subtree will be modified and all of the licenses stored
**              in this subtree will no longer be able to be retrieved. Instead, we 
**              traverse to the leftmost leaf block of this subtree and move its 
**              content to the given block. Then we can free that emptied leaf
**              block. This way, since the leaf block does not have any children,
**              its subtree can be freed.
***********************************************************************
*/
static DRM_RESULT
_HdsDefragmentFile(
    IN _CommBlockHDR *pRefBlock,
    OUT DRM_BOOL *pfRefBlockFreed)
{
    DRM_RESULT dr=DRM_SUCCESS;
    DRM_DWORD nParentBlock=0;
    DRM_DWORD nLeafBlock=0;
    DRM_DWORD nIndex=0;
    _CommBlockHDR *pLeafBlock=NULL;

    DRMASSERT(pRefBlock && pRefBlock->pNS && pRefBlock->pNS->fInited);

    if ( ((_ChildBlockHDR*)pRefBlock)->File._image.nFreeMem < (pRefBlock->pNS->nImagesize_ChildBlockPayload) )
    {
        goto ErrorExit;     /* this block is not empty! */
    }

    /* find the leftmost leaf node in the 'hash path' */
    ChkDR(_HdsAllocBlockBuffer(pRefBlock->pNS, eCHILDBLOCK, &pLeafBlock));
    ChkDR(_HdsFindLeftmostLeafBlock(pRefBlock, &nLeafBlock));

    if ( pfRefBlockFreed )
    {
        *pfRefBlockFreed = FALSE;
    }

    /* ref block is a leaf */
    if ( nLeafBlock == pRefBlock->nBlockNum )  
    {
        if ( pRefBlock->File._image.nParentBlockNum == ROOTPARENTNUM )
        {
            goto ErrorExit;     /* This is the root node of namespace, leave it */
        }

        nParentBlock = pRefBlock->File._image.nParentBlockNum;
        _HdsFreeBlock(pRefBlock);
        if ( pfRefBlockFreed )
        {
            *pfRefBlockFreed = TRUE;
        }
        goto UpdateParentBlock;
    }

    ChkDR(_HdsLoadBlockHDR(pRefBlock->pNS, nLeafBlock, &pLeafBlock)); /* load the leaf block */
    ChkDR(_HdsCopyChildPayload(pRefBlock, pLeafBlock));    /* copy its content to the ref block */
    ChkDR(_HdsWriteBlockHDR(pRefBlock));  /* update ref block */

    /* get the parent block num of the leaf block and free the leaf block */
    nParentBlock = pLeafBlock->File._image.nParentBlockNum;
    _HdsFreeBlock(pLeafBlock);  /* delete the ref block in file */

UpdateParentBlock:

    /* update the leaf block's parent's alloctable so that it won't point to the leaf node */
    _HdsInitBlockBuffer(pRefBlock->pNS, pLeafBlock, 0, eCHILDBLOCK);
    ChkDR(_HdsLoadBlockHDR(pRefBlock->pNS, nParentBlock, &pLeafBlock));

    /* walk the allocTable to find the slot contains the leaf block # */
    for (nIndex=0; nIndex<pLeafBlock->pNS->wMaxNumChildren; nIndex++)
    {
        DRM_DWORD nChildBlkNum;
        ChkDR(_HdsGetPutChildBlockNum(pRefBlock->pNS, ((_ChildBlockHDR*)pLeafBlock)->File._image.bChildAllocTable,
            nIndex, &nChildBlkNum, TRUE));
        if ( nChildBlkNum == nLeafBlock )
        {
            nChildBlkNum = 0;
            ChkDR(_HdsGetPutChildBlockNum(pRefBlock->pNS, ((_ChildBlockHDR*)pLeafBlock)->File._image.bChildAllocTable,
                nIndex, &nChildBlkNum, FALSE));

            ChkDR(_HdsWriteBlockHDR(pLeafBlock));
            goto ErrorExit;
        }
    }

    ChkDR(DRM_E_HDSFILECORRUPTED);     /* something must be wrong */

ErrorExit:        
    _Hds_free(pRefBlock->pNS->pHDS, pLeafBlock);
    return dr;
}

/*
**
*/
static DRM_RESULT _HdsSlotRead( 
    IN  _SlotContext *pSlotCtx,
    IN  DRM_DWORD      cbData,
    OUT DRM_BYTE     *pbData,
    OUT DRM_DWORD    *pcbRead)
{
    DRM_RESULT dr=DRM_SUCCESS;
    DRM_BOOL fSlotCtxIsValid=FALSE;

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

    ChkDR(_HdsVerifySlotContext(pSlotCtx, &fSlotCtxIsValid));
    if (pSlotCtx->eStatus!=eSlotCtxReady || !fSlotCtxIsValid)
    {
        ChkDR(DRM_E_HDSSLOTNOTFOUND);
    }
    ChkDR(_HdsReadWriteSlot(pSlotCtx, cbData, pbData, pcbRead, TRUE));

ErrorExit:

    return dr;
}


/*
**
*/
static DRM_RESULT _HdsSlotWrite( 
    IN _SlotContext   *pSlotCtx,
    IN DRM_DWORD        cbData,
    IN const DRM_BYTE *pbData,
    OUT DRM_DWORD      *pcbWritten)
{
    DRM_RESULT dr=DRM_SUCCESS;
    DRM_BOOL fSlotCtxIsValid=FALSE;

    ChkArg(ISVALIDCONTEXT(pSlotCtx, eSlotContextSignature));
    DRMASSERT(pSlotCtx && pSlotCtx->pNS && pSlotCtx->pNS->pHDS 
        && pSlotCtx->pNS->pHDS->fp!=OEM_INVALID_HANDLE_VALUE );
    
    ChkDR(_HdsVerifySlotContext(pSlotCtx, &fSlotCtxIsValid));
    if (pSlotCtx->eStatus!=eSlotCtxReady || !fSlotCtxIsValid)
    {
        ChkDR(DRM_E_HDSSLOTNOTFOUND);
    }

    /* slot must be locked EXCLUSIVE */
    if ( (pSlotCtx->eLockMode & eDRM_HDS_LOCKEXCLUSIVE) == 0 )
    {
        ChkDR(DRM_E_HDSNOTLOCKEDEXCLUSIVE);
    }

    /* 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));

ErrorExit:
    return dr;
}


/**********************************************************************
** Function:    _HdsRemoveSlot
** Synopsis:    Remove the slot from the block. If it is oversized, the
**              associated _DataBlocks are also removed.
** Arguments:   [pSlotCtx] -- 
**              [fPermanent] -- TRUE if to remove the slot permanently,
**                              FALSE if just to set it as 'hidden'
** Returns:     DRM_SUCCESS on success
** Notes:       It is the callers responsibility to lock the block for
**              EXCLUSIVE.
***********************************************************************
*/
static DRM_RESULT _HdsRemoveSlot( 
    _SlotContext    *pSlotCtx,
    _ERemoveSlotMode eMode)
{
    DRM_RESULT dr=DRM_SUCCESS;
    DRM_DWORD   wDestPos=pSlotCtx->dwSlotPosInBlock; 
    DRM_DWORD   wSrcPos=0;

    if ( eMode == eRemoveSlotPermanent )
    {
        /* remove _DataBlockHDR's, if necessary */
        if ( !ISOVERSIZESLOT(pSlotCtx->oSlotHeader.dwSlotSize, pSlotCtx->pNS) )
        {
            /* lock the associated block */
            wSrcPos = pSlotCtx->dwSlotPosInBlock + SIZEOF(_SlotHeader) + 
                pSlotCtx->oSlotHeader.dwSlotSize;
        }
        else
        {
            DRM_DWORD nDataBlockNum = pSlotCtx->dwFirstDataBlockNum;

            wSrcPos = pSlotCtx->dwSlotPosInBlock + SIZEOF(_SlotHeader) + SIZEOF(DRM_DWORD);
            while (nDataBlockNum > 0)
            {
                ChkDR(_HdsLoadBlockHDR(pSlotCtx->pNS, nDataBlockNum, &pSlotCtx->pCurrDataBlock));
                nDataBlockNum = ((_DataBlockHDR*)(pSlotCtx->pCurrDataBlock))->File._image.nCascadingBlockNum;
                ChkDR(_HdsFreeBlock(pSlotCtx->pCurrDataBlock));
            }
        }

        /* remove slot in _ChildBlockHDR */
        ChkDR(_HdsAdjustChildPayload(pSlotCtx->pCurrChildBlock, wDestPos, wSrcPos, ADJUST_PAYLOAD_TO_END));
        ChkDR(_HdsWriteBlockHDR(pSlotCtx->pCurrChildBlock));
    }
    else if ( eMode == eRemoveSlotSetNull )
    {
        if ( !ISOVERSIZESLOT(pSlotCtx->oSlotHeader.dwSlotSize, pSlotCtx->pNS) )
        {
            /* trash content of slot to NULL */
            DRM_BYTE *pbCopyBuffer=NULL;
            DRM_DWORD  cbWrite=0, cbWritten=0;
            
            dr = _Hds_malloc(pSlotCtx->pNS->pHDS, COPYBUFF_SIZE, (DRM_VOID**)&pbCopyBuffer);
            if ( DRM_SUCCEEDED(dr) )
            {
                pSlotCtx->dwSeekPointer=0;
                while (pSlotCtx->dwSeekPointer < pSlotCtx->oSlotHeader.dwSlotSize )
                {
                    cbWrite = min(COPYBUFF_SIZE, pSlotCtx->oSlotHeader.dwSlotSize-pSlotCtx->dwSeekPointer);
                    dr = _HdsSlotWrite(pSlotCtx, cbWrite, pbCopyBuffer, &cbWritten);
                    if ( DRM_FAILED(dr) )
                    {
                        break;
                    }
                    else if ( cbWrite != cbWritten )
                    {
                        dr = DRM_E_FILEWRITEERROR;
                        break;
                    }
                }

                _Hds_free(pSlotCtx->pNS->pHDS, pbCopyBuffer);
            }
        }
        else
        {
            /* delete all _DataBlock and set Datablock number to 0 in slot */
            DRM_DWORD nDataBlockNum = pSlotCtx->dwFirstDataBlockNum;

            /*wSrcPos = pSlotCtx->dwSlotPosInBlock + SIZEOF(_SlotHeader) + SIZEOF(DRM_DWORD);*/
            while (nDataBlockNum > 0)
            {
                ChkDR(_HdsLoadBlockHDR(pSlotCtx->pNS, nDataBlockNum, &pSlotCtx->pCurrDataBlock));
                nDataBlockNum = ((_DataBlockHDR*)(pSlotCtx->pCurrDataBlock))->File._image.nCascadingBlockNum;
                ChkDR(_HdsFreeBlock(pSlotCtx->pCurrDataBlock));
            }

            /* make sure the flag is hidden */
            pSlotCtx->oSlotHeader.dwAttributes |= eSlotIsHidden;
            FIX_ENDIAN_DWORD( pSlotCtx->oSlotHeader.dwAttributes );
            FIX_ENDIAN_DWORD( pSlotCtx->oSlotHeader.dwSlotSize );
            dr = _HdsGetPutBlockPayload(pSlotCtx->pCurrChildBlock, pSlotCtx->dwSlotPosInBlock,
                SIZEOF(_SlotHeader), &pSlotCtx->oSlotHeader, GPBP_OPERATION_WRITE);
            FIX_ENDIAN_DWORD( pSlotCtx->oSlotHeader.dwAttributes );
            FIX_ENDIAN_DWORD( pSlotCtx->oSlotHeader.dwSlotSize );

            /* update _DataBlock number in Slot */
            pSlotCtx->dwFirstDataBlockNum = 0;
            dr = _HdsGetPutBlockPayload(pSlotCtx->pCurrChildBlock, pSlotCtx->dwSlotPosInBlock+SIZEOF(_SlotHeader),
                SIZEOF(DRM_DWORD), &pSlotCtx->dwFirstDataBlockNum, GPBP_OPERATION_WRITE);
        }
    }
    else if ( eMode == eRemoveSlotSetHidden )
    {
        /* set the flag to hidden */
        pSlotCtx->oSlotHeader.dwAttributes |= eSlotIsHidden;
        FIX_ENDIAN_DWORD( pSlotCtx->oSlotHeader.dwAttributes );
        FIX_ENDIAN_DWORD( pSlotCtx->oSlotHeader.dwSlotSize );
        dr = _HdsGetPutBlockPayload(pSlotCtx->pCurrChildBlock, pSlotCtx->dwSlotPosInBlock,
            SIZEOF(_SlotHeader), &pSlotCtx->oSlotHeader, GPBP_OPERATION_WRITE);
        FIX_ENDIAN_DWORD( pSlotCtx->oSlotHeader.dwAttributes );
        FIX_ENDIAN_DWORD( pSlotCtx->oSlotHeader.dwSlotSize );
    }
    
ErrorExit:

    return dr;
}

/*
** Duplicate the given Slot Context structure. 
** Caller is responsible to _PrivateStackFree the resulting duplicate if buffer 
** is not pre-allocated.
*/
static DRM_RESULT
_HdsDupSlotContext(
    IN const _SlotContext *pSrcContext,
    OUT _SlotContext **ppDstContext)
{
    DRM_RESULT dr=DRM_SUCCESS;
    _SlotContext *pDupCtx=NULL;

    DRMASSERT(pSrcContext && ppDstContext);
    if ( *ppDstContext==NULL )
    {
        ChkDR(_Hds_malloc(pSrcContext->pNS->pHDS, pSrcContext->dwContextSize, (DRM_VOID**)&pDupCtx));
    }
    else
    {
        pDupCtx = *ppDstContext;
        ZEROMEM(pDupCtx, pSrcContext->dwContextSize);
    }

    MEMCPY(pDupCtx, pSrcContext, pSrcContext->dwContextSize);
    pDupCtx->pCurrChildBlock = (_CommBlockHDR *)pDupCtx->bBuff;
    if ( pSrcContext->pCurrDataBlock )
    {
        pDupCtx->pCurrDataBlock = (_CommBlockHDR *)(pDupCtx->bBuff + __CB_DECL(GetMemsize_ChildBlock(pSrcContext->pNS)));
    }
    
ErrorExit:
    if ( DRM_FAILED(dr) )
    {
        _Hds_free(pSrcContext->pNS->pHDS, pDupCtx);
    }
    else
    {
        *ppDstContext = pDupCtx;
    }
    
    return dr;
}


/*
** upon success, ppNewSlotCtx contains a new slot. Caller is responsible
** for freeing the buffer. Upon success, the new slot will have the same lock
** as the source slot.
*/
static DRM_RESULT _HdsCopyAndLockNewSlot(
    IN  _SlotContext  *pSlotCtx, 
    IN  DRM_DWORD      cbNewSize,
    OUT _SlotContext **ppNewSlotCtx)
{
    DRM_RESULT    dr=DRM_SUCCESS;
    DRM_DWORD     cbSlotContext=0;
    DRM_BYTE     *pbCopyBuffer=NULL;
    _HdsContext  *pHDS=NULL;
    _NsContext   *pNS=NULL;
    _SlotContext *pNewSlotCtx=NULL;
    _SlotHeader   oTmpHeader;
    DRM_DWORD     cbRead=0, cbWritten=0;

    DRMASSERT(pSlotCtx && ppNewSlotCtx);
    pHDS = pSlotCtx->pNS->pHDS;
    pNS  = pSlotCtx->pNS;

    /* alloc copy buffer & context for new slot */
    cbSlotContext = CALC_SLOTCONTEXTLEN(pNS);
    ChkDR(_Hds_malloc(pHDS, cbSlotContext, (DRM_VOID **)&pNewSlotCtx));
    ChkDR(_HdsInitSlotContext(pNS, (DRM_BYTE*)pNewSlotCtx, cbSlotContext));
    ChkDR(_Hds_malloc(pHDS, COPYBUFF_SIZE, (DRM_VOID**)&pbCopyBuffer));

    /* Copy key from original slot and create new slot with temp keys. 
    ** if this succeed, an EXCLUSIVE lock is issued for the new slot */
    MEMCPY(&oTmpHeader, &pSlotCtx->oSlotHeader, SIZEOF(_SlotHeader));
    ChkDR(_HdsCreateAndOpenSlot(
        pNewSlotCtx->pNS, 
        &oTmpHeader.oHashkey, 
        &oTmpHeader.oUniquekey, 
        TRUE, 
        cbNewSize, 
        pNewSlotCtx,
        pSlotCtx->eLockMode));

    /* copy content from original slot to new slot 
    ** BUGBUG: This copy process can be enhanced for oversized slot:
    ** it may be more efficient to simple detach the head of DataBlock list
    ** to the new slot. 
    */
    pSlotCtx->dwSeekPointer=0;
    pNewSlotCtx->dwSeekPointer=0;
    while (pNewSlotCtx->dwSeekPointer < min(cbNewSize, pSlotCtx->oSlotHeader.dwSlotSize))
    {
        dr = _HdsSlotRead(pSlotCtx, min(cbNewSize-pSlotCtx->dwSeekPointer, COPYBUFF_SIZE), pbCopyBuffer, &cbRead);
        if ( DRM_SUCCEEDED(dr) )    
        {
            dr = _HdsSlotWrite(pNewSlotCtx, cbRead, pbCopyBuffer, &cbWritten);
            if ( DRM_SUCCEEDED(dr) )    
            {
                if ( cbRead == cbWritten)
                {
                    continue;
                }
                else if (cbRead != COPYBUFF_SIZE)
                {
                    break;
                }
                else /* (cbRead != cbWritten) */
                {
                    dr = DRM_E_FILEWRITEERROR;
                } 
            }
        }
        break;
    }

    if (DRM_FAILED(dr))
    {
        _HdsUnlockSlot(pNewSlotCtx);  /* some error occured, unlock the slot */
    }
    
ErrorExit:
    _Hds_free(pHDS, pbCopyBuffer);
    if ( DRM_FAILED(dr) )
    {
        _Hds_free(pHDS, pNewSlotCtx);
    }
    else
    {
        *ppNewSlotCtx = pNewSlotCtx;
    }
    return dr;
}


/**********************************************************************
** Function:    _HdsRelocateSlotAndKeepCurrLock
** Synopsis:    Relocate a slot
** Arguments:   [pSlotCtx] -- Slot context of interest
**              [cbSize] -- new size
** Returns:     DRM_SUCCESS on success
** Notes:       The slot must be locked exclusive before calling
***********************************************************************
*/
static DRM_RESULT _HdsRelocateSlotAndKeepCurrLock( 
    IN _SlotContext *pSlotCtx, 
    IN DRM_DWORD     cbSize)
{
    DRM_RESULT    dr=DRM_SUCCESS;
    _SlotContext *pNewSlotCtx=NULL;
    _HdsContext  *pHDS=NULL;
    _NsContext   *pNS=NULL;

    DRMASSERT(pSlotCtx != NULL
          &&  pSlotCtx->pNS != NULL
          &&  pSlotCtx->pNS->pHDS != NULL
          &&  pSlotCtx->eStatus == eSlotCtxReady 
          &&  cbSize > 0
          &&  (pSlotCtx->eLockMode & eDRM_HDS_LOCKEXCLUSIVE) != 0 );

    pHDS = pSlotCtx->pNS->pHDS;
    pNS  = pSlotCtx->pNS;

    ChkDR(_HdsRemoveSlot(pSlotCtx, eRemoveSlotSetHidden));  /* set slot as 'hidden' */
    dr = _HdsCopyAndLockNewSlot(pSlotCtx, cbSize, &pNewSlotCtx);
    if ( DRM_SUCCEEDED(dr) )
    {
        /* BUGBUG: if we failed any where here, the new slot is left in file !! */
        DRM_BOOL fResult=FALSE;

        /* set old slot to null first */
        _HdsRemoveSlot(pSlotCtx, eRemoveSlotSetNull);

        /* remove old slot permanently if we can lock the block. It's okay to 
        ** leave it as 'hidden'. we can pick it up when doing store cleanup. */
        _HdsUnlockSlot(pSlotCtx);       /* igmore return values */
        _HdsUnlockSlot(pNewSlotCtx);
        if ( DRM_SUCCEEDED(_HdsLockBlock2DeleteSlot(
                pSlotCtx, 
                eDRM_HDS_LOCKEXCLUSIVE ) ) )
        {
            _HdsRemoveSlot(pSlotCtx, eRemoveSlotPermanent);
            _HdsUnlockBlock2DeleteSlot(pSlotCtx);
        }

        /* re-initialize the new slot. This is necessary because _HdsRemoveSlot()
        ** may move the location of the new slot 
        */
        ChkOverflowSLOTCONTEXTLEN( pNS );
        ChkDR(_HdsInitSlotContext(pNS, (DRM_BYTE*)pSlotCtx, CALC_SLOTCONTEXTLEN(pNS)));

        ChkDR(_HdsSearchSlotInFile(pNS, &pNewSlotCtx->oSlotHeader.oHashkey, 
            &pNewSlotCtx->oSlotHeader.oUniquekey, eSearchDataSlot, NULL, pSlotCtx, &fResult));
        DRMASSERT(fResult); /* this should not happen, something must be wrong */

        /* issue lock to the relocated slot */
        dr = _HdsLockSlot(pSlotCtx, eDRM_HDS_LOCKEXCLUSIVE | eDRM_HDS_LOCKWAIT);
    }
    else
    {
        /* try recovering ... try unhide the slot */
        pSlotCtx->oSlotHeader.dwAttributes &= ~eSlotIsHidden;

        /* we do not care if this success or not. that's all we can do ... */
        FIX_ENDIAN_DWORD( pSlotCtx->oSlotHeader.dwAttributes );
        FIX_ENDIAN_DWORD( pSlotCtx->oSlotHeader.dwSlotSize );
        _HdsGetPutBlockPayload(pSlotCtx->pCurrChildBlock, pSlotCtx->dwSlotPosInBlock,
            SIZEOF(_SlotHeader), &pSlotCtx->oSlotHeader, GPBP_OPERATION_WRITE);
        FIX_ENDIAN_DWORD( pSlotCtx->oSlotHeader.dwAttributes );
        FIX_ENDIAN_DWORD( pSlotCtx->oSlotHeader.dwSlotSize );
    }

ErrorExit:

    _Hds_free(pHDS, pNewSlotCtx);
    return dr;
}


/*
**
*/
static DRM_RESULT
_HdsDeleteSubTree(
    _NsContext *pNS,
    DRM_DWORD nSubtreeRootBlockNum)
{
    DRM_RESULT dr=DRM_SUCCESS;
    DRM_DWORD nIndex=0;
    _CommBlockHDR *pCurrBlock=NULL;
    DRM_DWORD nBlkNum=0;
    DRM_DWORD nParentBlkNum=0;

    ChkArg(ISVALIDCONTEXT(pNS, eCfgContextSignature));
    DRMASSERT(pNS && pNS->fInited);

    ChkDR(_HdsAllocBlockBuffer(pNS, eCHILDBLOCK, &pCurrBlock));
    nBlkNum = nSubtreeRootBlockNum;

UPLEVEL:

    _HdsInitBlockBuffer(pNS, pCurrBlock, 0, eCHILDBLOCK);
    ChkDR(_HdsLoadBlockHDR(pNS, nBlkNum, &pCurrBlock));
    if (nParentBlkNum == 0 )    /* this should be run the first time */
    {
        nParentBlkNum = pCurrBlock->nBlockNum;
        DRMASSERT(nParentBlkNum!=0);  /* the root block of any Config sub-tree should NOT have parent block num == 0 */
    }

DOWNLEVEL:
    
    for (nIndex=0; nIndex < pCurrBlock->pNS->wMaxNumChildren; nIndex++)
    {
        ChkDR(_HdsGetPutChildBlockNum(pNS, ((_ChildBlockHDR*)pCurrBlock)->File._image.bChildAllocTable,
            nIndex, &nBlkNum, TRUE));
        if ( nBlkNum != 0 )  /* child node exists */
        {
            /* update child alloc table */
            DRM_DWORD n=0;
            ChkDR(_HdsGetPutChildBlockNum(pNS, ((_ChildBlockHDR*)pCurrBlock)->File._image.bChildAllocTable,
                nIndex, &n, FALSE));
            ChkDR(_HdsWriteBlockHDR(pCurrBlock));
            
            /* load the child block */
            ChkDR(_HdsLoadBlockHDR(pNS, nBlkNum, &pCurrBlock));
            goto DOWNLEVEL;
        }
    }

    if ( pCurrBlock->nBlockNum == nParentBlkNum )   /* terminating condition */
    {
        ChkDR(_HdsFreeBlock(pCurrBlock));
        goto ErrorExit; /* this is the root */
    }

    nBlkNum = pCurrBlock->File._image.nParentBlockNum;
    ChkDR(_HdsFreeBlock(pCurrBlock));
    goto UPLEVEL;
    
ErrorExit:
    if ( pNS )
    {
        _Hds_free(pNS->pHDS, pCurrBlock);
    }

    return dr;
}

/*
** 
*/
static DRM_RESULT
_HdsTraverseNextRightSiblingBlock(    
    IN _NsContext *pNS,
    IN _CommBlockHDR * pCurrBlock, 
    OUT DRM_DWORD *pnNextSiblingBlkNum)    /* 0 if no more child can be found */
{
    DRM_RESULT dr=DRM_SUCCESS;
    _CommBlockHDR *pParentBlock=NULL;
    DRM_DWORD nIndex=0;

    DRMASSERT(pNS && pCurrBlock && pnNextSiblingBlkNum);
    *pnNextSiblingBlkNum = 0;  

    /* get ChildAllocTable of the parent node */
    if ( pCurrBlock->File._image.nParentBlockNum == ROOTPARENTNUM )
    {
        goto ErrorExit;     /* Currblock is the root, that's it */
    }

    ChkDR(_HdsAllocBlockBuffer(pNS, eCHILDBLOCK, &pParentBlock));
    ChkDR(_HdsLoadBlockHDR(pNS, pCurrBlock->File._image.nParentBlockNum, &pParentBlock));

    /* search index of the current child in parent's ChildAlloc table */
    for (nIndex=0; nIndex<(DRM_DWORD)pNS->wMaxNumChildren; nIndex++)
    {
        DRM_DWORD nChildNum;
        ChkDR(_HdsGetPutChildBlockNum(pNS, ((_ChildBlockHDR*)pParentBlock)->File._image.bChildAllocTable,
            nIndex, &nChildNum, TRUE));
        
        if ( nChildNum == pCurrBlock->nBlockNum ) /* found current child */
        {
            /* search for next child */
            ++nIndex;
            for (; nIndex<(DRM_DWORD)pNS->wMaxNumChildren; nIndex++)
            {
                ChkDR(_HdsGetPutChildBlockNum(pNS, ((_ChildBlockHDR*)pParentBlock)->File._image.bChildAllocTable,
                    nIndex, &nChildNum, TRUE));
                if ( nChildNum!= 0 )    /* found one */
                {
                    *pnNextSiblingBlkNum = nChildNum;
                    break;
                }
            }
            goto ErrorExit;
        }
    }

    /* cannot find current child from parent, this should never happen */
    ChkDR(DRM_E_HDSFILECORRUPTED);  /* internal error */

ErrorExit:        
    if (pParentBlock)
    {
        _Hds_free(pNS->pHDS, pParentBlock);
    }
    
    return dr;
}

/**********************************************************************
** Function:    _HdsTraverseBlocksInPostOrder
** Synopsis:    
** Arguments:   [pNS] -- 
**              [pCurrBlock] -- 
**              [pNextBlock] -- 
**              [pResult] 
** Returns:     DRM_SUCCESS on success
** Notes:       From the given current block, traverse the tree in POST ORDER fashion.
** Algorithm:
**    PostOrder(CFG, Current, Next)
**    {
**        if ( Current == NULL )
**            Next = CFG->Root;
**        else
**        {
**            Next = NextRightSibling(Current);
**            if ( Next == NULL )
**            {
**                Next = Current->Parent;
**                if ( Next == NULL )
**                    return FALSE;  // that's it
**                else
**                    return TRUE;
**            }
**        }
**        Next = LeftmostLeaf(Next);
**        return TRUE;
**    }
**
***********************************************************************
*/
static DRM_RESULT
_HdsTraverseBlocksInPostOrder(
    IN _NsContext *pNS,
    IN _CommBlockHDR *pCurrBlock,
    OUT _CommBlockHDR *pNextBlock,
    OUT DRM_BOOL *pResult)
{
    DRM_RESULT dr=DRM_SUCCESS;
    DRM_DWORD nBlockNum=0;
    
    DRMASSERT( pNS && pNextBlock && pResult);
    *pResult = FALSE;

    if ( pCurrBlock == NULL )   /* start from Root of current Namespace */
    {
        nBlockNum = pNS->nCfgRootBlockNum;
    }
    else
    {
        ChkDR(_HdsTraverseNextRightSiblingBlock(pNS, pCurrBlock, &nBlockNum));
        if ( nBlockNum == 0 )   /* no more sibling found */
        {
            /* no more sibling found, load the parent, if any, and return 
            ** if no more sibling found and the parent is root, that's it 
            */
            if ( pCurrBlock->File._image.nParentBlockNum == ROOTPARENTNUM )
            {
                *pResult = FALSE;   /* current block is the root */
            }
            else
            {   /* load parent block and return */
                ChkDR(_HdsLoadBlockHDR(pNS, pCurrBlock->File._image.nParentBlockNum, 
                    &pNextBlock));
                *pResult = TRUE;
            }

            goto ErrorExit;
        }
    }

    ChkDR(_HdsLoadBlockHDR(pNS, nBlockNum, &pNextBlock));
    ChkDR(_HdsFindLeftmostLeafBlock(pNextBlock, &nBlockNum));
    if ( nBlockNum != pNextBlock->nBlockNum )
        ChkDR(_HdsLoadBlockHDR(pNS, nBlockNum, &pNextBlock));

    *pResult = TRUE;
    
ErrorExit:
    return dr;
}

/*
** Get/Put the "attributes" field in the header of the specific slot
*/
static DRM_RESULT _HdsGetPutSlotAttributes( 
    IN _NsContext              *pNS,
    IN const DRM_HDS_HASHKEY   *pHashKey, 
    IN const DRM_HDS_UNIQUEKEY *pUniqueKey,
    IN DRM_BOOL                 fIsDataSlot,  
    IN OUT DRM_UINT            *pfAttributes,
    IN DRM_BOOL                 fGet)
{
    DRM_RESULT dr=DRM_SUCCESS;
    _SlotContext *pSlotCtx=NULL;
    DRM_BOOL fResult=FALSE;
    DRM_DWORD cbSlotContext=0;

    ChkArg(ISVALIDCONTEXT(pNS, eCfgContextSignature));
    DRMASSERT(pNS->fInited && pNS->pHDS && pNS->pHDS->fp!=OEM_INVALID_HANDLE_VALUE
        && pHashKey && pUniqueKey && pfAttributes);

    DRMASSERT(pNS->nCfgRootBlockNum>0);

    /* locate and open the slot */
    cbSlotContext = CALC_SLOTCONTEXTLEN(pNS);
    ChkDR( _Hds_malloc(pNS->pHDS, cbSlotContext, (DRM_VOID **)&pSlotCtx) );
    ChkDR( _HdsInitSlotContext(pNS, (DRM_BYTE*)pSlotCtx, cbSlotContext) );
    ChkDR( _HdsSearchSlotInFile(pNS, pHashKey, pUniqueKey, 
        fIsDataSlot? eSearchDataSlot : eSearchNamespace, NULL, 
        pSlotCtx, &fResult) );
    if ( !fResult )
    {
        ChkDR(DRM_E_HDSSLOTNOTFOUND);
    }

    if ( fGet )
    {
        ChkDR(_HdsGetPutBlockPayload(pSlotCtx->pCurrChildBlock, pSlotCtx->dwSlotPosInBlock, 
            SIZEOF(_SlotHeader), &pSlotCtx->oSlotHeader, GPBP_OPERATION_READ));
        FIX_ENDIAN_DWORD( pSlotCtx->oSlotHeader.dwAttributes );
        FIX_ENDIAN_DWORD( pSlotCtx->oSlotHeader.dwSlotSize );
        *pfAttributes = pSlotCtx->oSlotHeader.dwAttributes;

        /* Strip off private attributes */
        *pfAttributes &= ~(eSlotIsHidden|eSlotIsNamespace);
    }
    else
    {
        /* update the flag and flush to disk */
        DRM_DWORD dwAttributes = (pSlotCtx->oSlotHeader.dwAttributes) & (eSlotIsNamespace|eSlotIsHidden);

        /* Set to new public attributes.  Turn off private attributes */
        pSlotCtx->oSlotHeader.dwAttributes = *pfAttributes & ~(eSlotIsNamespace|eSlotIsHidden);
        
        /* OR back in private attributes */
        pSlotCtx->oSlotHeader.dwAttributes |= dwAttributes;
        FIX_ENDIAN_DWORD( pSlotCtx->oSlotHeader.dwAttributes );
        FIX_ENDIAN_DWORD( pSlotCtx->oSlotHeader.dwSlotSize );
        ChkDR(_HdsGetPutBlockPayload(pSlotCtx->pCurrChildBlock, pSlotCtx->dwSlotPosInBlock, 
            SIZEOF(_SlotHeader), &pSlotCtx->oSlotHeader, GPBP_OPERATION_WRITE));
        FIX_ENDIAN_DWORD( pSlotCtx->oSlotHeader.dwAttributes );
        FIX_ENDIAN_DWORD( pSlotCtx->oSlotHeader.dwSlotSize );
    }

ErrorExit:        
    if ( pSlotCtx )
    {
        _Hds_free(pNS->pHDS, pSlotCtx);
    }

    return dr;
}


/*
**
*/
static DRM_RESULT _HdsInitSlotEnum(
    IN  _NsContext            *pNS,
    IN  const DRM_HDS_HASHKEY *pHashKey,
    IN  DRM_HDS_LOCKMODE       eLockMode,
    OUT _EnumContext          *pEnum)
{
    DRM_RESULT dr=DRM_SUCCESS;
    DRM_BOOL   fResult=FALSE;

    /* init Enum context */
    ZEROMEM(pEnum, SIZEOF(DRM_HDS_ENUM_CONTEXT));
    pEnum->wContextSize = SIZEOF(DRM_HDS_ENUM_CONTEXT);
    pEnum->pNS = pNS;
    pEnum->fInited = TRUE;
    pEnum->eContextSignature = eEnumContextSignature;
    pEnum->fCurrBlockIsValid = FALSE;
    pEnum->eLockMode = eLockMode;

    /* locate and prepare pEnum->oCurrBlock, if possible, for EnumNext() */
    ChkDR(_HdsInitBlockBuffer(pEnum->pNS, &pEnum->oCurrBlock, 0, eCHILDBLOCK));
    if ( pHashKey != NULL )     /* Hashkey is given, do query on the Hashkey */
    {
        DRM_DWORD      cbSlotCtx; 
        _SlotContext *pSlotCtx=NULL;

        /* find the first block containing the Hashkey */
        pEnum->fIsQuery = TRUE;
        MEMCPY(pEnum->oHashKey.rgb, pHashKey->rgb, DRM_HDS_HASHKEY_SIZE);
    
        /* allocate temp slotcontext */
        cbSlotCtx = CALC_SLOTCONTEXTLEN(pNS);
        ChkDR(_Hds_malloc(pNS->pHDS, cbSlotCtx, (DRM_VOID**)&pSlotCtx));
        dr = _HdsInitSlotContext(pNS, (DRM_BYTE *)pSlotCtx, cbSlotCtx);
        if ( DRM_SUCCEEDED(dr) )
        {
            /* search the first slot matching given hashkey */
            dr = _HdsSearchSlotInFile(pNS, pHashKey, NULL, eSearchSlotDontCare, NULL, 
                pSlotCtx, &fResult);
            if ( DRM_SUCCEEDED(dr) )
            {
                if ( !fResult )
                {
                    dr = DRM_E_HDSSLOTNOTFOUND;
                }
                else
                {
                    dr = _HdsCopyBlockBuffer(&pEnum->oCurrBlock, pSlotCtx->pCurrChildBlock);
                    if ( DRM_SUCCEEDED(dr) )
                    {
                        ((_ChildBlockHDR*)(&pEnum->oCurrBlock))->nCurrSlotPos = MAXSLOTPOS;
                        ((_ChildBlockHDR*)(&pEnum->oCurrBlock))->nNextSlotPos = MAXSLOTPOS;
                    }
                }
            }
        }

        _Hds_free(pNS->pHDS, pSlotCtx);
        ChkDR(dr);
    }
    else
    {
        /* find the first block in postorder */
        pEnum->fIsQuery = FALSE;
        ChkDR(_HdsTraverseBlocksInPostOrder(pNS, NULL, &pEnum->oCurrBlock, &fResult));
        if ( !fResult )
        {
            ChkDR(DRM_E_HDSSLOTNOTFOUND);
        }
    }

    pEnum->fCurrBlockIsValid = TRUE;
    MEMCPY(&pEnum->oFileBlock, &pEnum->oCurrBlock, SIZEOF(_CommBlockHDR));

ErrorExit:        
    return dr;
}


/*
**
*/
static DRM_RESULT
_HdsCleanupNamespace(
    IN _NsContext *pNS)
{
    DRM_RESULT dr = DRM_SUCCESS;    
    DRM_BOOL fResult;
    _CommBlockHDR *pCurrBlock=NULL;
    _CommBlockHDR *pNextBlock=NULL;

    ChkArg(ISVALIDCONTEXT(pNS, eCfgContextSignature));
    DRMASSERT(pNS && pNS->fInited && pNS->pHDS && pNS->pHDS->fp!=OEM_INVALID_HANDLE_VALUE);
    ChkDR(_HdsAllocBlockBuffer(pNS, eCHILDBLOCK, &pCurrBlock));
    ChkDR(_HdsAllocBlockBuffer(pNS, eCHILDBLOCK, &pNextBlock));

    /* traverse the namespace in POST ORDER and process each block */    
    ChkDR(_HdsTraverseBlocksInPostOrder(pNS, NULL,  pCurrBlock, &fResult));
    while (fResult)
    {
        DRM_BOOL fEmpty=FALSE;

#if 0
        ChkDR(_HdsCleanupBlock((_ChildBlockHDR*)pCurrBlock));   /* cleanup hidden slots */
#endif
        if ( ((_ChildBlockHDR*)pCurrBlock)->File._image.nFreeMem == pNS->nImagesize_ChildBlockPayload )
        {
            fEmpty = TRUE;
        }
        
        ChkDR(_HdsTraverseBlocksInPostOrder(pNS, pCurrBlock, pNextBlock, &fResult)); /* traverse next */
        if ( fEmpty )
        {
            /* perform defragment now.  note: pCurrBlock might get free'd */
            ChkDR(_HdsDefragmentFile(pCurrBlock, NULL));
        }
        if ( !fResult )
        {
            goto ErrorExit;  /* that's it */
        }
        /* the block becomes the current block */
        ChkDR(_HdsCopyBlockBuffer(pCurrBlock, pNextBlock));
    }

ErrorExit:
    if ( pNextBlock )
    {
        _Hds_free(pNS->pHDS, pNextBlock);
    }
    if ( pCurrBlock )
    {
        _Hds_free(pNS->pHDS, pCurrBlock);
    }
    
    return dr;
}



/*
**
*/
static DRM_RESULT _HdsCloseSlot(
    IN _SlotContext *pSlotCtx)
{
    DRM_RESULT dr=DRM_SUCCESS;
    
    ChkArg(ISVALIDCONTEXT(pSlotCtx, eSlotContextSignature));
    DRMASSERT(pSlotCtx && pSlotCtx->pNS && pSlotCtx->pNS->pHDS && 
        pSlotCtx->pNS->pHDS->fp!=OEM_INVALID_HANDLE_VALUE);

#if DBG
    /* trash the slot context and return */    
    MEMSET(pSlotCtx, 0xdb, pSlotCtx->dwContextSize);
#endif

    /* to indicate the slot is closed */
    pSlotCtx->eStatus = eSlotCtxUninit;
ErrorExit:
    return dr;
}


/**********************************************************************
** Function:    _HdsSlotEnumNext
** Synopsis:    
** Arguments:   [pEnumContext] -- Enum Context init'd by DRM_HDS_InitSlotEnum()
**              [pSlotContext] -- Slot Context of the current slot
**              [pHashKey] -- Hashkey of the current slot
**              [pUniqueKey] -- Uniquekey of the current slot
**              [pcbSlotSize] -- size of the current slot
** Returns:     DRM_SUCCESS on success
** Notes:       
***********************************************************************
*/
static DRM_RESULT _HdsSlotEnumNext( 
    IN  _EnumContext      *pEnum,
    OUT _SlotContext      *pSlotCtx,
    OUT DRM_HDS_HASHKEY   *pHashKey, 
    OUT DRM_HDS_UNIQUEKEY *pUniqueKey,
    OUT DRM_DWORD         *pcbSlotSize)
{
    DRM_RESULT       dr = DRM_SUCCESS;    
    DRM_BOOL         fResult=FALSE;
    DRM_HDS_HASHKEY *pQueriedHashkey=NULL;
    _CommBlockHDR   *pNextBlock=NULL;
    _CommBlockHDR   *pTmpBlock=NULL;

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

    /* 
    ** Verify validity of the current block of the slot context
    ** BUGBUG: a more detail analysis is needed for this scenario 
    */
    {
        _CommBlockHDR *pBlock = &pEnum->oFileBlock;
        _CommBlockHDR *pCurrBlock = &pEnum->oCurrBlock;
        
        /* load generic block header */
        ChkDR(_HdsGetPutBlockHDR(pEnum->pNS, pEnum->oFileBlock.nBlockNum, &pBlock, 
            GPBH_GENERIC_ONLY, GPBH_OPERATION_READ));

        /* verify the generic header */
        if ( !DRM_UI64Eql(pEnum->oFileBlock.File._image.ui64TimeStamp, pCurrBlock->File._image.ui64TimeStamp) ||
           MEMCMP(pEnum->oFileBlock.File._image.bBlockHash, pCurrBlock->File._image.bBlockHash, SIZEOF(pCurrBlock->File._image.bBlockHash))!=0 )
        {
            /* the current block has been updated/written since last read */
            _SlotHeader slotHeader;
            DRM_DWORD   dwCurrPos = 0;

            /* check if it is still a CHILD block or belong to the same parent */
            if ( GET_BYTE( &pEnum->oFileBlock.File._image.bBlockType, 0 ) != GET_BYTE( &pCurrBlock->File._image.bBlockType, 0 ) 
              || pEnum->oFileBlock.File._image.nParentBlockNum            != pCurrBlock->File._image.nParentBlockNum )
            {
                /* there is nothing we can do when the current block is 
                ** totally invalid 
                */
                ChkDR(DRM_E_NOMORE);    
            }

            /* check if query started yet */
            if ( ((_ChildBlockHDR*)pCurrBlock)->nNextSlotPos == MAXSLOTPOS )
            {
                /* query has not been started, reload the current block from file and keep on */
                ChkDR(_HdsGetPutBlockHDR(pEnum->pNS, pEnum->oFileBlock.nBlockNum, 
                    &pCurrBlock, GPBH_ANY_HEADER, GPBH_OPERATION_READ));
                goto Verified_Okay;
            }

            /* load the child block in a tmp buffer */
            ChkDR(_HdsAllocBlockBuffer(pEnum->pNS, eCHILDBLOCK, &pTmpBlock));
            ChkDR(_HdsGetPutBlockHDR(pEnum->pNS, pEnum->oFileBlock.nBlockNum, 
                &pTmpBlock, GPBH_ANY_HEADER, GPBH_OPERATION_READ));

            /* load the current slot header from file */
            ChkDR(_HdsGetPutBlockPayload(pTmpBlock, ((_ChildBlockHDR*)pCurrBlock)->nCurrSlotPos,
                SIZEOF(_SlotHeader), &slotHeader, GPBP_OPERATION_READ));
            FIX_ENDIAN_DWORD( slotHeader.dwAttributes );
            FIX_ENDIAN_DWORD( slotHeader.dwSlotSize );
            if (MEMCMP(&slotHeader, &pEnum->oSlotHeader, SIZEOF(_SlotHeader))==0 )
            {
                /* the current slot is intact, update the block buffer and continue */
                DRM_DWORD _dwCurrPos = ((_ChildBlockHDR*)pCurrBlock)->nCurrSlotPos;
                DRM_DWORD _dwNextPos = ((_ChildBlockHDR*)pCurrBlock)->nNextSlotPos;
                
                ChkDR(_HdsGetPutBlockHDR(pEnum->pNS, pEnum->oFileBlock.nBlockNum, &pCurrBlock, GPBH_ANY_HEADER, GPBH_OPERATION_READ));
                ((_ChildBlockHDR*)pCurrBlock)->nCurrSlotPos = _dwCurrPos;
                ((_ChildBlockHDR*)pCurrBlock)->nNextSlotPos = _dwNextPos;
                goto Verified_Okay;
            }

            /* walk the new block, try seek to >=  the current slot position. */
            dwCurrPos = ((_ChildBlockHDR*)pCurrBlock)->nCurrSlotPos;
            if ( pEnum->fIsQuery )
            {
                pQueriedHashkey = &pEnum->oHashKey;
            }
            
            ChkDR(_HdsInitSlotContext(pEnum->pNS, (DRM_BYTE*)pSlotCtx, CALC_MAXSLOTCONTEXTLEN));
            while (TRUE)
            {
                /* search current block at next postion */
                ChkDR(_HdsSearchSlotInBlock(pTmpBlock, pQueriedHashkey, NULL, 
                    eSearchSlotDontCare, pSlotCtx, &fResult));
                if ( !fResult )
                {
                    /* the current block has less slots than before, restart query at next block, if any */
                    goto Verified_Okay;
                }
                if ( ((_ChildBlockHDR*)pTmpBlock)->nCurrSlotPos >= dwCurrPos )
                {
                    /* walk to position >= current slot position in old block. restart query from here in new block*/
                    ((_ChildBlockHDR*)pTmpBlock)->nNextSlotPos = ((_ChildBlockHDR*)pTmpBlock)->nCurrSlotPos;
                    ChkDR(_HdsCopyBlockBuffer(&pEnum->oCurrBlock, pTmpBlock));

                    goto Verified_Okay;
                }
            }
        }
    }

Verified_Okay:

    if ( pEnum->fIsQuery )
    {
        pQueriedHashkey = &pEnum->oHashKey;
    }
    
    ChkDR(_HdsInitSlotContext(pEnum->pNS, (DRM_BYTE*)pSlotCtx, CALC_MAXSLOTCONTEXTLEN));
    ChkDR(_HdsAllocBlockBuffer(pEnum->pNS, eCHILDBLOCK, &pNextBlock));
    while (TRUE)
    {
        /* search current block at next postion */
        ChkDR(_HdsSearchSlotInBlock(&pEnum->oCurrBlock, pQueriedHashkey, NULL, 
            eSearchSlotDontCare, pSlotCtx, &fResult));
        if ( fResult )
        {
            if ( pHashKey )
            {
                MEMCPY(pHashKey->rgb, pSlotCtx->oSlotHeader.oHashkey.rgb, DRM_HDS_HASHKEY_SIZE);
            }
            if ( pUniqueKey )
            {
                MEMCPY(pUniqueKey->rgb, pSlotCtx->oSlotHeader.oUniquekey.rgb, DRM_HDS_UNIQUEKEY_SIZE);
            }
            if ( pcbSlotSize )
            {
               *pcbSlotSize = pSlotCtx->oSlotHeader.dwSlotSize;
            }
    
            pSlotCtx->eStatus = eSlotCtxReady;
            break;
        }

        /* keep digging */
        if ( pEnum->fIsQuery )
        {
            ChkDR(_HdsHashToChildBlock(pEnum->pNS, &pEnum->oCurrBlock, pEnum->oHashKey.rgb, 
                &pNextBlock, &fResult, NULL));
        }
        else
        {
            ChkDR(_HdsTraverseBlocksInPostOrder(pEnum->pNS, &pEnum->oCurrBlock, 
                pNextBlock, &fResult));
        }
        
        if ( !fResult )
        {
            /* no more block found */
            dr = DRM_E_NOMORE;
            goto ErrorExit;
        }
        
        /* the block becomes the current block */
        ChkDR(_HdsCopyBlockBuffer(&pEnum->oCurrBlock, pNextBlock));
    }

    MEMCPY(&pEnum->oFileBlock, &pEnum->oCurrBlock, SIZEOF(_CommBlockHDR));
    MEMCPY(&pEnum->oSlotHeader, &pSlotCtx->oSlotHeader, SIZEOF(_SlotHeader));

ErrorExit:

    if ( pNextBlock )
    {
        _Hds_free(pEnum->pNS->pHDS, pNextBlock);
    }
    if ( pTmpBlock )
    {
        _Hds_free(pEnum->pNS->pHDS, pTmpBlock);
    }
    return dr;
}



/**********************************************************************
** Function:    _HdsCleanupStore
** Synopsis:    
** Arguments:   [pHDS] -- 
** Returns:     DRM_SUCCESS on success
** Notes:       
***********************************************************************
*/
static DRM_RESULT _HdsCleanupStore(
    _HdsContext *pHDS)
{
    DRM_RESULT            dr=DRM_SUCCESS;
    DRM_DWORD              cbEntry=0, cbRead=0;
    _EnumContext         *pEnumContext=NULL;
    _NsContext           *pNsStoreCTX=NULL; 
    _NsContext           *pNsCtx=NULL;
    DRM_HDS_SLOT_CONTEXT *pSlotContext=NULL;
    DRM_HDS_NAMESPACE    *pNamespace=NULL;
    _NSEntry             *pNsStoreEntry=NULL;

    /* no namespace store exist ==> no namespace found */
    DRMASSERT(pHDS && pHDS->oSRN.nNsStoreRootBlockNum>0);

    /* allocate memory */
    ChkDR(_Hds_malloc(pHDS, DRM_HDS_ENUM_CONTEXT_LEN,      (DRM_VOID**)&pEnumContext));
    ChkDR(_Hds_malloc(pHDS, DRM_HDS_NAMESPACE_CONTEXT_LEN, (DRM_VOID**)&pNsStoreCTX));
    ChkDR(_Hds_malloc(pHDS, DRM_HDS_NAMESPACE_CONTEXT_LEN, (DRM_VOID**)&pNsCtx));
    ChkDR(_Hds_malloc(pHDS, SIZEOF(DRM_HDS_SLOT_CONTEXT),  (DRM_VOID**)&pSlotContext));
    ChkDR(_Hds_malloc(pHDS, SIZEOF(DRM_HDS_NAMESPACE),     (DRM_VOID**)&pNamespace));

    /* Enum each namespace, call cleanupNamespace */
    ChkDR(_HdsInitNsContext(pHDS, pNsStoreCTX, NULL, pHDS->oSRN.nNsStoreRootBlockNum, 
        NsStoreNumofChildren));
    ChkDR(_HdsInitSlotEnum(pNsStoreCTX, NULL, eDRM_HDS_LOCKSHARED, pEnumContext));
    while (TRUE)
    {
        dr = _HdsSlotEnumNext(pEnumContext, (_SlotContext*)pSlotContext, NULL, NULL, &cbEntry);
        if (dr == DRM_E_NOMORE)
        {
            dr = DRM_SUCCESS;
            break;
        }
        ChkDR(dr);

        /* alloc memory and read the entry */
        ChkDR(_Hds_malloc(pHDS, cbEntry, (DRM_VOID**)&pNsStoreEntry));
        ChkDR(_HdsSlotRead((_SlotContext*)pSlotContext, cbEntry, (DRM_BYTE *)pNsStoreEntry, &cbRead));
        if ( cbRead != cbEntry )
        {
            ChkDR(DRM_E_FILEREADERROR);
        }
        FIX_ENDIAN_WORD( pNsStoreEntry->wMaxNumChildren );
        FIX_ENDIAN_DWORD( pNsStoreEntry->nNSRBlockNum );

        ChkDR(_HdsCloseSlot((_SlotContext*)pSlotContext));
        ChkDR(_HdsValidateNSEntryFromDisk( pHDS, pNsStoreEntry ) );

        /* cleanup this namespace */
        MEMCPY(pNamespace->rgb, pNsStoreEntry->bNSName, DRM_HDS_NSNAME_SIZE);
        ChkDR(_HdsInitNsContext(pHDS, pNsCtx, pNamespace, pNsStoreEntry->nNSRBlockNum,
            pNsStoreEntry->wMaxNumChildren));
        ChkDR(_HdsCleanupNamespace(pNsCtx));
        ChkDR(_Hds_free(pHDS, pNsStoreEntry));
        pNsStoreEntry = NULL;
    }
    
ErrorExit:
    /* free the local memory in reverse order */
    _Hds_free(pHDS, pNsStoreEntry);
    _Hds_free(pHDS, pNamespace);
    _Hds_free(pHDS, pSlotContext);
    _Hds_free(pHDS, pNsCtx);
    _Hds_free(pHDS, pNsStoreCTX);
    _Hds_free(pHDS, pEnumContext);
    return dr;
}


/**********************************************************************
** Function:    _HdsSlotResize
** Synopsis:    
** Arguments:   [pSlotCtx]  -- slot context
**              [cbNewSize] -- new size to be used
** Returns:     DRM_SUCCESS on success
** Notes:       The resized slot will have the same type of lock as before.
***********************************************************************
*/
static DRM_RESULT _HdsSlotResize( 
    IN _SlotContext *pSlotCtx, 
    IN DRM_DWORD     cbNewSize)
{
    DRM_RESULT     dr=DRM_SUCCESS;
    DRM_DWORD      dwOldSize=0;

    DRMASSERT(pSlotCtx && cbNewSize && pSlotCtx->pNS && pSlotCtx->pNS->pHDS);
    dwOldSize=pSlotCtx->oSlotHeader.dwSlotSize;
    if ( cbNewSize == dwOldSize )    /* same size, do nothing */
    {
        goto ErrorExit;
    }

    /* slot must be locked EXCLUSIVE */
    if ( (pSlotCtx->eLockMode & eDRM_HDS_LOCKEXCLUSIVE) == 0 )
    {
        ChkDR(DRM_E_HDSNOTLOCKEDEXCLUSIVE);
    }
    
    /*************************************************************************
    ** all other cases will involve copying or moving slot content
    */
    ChkDR(_HdsRelocateSlotAndKeepCurrLock(pSlotCtx, cbNewSize));
    pSlotCtx->dwSeekPointer = 0;
    
ErrorExit:
    if( DRM_SUCCEEDED( dr ) )
    {
        /* On success the size of the slot should be the user given size */
        pSlotCtx->oSlotHeader.dwSlotSize = cbNewSize;
    }
    return dr;
}

static DRM_VOID _ZeroHDSContextIgnoringCritSec(
    _HdsContext *f_pcontextHDS)
{
    f_pcontextHDS->dwContextSize               = 0;
    f_pcontextHDS->fInited                     = FALSE;
    f_pcontextHDS->eContextSignature           = 0;
    f_pcontextHDS->fp                          = OEM_INVALID_HANDLE_VALUE;
    f_pcontextHDS->nImagesize_FileBlockHDR     = 0;
    f_pcontextHDS->nImagesize_DataBlockHDR     = 0;
    f_pcontextHDS->nImagesize_DataBlockPayload = 0;
    f_pcontextHDS->nDataBlockPayloadPos        = 0;

    ZEROMEM(&f_pcontextHDS->oHeap,    SIZEOF(f_pcontextHDS->oHeap));
    ZEROMEM(&f_pcontextHDS->oSRN,     SIZEOF(f_pcontextHDS->oSRN));
    ZEROMEM(&f_pcontextHDS->contextMD5, SIZEOF(f_pcontextHDS->contextMD5));
}


static DRM_RESULT _HdsCreateNameSpaceStore(
    IN _HdsContext *pHDS)
{
    DRM_RESULT     dr   = DRM_SUCCESS;
    _NsContext     oNsStoreCFG;
    _CommBlockHDR *pNsStoreBlock = NULL;

    /* if namespace store does not exist */
    if ( pHDS->oSRN.nNsStoreRootBlockNum == 0 )
    {
        /* NamespaceStore root block does not exist, create one and update SRN */
        ChkDR(_HdsInitNsContext(pHDS, &oNsStoreCFG, NULL, 0, NsStoreNumofChildren));
        ChkDR(_HdsLoadSRN(pHDS));   /* update SRN in memory */
        
        ChkDR(_HdsAllocBlock(&oNsStoreCFG, ROOTPARENTNUM, &pNsStoreBlock, eCHILDBLOCK));
        pHDS->oSRN.nNsStoreRootBlockNum = pNsStoreBlock->nBlockNum;

        ChkDR(_HdsUpdateSRN(pHDS));

        /* we do not create namespace too often, let's flush it now */
        if ( !OEM_FlushFileBuffers(pHDS->fp) )
        {
            ChkDR(DRM_E_FILEWRITEERROR);
        }
    }

ErrorExit:

    _Hds_free(pHDS, pNsStoreBlock);
    return dr;
}


static DRM_RESULT _HdsCreateNamespace( 
    IN       DRM_HDS_CONTEXT   *pcontextHDS,
    IN const DRM_HDS_NAMESPACE *pNamespace,
    IN const DRM_WORD           wMaxNumChildren, /* wMaxNumChildren must be <= DRM_HDS_MAXIMUM_CHILDREN */
    IN       DRM_BOOL           fWait)
{
    DRM_RESULT        dr=DRM_SUCCESS;
    DRM_DWORD         cbNsStoreSlotCtx=0, cbNsStoreSlot=0;
    _HdsContext      *pHDS=(_HdsContext*)pcontextHDS;
    _NsContext        oNsStoreCFG;
    _SlotContext     *pNsStoreSlotCtx=NULL;
    _NSEntry         *pNsStoreEntry=NULL;
    _CommBlockHDR    *pNsRootBlock=NULL;
    DRM_BOOL          fCreated=FALSE;
    _NsContext        oNsCtx;

    DRMASSERT(pcontextHDS && pNamespace && pHDS->fp!=OEM_INVALID_HANDLE_VALUE &&
        wMaxNumChildren > 0 && wMaxNumChildren <= DRM_HDS_MAXIMUM_CHILDREN);

    /* flush SRN in context */
    ChkDR(_HdsLoadSRN(pHDS));

    /* First, set up a HDS Config for accessing the NamespaceStore 
    ** ===========================================================
    */
    ChkDR(_HdsCreateNameSpaceStore(pHDS));


    /* Second, set up tmp Config for the new namespace, allocate and set up 
    ** the Root block for the new namespace 
    ** Note, parent of NamespaceRoot block is always 'ROOTPARENTNUM'
    ** ============================================================
    */
    ChkDR(_HdsInitNsContext(pHDS, &oNsCtx, pNamespace, 0, wMaxNumChildren));
    ChkDR(_HdsAllocBlock(&oNsCtx, ROOTPARENTNUM, &pNsRootBlock, eCHILDBLOCK));

    /* Third, alloc buffer and setup _SlotContext to store the given 
    ** namespace in NamespaceStore
    ** ==============================================================
    */
    {
        DRM_HDS_HASHKEY   oNsHashKey;
        DRM_HDS_UNIQUEKEY oNsUniqueKey;
        DRM_DWORD         cbNsStoreSlotCtx = 0;
    
        ChkDR(_HdsInitNsContext(pHDS, &oNsStoreCFG, NULL, pHDS->oSRN.nNsStoreRootBlockNum, 
            NsStoreNumofChildren));     /* set up the Config for NamespaceStore */

        cbNsStoreSlotCtx = CALC_SLOTCONTEXTLEN(&oNsStoreCFG);  /* size of slot context */
        ChkDR(_Hds_malloc(pHDS, cbNsStoreSlotCtx, (DRM_VOID**)&pNsStoreSlotCtx));
        ChkDR(_HdsInitSlotContext(&oNsStoreCFG, (DRM_BYTE *)pNsStoreSlotCtx, cbNsStoreSlotCtx));

        /* prepare the namespace content and store it in NamespaceStore
        ** ===================================================================
        */
        _GenNamespaceKeys (&pHDS->contextMD5, 
               (DRM_CHAR *) pNamespace->rgb, 
                            DRM_HDS_NSNAME_SIZE, 
                            oNsHashKey.rgb, 
                            oNsUniqueKey.rgb);  /* gen key for given namespace */
        cbNsStoreSlot = SIZEOF(_NSEntry) + DRM_HDS_NSNAME_SIZE;    /* size of the namespace slot in NamespaceStore */
        dr = _HdsCreateAndOpenSlot(
                &oNsStoreCFG, 
                &oNsHashKey, 
                &oNsUniqueKey, 
                FALSE, 
                cbNsStoreSlot, 
                pNsStoreSlotCtx,
                eDRM_HDS_LOCKEXCLUSIVE | (fWait? eDRM_HDS_LOCKWAIT : 0) );
        if ( DRM_SUCCEEDED(dr) )
        {
            /* store the namespace into NamespaceStore */
            _NsContext oNsCtx;
            DRM_DWORD cbWritten=0;

            fCreated = TRUE;

            /* set up tmp Config for the new namespace and allocate and set up 
            ** the Root block for the new namespace 
            ** Note, parent of NamespaceRoot block is always 'ROOTPARENTNUM'*/
            ChkDR(_HdsInitNsContext(pHDS, &oNsCtx, pNamespace, 0, wMaxNumChildren));

            /* allocate buffer and prepare the namespace slot */
            ChkDR(_Hds_malloc(pHDS, cbNsStoreSlot, (DRM_VOID**)&pNsStoreEntry));
            pNsStoreEntry->nNSRBlockNum = pNsRootBlock->nBlockNum;
            pNsStoreEntry->wMaxNumChildren = wMaxNumChildren;
            MEMCPY(pNsStoreEntry->bNSName, pNamespace->rgb, DRM_HDS_NSNAME_SIZE);

            /* write the namespace slot in NsStore */
            FIX_ENDIAN_WORD( pNsStoreEntry->wMaxNumChildren );
            FIX_ENDIAN_DWORD( pNsStoreEntry->nNSRBlockNum );
            dr = _HdsSlotWrite(pNsStoreSlotCtx, cbNsStoreSlot, (DRM_BYTE *)pNsStoreEntry, &cbWritten);
            FIX_ENDIAN_WORD( pNsStoreEntry->wMaxNumChildren );
            FIX_ENDIAN_DWORD( pNsStoreEntry->nNSRBlockNum );

            /* we do not create namespace too often, let's flush it now */
            if ( !OEM_FlushFileBuffers(pHDS->fp) )
            {
                dr = DRM_E_FILEWRITEERROR;
            }
            
            goto ErrorExit;        
        }
        else if ( dr == DRM_E_HDSSLOTEXIST )
        {
            TRACE(("DRM_HDS_CreateNamespace(): namespace '%s' already exist.\n", pNamespace->rgb));
            dr = DRM_E_HDSNAMESPACEEXIST;
        }
    }

ErrorExit:

    if ( DRM_FAILED(dr)  &&  pNsRootBlock != NULL )
    {
        /* we have error, remove the allocated slot */
        _HdsFreeBlock((_CommBlockHDR *)pNsRootBlock);
    }        
    
    if ( fCreated )
    {
        _HdsUnlockSlot(pNsStoreSlotCtx);
        _HdsCloseSlot(pNsStoreSlotCtx);
    }
    
    _Hds_free(pHDS, pNsStoreEntry);
    _Hds_free(pHDS, pNsStoreSlotCtx);
    _Hds_free(pHDS, pNsRootBlock);
    return dr;
}




static DRM_RESULT _HdsOpenExistingNamespace(
    IN       DRM_HDS_CONTEXT           *pcontextHDS,
    IN const DRM_HDS_NAMESPACE         *pNamespace,
    OUT      DRM_HDS_NAMESPACE_CONTEXT *pcontextNS) /* user given NS context buffer, NULL to get size */
{
    DRM_RESULT        dr=DRM_SUCCESS;
    DRM_HDS_HASHKEY   oNsHashKey;
    DRM_HDS_UNIQUEKEY oNsUniqueKey;
    DRM_DWORD         cbNsStoreSlotCtx=0;
    DRM_DWORD         cbNsSlotSize=0;
    _NsContext        oNsStoreCFG;
    _HdsContext      *pHDS=(_HdsContext*)pcontextHDS;
    _SlotContext     *pNsStoreSlotCtx=NULL;
    _NSEntry         *pNsEntry=NULL;

    DRMASSERT(pcontextHDS && pNamespace && pcontextNS && pHDS->fp!=OEM_INVALID_HANDLE_VALUE);
    DRMSIZEASSERT( SIZEOF( _NsContext ), SIZEOF( DRM_HDS_NAMESPACE_CONTEXT ) );

    /* 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_MAXSLOTCONTEXTLEN;
    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);
    dr = _HdsOpenExistingSlot(&oNsStoreCFG, &oNsHashKey, &oNsUniqueKey, FALSE,
        &cbNsSlotSize, pNsStoreSlotCtx);
    if ( !DRM_FAILED(dr) )
    {
        _NsContext *pNsCFG=NULL;

        /* 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 ) );

        pNsCFG = (_NsContext *)pcontextNS;
        ChkDR(_HdsInitNsContext(pHDS, pNsCFG, pNamespace, pNsEntry->nNSRBlockNum,
            pNsEntry->wMaxNumChildren));

#ifdef _CHKHDS_
        printf("\n\t[Namespace Info]\n");
        printf("\t[# of Children blocks]\t\t%d\n", pNsEntry->wMaxNumChildren);
        printf("\t[Namespace Root block#]\t\t%d\n", pNsEntry->nNSRBlockNum);
        printf("\t[Generic block header size]\t%d\n", pHDS->nImagesize_FileBlockHDR);
        printf("\t[CHILD block header size]\t%d\n", pNsCFG->nImagesize_ChildBlockHDR);
        printf("\t[CHILD alloctable size]\t\t%d\n", pNsCFG->nImagesize_ChildAllocTable);
        printf("\t[CHILD block payload size]\t%d\n", pNsCFG->nImagesize_ChildBlockPayload);
        printf("\t[CHILD block payload pos]\t%d\n", pNsCFG->nChildBlockPayloadPos);
#endif
        
        ChkDR(_HdsCloseSlot(pNsStoreSlotCtx));
    }
    else if ( dr == DRM_E_HDSSLOTNOTFOUND )
    {
        TRACE(("_HdsOpenExistingNamespace(): namespace '%s' does not exist.\n", pNamespace->rgb));
        ChkDR(DRM_E_HDSNAMESPACENOTFOUND);
    }
    ChkDR(dr);

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


static DRM_RESULT _HdsFormatExpandedSpace(
    IN  _HdsContext *f_pHDS,
    IN  DRM_DWORD    f_nBeginBlockNum,
    IN  DRM_DWORD    f_nNumBlocks)
{
    DRM_RESULT     dr     = DRM_SUCCESS;
    _CommBlockHDR *pBlock = NULL;
    DRM_DWORD      i;

    ChkArg( f_pHDS != NULL
        && f_nNumBlocks > 0 );

    ChkDR(_Hds_malloc(f_pHDS, GetMemsize_DataBlock(), (DRM_VOID**)&pBlock));
    
    /* format the expanded space in blocks, start from last block */
    for (i = (f_nBeginBlockNum + f_nNumBlocks - 1); i >= f_nBeginBlockNum; i--)
    {
        DRM_DWORD      dwFilePos  = 0;
        _DataBlockHDR *pDataBlock = (_DataBlockHDR*)pBlock;

        /* initialize the tmp block */
        pBlock->nBlockNum = i;
        pBlock->File._image.nParentBlockNum = 0;
        pBlock->File._image.ui64TimeStamp   = _GetTimeStamp();

        PUT_BYTE( &pBlock->File._image.bBlockType, 0, eFREEBLOCK );

        pDataBlock->nPayloadSize = f_pHDS->nImagesize_DataBlockPayload;
        pDataBlock->File._image.nCascadingBlockNum = f_pHDS->oSRN.nFreeListHead;

        /* chain the block to SRN's freelist */
        f_pHDS->oSRN.nFreeListHead = pBlock->nBlockNum;
        if ( f_pHDS->oSRN.nHighestFormattedBlockNum < pBlock->nBlockNum )
        {
            f_pHDS->oSRN.nHighestFormattedBlockNum = pBlock->nBlockNum;
        }

        ChkDR(_HdsGenBlockHash(f_pHDS, NULL, pBlock, pBlock->File._image.bBlockHash));

        /* update block to file */
        ChkDR(_HdsBlockNum2FilePos(f_pHDS, i, &dwFilePos));
        if ( OEM_SetFilePointer(f_pHDS->fp, dwFilePos, OEM_FILE_BEGIN, NULL)
          && _WriteCommonBlockHeader(f_pHDS, pBlock, f_pHDS->nImagesize_FileBlockHDR, NULL) 
          && _WriteDataBlockHeader(f_pHDS, pDataBlock) )
        {
            /* commit changes of root node to file */
            ChkDR(_HdsUpdateSRN(f_pHDS));
        }
        else
        {
            ChkDR(DRM_E_FILEWRITEERROR);
        }
    }


ErrorExit:

    _Hds_free(f_pHDS, pBlock);
    return dr;
}

/* number of blocks to format in the expanded/raw store space.
** this number must be >= 1
*/
#define NUMBLOCKSTOFORMAT  1

static DRM_RESULT _HdsPreAlloc( 
    IN  _HdsContext *f_pHDS,
    IN  DRM_DWORD    f_PreAllocFileSizeInKB,
    IN  DRM_BOOL     f_fUpToSpecifiedSize,
    OUT DRM_DWORD   *f_pnNextBlockNum)
{
    DRM_RESULT     dr   = DRM_SUCCESS;
    DRM_DWORD      i    = 0;
    DRM_DWORD      dwFileSize = 0;
    DRM_DWORD      nNumBlocks    = 0;
    DRM_DWORD      nNextBlockNum = 0;
    DRM_DWORD      nLastBlockNum = 0;

    /* calc # of blocks to be appended at end of file, then append them */
    if (!OEM_GetFileSize(f_pHDS->fp, &dwFileSize))
    {
        ChkDR(DRM_E_FILEREADERROR);
    }

    /* get the block number of the last block */
    ChkDR(_HdsFilePos2BlockNum(f_pHDS, dwFileSize, &nLastBlockNum));
    --nLastBlockNum;

    /* check if we need to expand the file */
    if ( f_pHDS->oSRN.nHighestFormattedBlockNum > 0
      && f_pHDS->oSRN.nHighestFormattedBlockNum < nLastBlockNum )
    {
        /* unformated store space exist, format the next NUMBLOCKSTOFORMAT blocks  */
        nNextBlockNum = f_pHDS->oSRN.nHighestFormattedBlockNum + 1;
        nNumBlocks = nLastBlockNum - f_pHDS->oSRN.nHighestFormattedBlockNum;
    }
    else
    {
        /* expand the store */    

        /* make sure the prealloc size is bigger than the current file size */
        if ( f_fUpToSpecifiedSize )
        {
            ChkArg( dwFileSize < (f_PreAllocFileSizeInKB * 1024) );
        }

        nNumBlocks = (f_PreAllocFileSizeInKB * 1024) / f_pHDS->oSRN.dwBlockSize;
        
        /* make sure # of blocks to prealloc is not zero */
        if (nNumBlocks == 0)
        {
            nNumBlocks = 1;
        }

        /* expand the file by n blocks */
        ChkDR(_HdsExpandStore(f_pHDS, nNumBlocks, &nNextBlockNum));

    }

    /* format NUMBLOCKSTOFORMAT blocks */
    if ( nNumBlocks > NUMBLOCKSTOFORMAT )
    {
        nNumBlocks = NUMBLOCKSTOFORMAT;
    }

    /* format at most NUMBLOCKSTOFORMAT blocks in the raw space  */
    ChkDR(_HdsFormatExpandedSpace(f_pHDS, nNextBlockNum, nNumBlocks));

    /* flush SRN in context */
    ChkDR( _HdsLoadSRN(f_pHDS) );

    if ( f_pnNextBlockNum != NULL )
    {
        *f_pnNextBlockNum = nNextBlockNum;
    }
ErrorExit:

    return dr;
}


/**********************************************************************
** Function:    _HdsLockRestOfBlock2DeleteSlot
** Synopsis:    lock block payload from the given slot to end of block.
**              It is essentially the same as _HdsLockSlot where we are
**              grabbing a lock on a bigger slot.
** Arguments:   [pSlotCtx] -- 
**              [fExclusive] --
** Returns:     DRM_SUCCESS on success
** Notes:       To remove a slot from a block, it is necessary to lock
**              the slot to end of block (to do adjustments).
***********************************************************************
*/
static DRM_RESULT _HdsLockRestOfBlock2DeleteSlot(
    _SlotContext *pSlotCtx,
    DRM_DWORD     eMode)
{
#if _MULTI_THREADING_SUPPORT==1

    DRM_RESULT dr = DRM_SUCCESS;
    DRM_DWORD  dwFilePos    = 0;
    DRM_LONG   dwLockSize   = 0;
    DRM_DWORD  nBytesToSkip = 0;

    /* compute filepos of the block */
    ChkDR(_HdsBlockNum2FilePos(pSlotCtx->pNS->pHDS, pSlotCtx->pCurrChildBlock->nBlockNum, &dwFilePos));

    /* skip current slot */
    if ( ISOVERSIZESLOT(pSlotCtx->oSlotHeader.dwSlotSize, pSlotCtx->pNS) )
    {
        nBytesToSkip = SIZEOF(_SlotHeader) + SIZEOF(DRM_DWORD);
    }
    else
    {
        nBytesToSkip = SIZEOF(_SlotHeader) + pSlotCtx->oSlotHeader.dwSlotSize;
    }

    /* adjust filepos relative to the block */
    dwLockSize = dwFilePos + pSlotCtx->pNS->pHDS->oSRN.dwBlockSize;
    dwFilePos += (pSlotCtx->pNS->nChildBlockPayloadPos + pSlotCtx->dwSlotPosInBlock + nBytesToSkip);
    dwLockSize -= dwFilePos;

#ifdef _TRACELOCK
    TRACE(("_HdsLockRestOfBlock2DeleteSlot issued:  %c%c - pos %d,  size %d\n",  
          (eMode & eDRM_HDS_LOCKEXCLUSIVE)? 'X' : 'S', 
          (eMode & eDRM_HDS_LOCKWAIT)? 'W' : ' ', 
          dwFilePos, 
          dwLockSize));
#endif

    /* lock it */
    if ( !OEM_LockFile(pSlotCtx->pNS->pHDS->fp, 
                       ((eMode & eDRM_HDS_LOCKEXCLUSIVE) != 0), 
                       dwFilePos, 
                       dwLockSize,
                       ((eMode & eDRM_HDS_LOCKWAIT) != 0) ) )
    {
        ChkDR(DRM_E_HDSLOCKFAILED);
    }
#ifdef _TRACELOCK
    TRACE(("_HdsLockRestOfBlock2DeleteSlot obtained\n"));
#endif    

ErrorExit:
    return dr;
    
#else   /* !_MULTI_THREADING_SUPPORT==1 */

    return DRM_SUCCESS;
    
#endif
}


static DRM_RESULT _HdsUnlockRestOfBlock2DeleteSlot(
    _SlotContext *pSlotCtx)
{
#if _MULTI_THREADING_SUPPORT==1

    DRM_RESULT dr = DRM_SUCCESS;
    DRM_DWORD  dwFilePos    = 0;
    DRM_LONG   dwLockSize   = 0;
    DRM_DWORD  nBytesToSkip = 0;
    DRM_BOOL   fResult = FALSE;

    /* compute filepos of the block */
    ChkDR(_HdsBlockNum2FilePos(pSlotCtx->pNS->pHDS, pSlotCtx->pCurrChildBlock->nBlockNum, &dwFilePos));

    /* skip current slot */
    if ( ISOVERSIZESLOT(pSlotCtx->oSlotHeader.dwSlotSize, pSlotCtx->pNS) )
    {
        nBytesToSkip = SIZEOF(_SlotHeader) + SIZEOF(DRM_DWORD);
    }
    else
    {
        nBytesToSkip = SIZEOF(_SlotHeader) + pSlotCtx->oSlotHeader.dwSlotSize;
    }

    /* adjust filepos relative to the block */
    dwLockSize = dwFilePos + pSlotCtx->pNS->pHDS->oSRN.dwBlockSize;
    dwFilePos += (pSlotCtx->pNS->nChildBlockPayloadPos+pSlotCtx->dwSlotPosInBlock + nBytesToSkip);
    dwLockSize -= dwFilePos;

#ifdef _TRACELOCK
    TRACE(("_HdsUnlockRestOfBlock2DeleteSlot issued:  - pos %d,  size %d\n", dwFilePos, 
        dwLockSize));
#endif

    /* unlock it, do not return even if this fail */
    fResult = OEM_UnlockFile(pSlotCtx->pNS->pHDS->fp, dwFilePos, dwLockSize);

    if ( !fResult )
    {
        dr = DRM_E_HDSLOCKFAILED;
    }

ErrorExit:
    return dr;
    
#else   /* !_MULTI_THREADING_SUPPORT==1 */

    return DRM_SUCCESS;
    
#endif
}




/**********************************************************************
** Function:    _HdsSlotEnumDeleteCurrent
** Synopsis:    
** Arguments:   [pEnumContext] -- Enum Context init'd by DRM_HDS_InitSlotEnum()
**              [pSlotContext] -- Slot Context of the current slot
** Returns:     DRM_SUCCESS on success
** Notes:       
***********************************************************************
*/
static DRM_RESULT _HdsSlotEnumDeleteCurrent( 
    IN  _EnumContext *pEnum,
    OUT _SlotContext *pSlotCtx)
{
    DRM_RESULT       dr = DRM_SUCCESS;    
    DRM_BOOL         fRestOfBlockLocked = FALSE;
    _CommBlockHDR   *pTmpBlock = NULL;

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

    /* 
    ** Verify validity of the current block of the slot context
    ** BUGBUG: a more detail analysis is needed for this scenario 
    */
    {
        _CommBlockHDR *pBlock = &pEnum->oFileBlock;
        _CommBlockHDR *pCurrBlock = &pEnum->oCurrBlock;

        /* check if query started yet */
        if ( ((_ChildBlockHDR*)pCurrBlock)->nNextSlotPos == MAXSLOTPOS )
        {
            /* query has not been started, cannot proceed */
            ChkDR(DRM_E_HDSSLOTNOTFOUND);
        }

        /* load generic block header */
        ChkDR(_HdsGetPutBlockHDR(pEnum->pNS, pEnum->oFileBlock.nBlockNum, &pBlock, GPBH_GENERIC_ONLY, GPBH_OPERATION_READ));

        /* verify the generic header */
        if ( !DRM_UI64Eql(pEnum->oFileBlock.File._image.ui64TimeStamp, pCurrBlock->File._image.ui64TimeStamp) 
          || MEMCMP(pEnum->oFileBlock.File._image.bBlockHash, pCurrBlock->File._image.bBlockHash, SIZEOF(pCurrBlock->File._image.bBlockHash))!=0 )
        {
            /* the current block has been updated/written since last read */
            _SlotHeader slotHeader;
            DRM_DWORD   dwCurrPos = 0;

            /* check if it is still a CHILD block or belong to the same parent */
            if ( GET_BYTE( &pEnum->oFileBlock.File._image.bBlockType, 0 ) != GET_BYTE( &pCurrBlock->File._image.bBlockType, 0 ) 
              || pEnum->oFileBlock.File._image.nParentBlockNum            != pCurrBlock->File._image.nParentBlockNum )
            {
                /* there is nothing we can do when the current block is totally invalid 
                */
                ChkDR(DRM_E_HDSSLOTNOTFOUND);    
            }

            /* load the child block in a tmp buffer */
            ChkDR(_HdsAllocBlockBuffer(pEnum->pNS, eCHILDBLOCK, &pTmpBlock));
            ChkDR(_HdsGetPutBlockHDR(pEnum->pNS, pEnum->oFileBlock.nBlockNum, 
                &pTmpBlock, GPBH_ANY_HEADER, GPBH_OPERATION_READ));

            /* load the current slot header from file */
            ChkDR(_HdsGetPutBlockPayload(pTmpBlock, ((_ChildBlockHDR*)pCurrBlock)->nCurrSlotPos,
                SIZEOF(_SlotHeader), &slotHeader, GPBP_OPERATION_READ));
            FIX_ENDIAN_DWORD( slotHeader.dwAttributes );
            FIX_ENDIAN_DWORD( slotHeader.dwSlotSize );
            if (MEMCMP(&slotHeader, &pEnum->oSlotHeader, SIZEOF(_SlotHeader))==0 )
            {
                /* the current slot is intact, update the block buffer and continue */
                DRM_DWORD _dwCurrPos = ((_ChildBlockHDR*)pCurrBlock)->nCurrSlotPos;
                DRM_DWORD _dwNextPos = ((_ChildBlockHDR*)pCurrBlock)->nNextSlotPos;
                
                ChkDR(_HdsGetPutBlockHDR(pEnum->pNS, pEnum->oFileBlock.nBlockNum, &pCurrBlock, GPBH_ANY_HEADER, GPBH_OPERATION_READ));
                ((_ChildBlockHDR*)pCurrBlock)->nCurrSlotPos = _dwCurrPos;
                ((_ChildBlockHDR*)pCurrBlock)->nNextSlotPos = _dwNextPos;
                goto Verified_Okay;
            }

            ChkDR(DRM_E_HDSSLOTNOTFOUND);    
        }
    }

Verified_Okay:

    /* we have exclusive lock of the slot already. Let's lock the rest of the block */
    if ( DRM_SUCCEEDED(_HdsLockRestOfBlock2DeleteSlot(pSlotCtx, eDRM_HDS_LOCKEXCLUSIVE)) )
    {
        fRestOfBlockLocked = TRUE;
        ChkDR( _HdsRemoveSlot(pSlotCtx, eRemoveSlotPermanent) );
    }

ErrorExit:

    if ( fRestOfBlockLocked )
    {
        _HdsUnlockRestOfBlock2DeleteSlot(pSlotCtx);
    }

    if ( pTmpBlock )
    {
        _Hds_free(pEnum->pNS->pHDS, pTmpBlock);
    }

    if ( DRM_SUCCEEDED(dr) )
    {
        _HdsUnlockSlot(pSlotCtx);
        _HdsCloseSlot(pSlotCtx);
    }
    return dr;
}


static DRM_RESULT _HdsBlockScanInit(
    IN        _NsContext      *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_HDS_LOCKMODE  f_lockmode,
    OUT _BlockScanContext     *f_pcontextBlockScan)
{
    DRM_RESULT    dr            = DRM_SUCCESS;
    DRM_BOOL      fOK       = FALSE;
    DRM_DWORD     cbSlotContext = 0; 
    _SlotContext *pcontextSlot  = NULL;

    /* init Enum context */

    ZEROMEM(f_pcontextBlockScan, SIZEOF(DRM_HDS_BLOCKSCAN_CONTEXT));
    f_pcontextBlockScan->cbContext         = SIZEOF(DRM_HDS_BLOCKSCAN_CONTEXT);
    f_pcontextBlockScan->pcontextNS        = f_pcontextNS;
    f_pcontextBlockScan->fInited           = TRUE;
    f_pcontextBlockScan->eContextSignature = eEnumContextSignature;
    f_pcontextBlockScan->fCurrBlockIsValid = FALSE;
    f_pcontextBlockScan->lockmode          = f_lockmode;
    f_pcontextBlockScan->pkeyUnique        = f_rgkeyUnique;
    f_pcontextBlockScan->cKeysUnique       = f_cKeysUnique;
    f_pcontextBlockScan->iKeyCurrent       = 0;

    /* locate and prepare pEnum->oCurrBlock, if possible, for EnumNext() */

    ChkDR(_HdsInitBlockBuffer(f_pcontextBlockScan->pcontextNS, 
                             &f_pcontextBlockScan->blockheaderCurrent, 
                              0, 
                              eCHILDBLOCK));

    /* save the hash key in the blockscan context */

    MEMCPY(f_pcontextBlockScan->keyHash.rgb, f_pkeyHash->rgb, DRM_HDS_HASHKEY_SIZE);

    /* allocate temp slotcontext */
    ChkOverflowSLOTCONTEXTLEN( f_pcontextNS );
    cbSlotContext = CALC_SLOTCONTEXTLEN(f_pcontextNS);

    ChkDR(_Hds_malloc(f_pcontextNS->pHDS, 
                        cbSlotContext, 
        (DRM_VOID **) &pcontextSlot));
    
    dr = _HdsInitSlotContext(f_pcontextNS, (DRM_BYTE *)pcontextSlot, cbSlotContext);

    /* find the first block containing the Hashkey */

    if (DRM_SUCCEEDED(dr))
    {
        /* search the first slot matching given hashkey */

        dr = _HdsSearchSlotInFile(f_pcontextNS, 
                                  f_pkeyHash, 
                                  NULL, 
                                  eSearchSlotDontCare, 
                                  NULL, 
                                  pcontextSlot, 
                                 &fOK);

        if (DRM_SUCCEEDED(dr))
        {
            if (! fOK)
            {
                dr = DRM_E_HDSSLOTNOTFOUND;
            }
            else
            {
                dr = _HdsCopyBlockBuffer(&f_pcontextBlockScan->blockheaderCurrent, pcontextSlot->pCurrChildBlock);

                if (DRM_SUCCEEDED(dr))
                {
                    _ChildBlockHDR *pheaderChild = (_ChildBlockHDR *) &f_pcontextBlockScan->blockheaderCurrent;

                    pheaderChild->nCurrSlotPos = MAXSLOTPOS;
                    pheaderChild->nNextSlotPos = MAXSLOTPOS;
                }
            }
        }
    }

    _Hds_free(f_pcontextNS->pHDS, pcontextSlot);
    ChkDR(dr);

    f_pcontextBlockScan->fCurrBlockIsValid = TRUE;

    MEMCPY(&f_pcontextBlockScan->blockheader, 
           &f_pcontextBlockScan->blockheaderCurrent, 
     SIZEOF(f_pcontextBlockScan->blockheaderCurrent));

ErrorExit:        
    return dr;
}

static DRM_BOOL _IsMatchingKey(
    IN       DRM_HDS_UNIQUEKEY *f_pkey,
    IN const DRM_HDS_UNIQUEKEY  f_rgkeys [],
    IN       DRM_DWORD          f_cKeys,
    IN       DRM_DWORD          f_iHint)
{
    DRM_BOOL  fMatch = FALSE;
    DRM_DWORD iKey   = 0;

    if (f_iHint >= f_cKeys)
    {
        f_iHint = 0;
    }

    for (iKey = f_iHint; iKey < f_cKeys; iKey++)
    {
        if (MEMCMP(f_pkey, f_rgkeys + iKey, SIZEOF(DRM_HDS_UNIQUEKEY)) == 0)
        {
            fMatch = TRUE;
            goto MatchFound;
        }
    }

    if (f_iHint > 0)
    {
        for (iKey = 0; iKey < f_iHint; iKey++)
        {
            if (MEMCMP(f_pkey, f_rgkeys + iKey, SIZEOF(DRM_HDS_UNIQUEKEY)) == 0)
            {
                fMatch = TRUE;
                break;
            }
        }
    }

MatchFound:
    return fMatch;
}

#define COA_BLOCK_NOT_SET 0xFFFFFFFF

static DRM_RESULT _CoalesceBlock(_CommBlockHDR   *f_pblockheader,
                                 _SlotContext    *f_pcontextSlotLock,
                                 DRM_HDS_HASHKEY *f_pkeyHash,
                                 DRM_HDS_LOCKMODE f_lockmode,
                                 DRM_BOOL        *f_fLocked)
{
    DRM_RESULT      dr                   = DRM_SUCCESS;
    DRM_BOOL        fFound               = TRUE;
    DRM_BOOL        fMove                = FALSE;
    DRM_DWORD       ibSource             = COA_BLOCK_NOT_SET;
    DRM_DWORD       ibNext               = COA_BLOCK_NOT_SET;
    DRM_DWORD       ibDest               = COA_BLOCK_NOT_SET;
    _SlotContext   *pcontextSlot         = NULL;
    const DRM_DWORD cbBlock              = f_pblockheader->pNS->pHDS->oSRN.dwBlockSize;
    const DRM_DWORD cbSlotContext        = CALC_SLOTCONTEXTLEN(f_pblockheader->pNS);

    ChkOverflowSLOTCONTEXTLEN( f_pblockheader->pNS );

    /* allocate temporary slotcontext */

    ChkDR(_Hds_malloc(f_pblockheader->pNS->pHDS, 
                      cbSlotContext, 
       (DRM_VOID **) &pcontextSlot));

    ((_ChildBlockHDR *) f_pblockheader)->nNextSlotPos = 0;

   *f_fLocked = FALSE;

    while (fFound)
    {
        ChkDR(_HdsInitSlotContext(f_pblockheader->pNS, 
                     (DRM_BYTE *) pcontextSlot, 
                                  cbSlotContext));

        /* get the next hidden block for this HASHKEY */

        ChkDR(_HdsSearchSlotInBlock(f_pblockheader,
                                    f_pkeyHash,
                                    NULL,
                                    eSearchHiddenOnly,
                                    pcontextSlot,
                                   &fFound));

        if (fFound)
        {
            fMove = TRUE;

            /* if this is the first of >= 1 adjacent blocks, cache the offset of the beginning  */

            if (ibDest == COA_BLOCK_NOT_SET)
            {
                ibDest = pcontextSlot->dwSlotPosInBlock;

                /* save the first slot to be affected by the move */

                MEMCPY(f_pcontextSlotLock, pcontextSlot, cbSlotContext);
                /* Set the pointers within the copied context that refer back to the source context,
                   since we will be freeing the source context at the end of this function */
                f_pcontextSlotLock->pCurrChildBlock = (_CommBlockHDR *)f_pcontextSlotLock->bBuff;
                f_pcontextSlotLock->pCurrDataBlock  = (_CommBlockHDR *) (f_pcontextSlotLock->bBuff + __CB_DECL(GetMemsize_ChildBlock(f_pblockheader->pNS)));


                ChkDR(_HdsLockBlock2DeleteSlot(f_pcontextSlotLock, f_lockmode));

               *f_fLocked = TRUE;
            }

            /* if this isn't contiguous with the last hidden block then close that previous hole 
            ** and continue from where we are
            */

            else if (pcontextSlot->dwSlotPosInBlock != ibNext)
            {
                DRM_DWORD cbMove = pcontextSlot->dwSlotPosInBlock - ibNext;

                ChkDR(_HdsAdjustChildPayload(pcontextSlot->pCurrChildBlock, 
                                             ibDest, 
                                             ibSource,
                                             cbMove));

                /* the new destination is the end of the blocks just moved */

                ibDest += cbMove;
            }

            /* remove _DataBlockHDR's, if necessary */

            if (ISOVERSIZESLOT(pcontextSlot->oSlotHeader.dwSlotSize, 
                               pcontextSlot->pNS))
            {
                DRM_DWORD nBlockData = pcontextSlot->dwFirstDataBlockNum;

                ibSource = pcontextSlot->dwSlotPosInBlock 
                         + SIZEOF(_SlotHeader) 
                         + SIZEOF(DRM_DWORD);

                while (nBlockData > 0)
                {
                    ChkDR(_HdsLoadBlockHDR(pcontextSlot->pNS, 
                                           nBlockData, 
                                          &pcontextSlot->pCurrDataBlock));

                    nBlockData = ((_DataBlockHDR *)(pcontextSlot->pCurrDataBlock))->File._image.nCascadingBlockNum;

                    ChkDR(_HdsFreeBlock(pcontextSlot->pCurrDataBlock));
                }
            }
            else
            {
                ibSource = pcontextSlot->dwSlotPosInBlock 
                         + pcontextSlot->oSlotHeader.dwSlotSize
                         + SIZEOF(_SlotHeader);
            }

            ibNext = ibSource;
        } /* endif found a hidden slot */
    }                                                           

    /* if we have an unprocessed move do it now */

    if (fMove)
    {
        /* move up the remaining space and update the free space */

        ChkDR(_HdsAdjustChildPayload(f_pblockheader, 
                                     ibDest, 
                                     ibSource, 
                                     ADJUST_PAYLOAD_TO_END));

        /* remove slot in _ChildBlockHDR */

        ChkDR(_HdsWriteBlockHDR     (f_pblockheader));
    }

ErrorExit:

    if (pcontextSlot != NULL)
    {
        _Hds_free(f_pblockheader->pNS->pHDS, pcontextSlot);
    }

    return dr;
}


