//
//  Copyright (C) 2000, Microsoft Corporation
//
//  File:       DfsADBlobCache.cxx
//
//  Contents:   the ADBlob DFS Store class, this contains the 
//              old AD blob store specific functionality. 
//
//  Classes:    DfsADBlobCache.
//
//  History:    Dec. 8 2000,   Author: udayh
//              April 9 2001  Rohanp - Added  AD specific code.
//
//-----------------------------------------------------------------------------
  
#include <DfsAdBlobCache.hxx>
#include <dfsrootfolder.hxx>
#include <dfserror.hxx>
#include "dfsadsiapi.hxx"
#include <sddl.h>
#include <dfssecurity.h>
#include <ntdsapi.h>
#include "dfsAdBlobCache.tmh"


DfsADBlobCache::DfsADBlobCache(
    DFSSTATUS *pStatus, 
    PUNICODE_STRING pShareName,
    DfsRootFolder * pRootFolder) :  DfsGeneric(DFS_OBJECT_TYPE_ADBLOB_CACHE)
{
    SHASH_FUNCTABLE FunctionTable;
    NTSTATUS NtStatus = STATUS_SUCCESS;
    DFSSTATUS Status = ERROR_SUCCESS;
    
    *pStatus = ERROR_SUCCESS;

    do {
        m_pBlob = NULL;
        m_pRootBlob = NULL;
        m_pTable = NULL;

        m_pAdHandle = NULL;
        m_AdReferenceCount = 0;
        m_RefreshCount = 0;
        m_ErrorOccured = DFS_DS_NOERROR;

        m_pRootFolder = pRootFolder;

        RtlInitUnicodeString(&m_LogicalShare, NULL);
        RtlInitUnicodeString(&m_ObjectDN, NULL);

        ZeroMemory(&FunctionTable, sizeof(FunctionTable));
        
        FunctionTable.AllocFunc = AllocateShashData;
        FunctionTable.FreeFunc = DeallocateShashData;

        ZeroMemory(&m_BlobAttributePktGuid, sizeof(GUID));
        
        m_fCritInit = InitializeCriticalSectionAndSpinCount( &m_Lock, DFS_CRIT_SPIN_COUNT);
        if(!m_fCritInit)
        {
            Status = GetLastError();
            break;
        }
        
        Status = DfsCreateUnicodeString(&m_LogicalShare, pShareName);

        if (Status != ERROR_SUCCESS)
        {
            break;
        }   

        Status = SetupObjectDN();

        if(Status != ERROR_SUCCESS)
        {
            if(DfsServerGlobalData.bDfsAdAlive)
            {
                DfsServerGlobalData.bDfsAdAlive = FALSE;
                DfsLogDfsEvent(DFS_ERROR_ACTIVEDIRECTORY_OFFLINE, 0, NULL, 0); 
            }
            break;
        }
        
        
        if(DfsServerGlobalData.bDfsAdAlive == FALSE)
        {
            DfsServerGlobalData.bDfsAdAlive = TRUE;
            DfsLogDfsEvent(DFS_INFO_ACTIVEDIRECTORY_ONLINE, 0, NULL, 0); 
        }

        NtStatus = ShashInitHashTable(&m_pTable, &FunctionTable);
        if (NtStatus != STATUS_SUCCESS)
        {
            Status = RtlNtStatusToDosError(NtStatus);
            break;
        }
        
    } while (FALSE);

    *pStatus = Status;
    
    return;
}


DfsADBlobCache::~DfsADBlobCache() 
{

    if(m_pBlob)
    {
        DeallocateShashData(m_pBlob);
        m_pBlob = NULL;
    }
    if(m_pRootBlob)
    {
        DeallocateShashData(m_pRootBlob);
        m_pRootBlob = NULL;
    }

    if (m_pTable != NULL)
    {
        InvalidateCache();
        ShashTerminateHashTable(m_pTable);
        m_pTable = NULL;
    }

    if (m_LogicalShare.Buffer != NULL)
    {
        delete [] m_LogicalShare.Buffer;
        m_LogicalShare.Buffer = NULL;
    }
    if (m_ObjectDN.Buffer != NULL)
    {
        delete [] m_ObjectDN.Buffer;
        m_ObjectDN.Buffer = NULL;
    }

    if(m_fCritInit)
    {
        DeleteCriticalSection(&m_Lock);
        m_fCritInit = FALSE;
    }

}    





#ifndef DFS_USE_LDAP


DFSSTATUS
DfsADBlobCache::UpdateCacheWithDSBlob(
    PVOID pHandle )
{
    HRESULT hr = S_OK;
    DFSSTATUS Status = ERROR_SUCCESS;
    BYTE *pBuffer = NULL;
    ULONG Length = 0;
    VARIANT BinaryBlob;

    IADs *pADs  = (IADs *)pHandle;

    DFS_TRACE_LOW( ADBLOB, "Cache %p: updating cache with blob \n", this);
    VariantInit(&BinaryBlob);

    hr = pADs->Get(ADBlobAttribute, &BinaryBlob);
    if ( SUCCEEDED(hr) )
    {
        Status = GetBinaryFromVariant( &BinaryBlob, &pBuffer, &Length );
        if (Status == ERROR_SUCCESS)
        {
            Status = UnpackBlob( pBuffer, &Length, NULL );

            DFS_TRACE_ERROR_LOW( Status, REFERRAL_SERVER, "Unpack blob done with status %x\n", Status);
            
            delete [] pBuffer;
        }
    }
    else
    {
        Status = DfsGetErrorFromHr(hr);
    }
    VariantClear(&BinaryBlob);

    DFS_TRACE_ERROR_LOW( Status, ADBLOB, "cache %p: Updated cache (length %x) status %x\n", 
                         this, Length, Status);

    return Status;
}

DFSSTATUS
DfsADBlobCache::GetObjectPktGuid( 
    PVOID pHandle,
    GUID *pGuid )
{
    HRESULT hr = S_OK;
    DFSSTATUS Status = ERROR_SUCCESS;
    BYTE *pBuffer = NULL;
    ULONG Length = 0;
    VARIANT BinaryBlob;

    IADs *pADs = (IADs *)pHandle;

    DFS_TRACE_LOW( ADBLOB, "Cache %p: getting pkt guid\n", this);

    VariantInit(&BinaryBlob);

    hr = pADs->Get(ADBlobPktGuidAttribute, &BinaryBlob);
    if ( SUCCEEDED(hr) )
    {
        Status = GetBinaryFromVariant( &BinaryBlob, &pBuffer, &Length );
        if (Status == ERROR_SUCCESS)
        {
            if (Length > sizeof(GUID)) Length = sizeof(GUID);

            RtlCopyMemory( pGuid, pBuffer, Length);
            
            delete [] pBuffer;
        }
    }
    VariantClear(&BinaryBlob);

    Status = DfsGetErrorFromHr(hr);

    DFS_TRACE_ERROR_LOW( Status, ADBLOB, "cache %p: got pkt guid, Status %x\n", 
                         this, Status );


    return Status;
}


DFSSTATUS 
DfsADBlobCache::UpdateDSBlobFromCache(
    PVOID pHandle,
    GUID *pGuid )
{
    HRESULT HResult = S_OK;
    DFSSTATUS Status = ERROR_SUCCESS;
    BYTE *pBuffer = NULL;
    ULONG Length;
    ULONG UseLength;
    ULONG TotalBlobBytes = 0;
    VARIANT BinaryBlob;
    IADs *pObject = (IADs *)pHandle;

    VariantInit(&BinaryBlob);

    DFS_TRACE_LOW( ADBLOB, "Cache %p: updating ds with cache \n", this);

    UseLength = ADBlobDefaultBlobPackSize;
retry:
    Length = UseLength;
    pBuffer =  (BYTE *) HeapAlloc(GetProcessHeap(), 0, 
                                  Length );
    if(pBuffer != NULL)
    {
        Status = PackBlob(pBuffer, &Length, &TotalBlobBytes); 
        if(Status == STATUS_SUCCESS)
        {
            Status = PutBinaryIntoVariant(&BinaryBlob, pBuffer, TotalBlobBytes);
            if(Status == STATUS_SUCCESS)
            {
                HResult = pObject->Put(ADBlobAttribute, BinaryBlob);
                if (SUCCEEDED(HResult) )
                {
                    HResult = pObject->SetInfo();
                }

                Status = DfsGetErrorFromHr(HResult);

                if (Status == ERROR_SUCCESS)
                {
                    Status = SetObjectPktGuid( pObject, pGuid );
                }
            }
        }
        DFS_TRACE_ERROR_LOW(Status, ADBLOB, "Cache %p: update ds (Buffer Len %x, Length %x) Status %x\n",
                            this, UseLength, Length, Status);

        HeapFree(GetProcessHeap(), 0, pBuffer);

        if (Status == ERROR_BUFFER_OVERFLOW)
        {
            if (UseLength < ADBlobMaximumBlobPackSize)
            {
                UseLength *= 2;
            }
            //
            // If we are still within the maximum bounds, retry.
            //
            if (UseLength <= ADBlobMaximumBlobPackSize)
            {
                goto retry;
            }
        }
    }
    else
    {
        Status = ERROR_NOT_ENOUGH_MEMORY;
    }

    VariantClear(&BinaryBlob);
    DFS_TRACE_ERROR_LOW(Status, ADBLOB, "Cache %p: update ds done (Buffer Length %x, Length %x) Status %x\n",
                        this, UseLength, Length, Status);

    return Status;
}


DFSSTATUS
DfsADBlobCache::SetObjectPktGuid( 
    IADs *pObject,
    GUID *pGuid )
{
    HRESULT HResult = S_OK;
    DFSSTATUS Status = ERROR_SUCCESS;
    VARIANT BinaryBlob;

    DFS_TRACE_LOW( ADBLOB, "Cache %p: setting pkt guid\n", this);
    VariantInit(&BinaryBlob);

    Status = PutBinaryIntoVariant( &BinaryBlob, (PBYTE)pGuid, sizeof(GUID));
    if (Status == ERROR_SUCCESS)
    {
        HResult = pObject->Put(ADBlobPktGuidAttribute, BinaryBlob);
        if (SUCCEEDED(HResult) )
        {
            HResult = pObject->SetInfo();
        }

        Status = DfsGetErrorFromHr(HResult);
    }

    VariantClear(&BinaryBlob);

    DFS_TRACE_ERROR_LOW( Status, ADBLOB, "cache %p: set pkt guid, Status %x\n", 
                         this, Status );


    return Status;
}

#else


DFSSTATUS
DfsADBlobCache::DfsLdapConnect(
    LPWSTR DCName,
    LDAP **ppLdap )
{
    LDAP *pLdap = NULL;
    LPWSTR ActualDC = NULL;
    DFSSTATUS Status = ERROR_SUCCESS;
    DFSSTATUS LocalStatus = ERROR_SUCCESS;
    LONG PreviousState = DFS_DS_ACTIVE;
    const TCHAR * apszSubStrings[4];

    apszSubStrings[0] = DCName;
    ActualDC = DCName;

    pLdap = ldap_initW(DCName, LDAP_PORT);
    if (pLdap != NULL)
    {
        Status = ldap_set_option(pLdap, LDAP_OPT_AREC_EXCLUSIVE, LDAP_OPT_ON);

        if (Status == LDAP_SUCCESS)
        {
            UNICODE_STRING DnsDomain;
            if (DfsGetDnsDomainName( &DnsDomain) == ERROR_SUCCESS)
            {
                Status = ldap_set_option(pLdap, LDAP_OPT_DNSDOMAIN_NAME, &DnsDomain.Buffer);

                DfsReleaseDomainName(&DnsDomain);
            }
        }
        if (Status == LDAP_SUCCESS)
        {

            DFS_TRACE_LOW(ADBLOB, 
                          "Ldap Connect, calling LDAP Bind DC: %ws, init ldap status %x\n",
                           DCName, Status);

            Status = ldap_bind_s(pLdap, NULL, NULL, LDAP_AUTH_NEGOTIATE);


            DFS_TRACE_LOW(ADBLOB, 
                          "Ldap Connect, returning from LDAP Bind DC: %ws, init ldap status %x\n",
                          DCName, Status);
        }
    }
    else
    {
        Status = LdapGetLastError();
        DFS_TRACE_ERROR_HIGH(Status, ADBLOB, 
                             "Ldap Connect, DC: %ws, init ldap status %x\n",
                             DCName, Status);
    }

    if (Status == LDAP_SUCCESS)
    {
        *ppLdap = pLdap;

        if(DCName == NULL)
        {

            //Get the actual host name that we connected to. We don't have to free
            //this name since it will get freed when ldap_unbind is called.
            LocalStatus = ldap_get_optionW(pLdap, LDAP_OPT_HOST_NAME , (void *) &ActualDC);
            apszSubStrings[0] = ActualDC;
        }

        DFS_TRACE_HIGH(ADBLOB, "ADBLOBConnected to actual DC: %ws, ldap status %x\n",
                       ActualDC, Status);

        PreviousState = InterlockedCompareExchange(&DfsServerGlobalData.FirstContact, DFS_DS_ACTIVE, DFS_DS_NOTACTIVE); 
        if(PreviousState == DFS_DS_NOTACTIVE)
        {
            DfsLogDfsEvent(DFS_INFO_DS_RECONNECTED, 1, apszSubStrings, Status);
        }
    }
    else
    {
        PreviousState = InterlockedCompareExchange(&DfsServerGlobalData.FirstContact, DFS_DS_NOTACTIVE, DFS_DS_ACTIVE); 
        if(PreviousState == DFS_DS_ACTIVE)
        {
            DfsLogDfsEvent(DFS_ERROR_DSCONNECT_FAILED, 1, apszSubStrings, Status); 
        }

        DFS_TRACE_ERROR_HIGH(Status, ADBLOB, 
                             "Ldap Connect, DC: %ws, ldap status %x\n",
                             DCName, Status);

        Status = LdapMapErrorToWin32(Status);
    }


    if (Status != ERROR_SUCCESS) 
    {
        if (pLdap != NULL)
        {
            //
            // 565302
            // we need to call ldap_unbind if we have a valid connection!
            // Note that this is true even if ldap_bind failed or we never
            // even called ldap_bind.
            //
            ldap_unbind(pLdap);
        }
    }

    return Status;

}



VOID
DfsADBlobCache::DfsLdapDisconnect(
    LDAP *pLdap )
{
    ldap_unbind(pLdap);
}


DFSSTATUS
DfsADBlobCache::DfsGetPktBlob(
    LDAP *pLdap,
    LPWSTR ObjectDN,
    PVOID *ppBlob,
    PULONG pBlobSize,
    PVOID *ppHandle,
    PVOID *ppHandle1 )
{

    DFSSTATUS Status = ERROR_SUCCESS;
    DFSSTATUS LdapStatus = ERROR_SUCCESS;
    PLDAPMessage pLdapSearchedObject = NULL;
    PLDAPMessage pLdapObject = NULL;
    PLDAP_BERVAL *pLdapPktAttr = NULL;
    LPWSTR Attributes[2];
    struct l_timeval Timeout;



    Status = ERROR_ACCESS_DENIED;     // fix this after we understand
                                      // ldap error correctly. When
                                      // ldap_get_values_len returns NULL
                                      // the old code return no more mem.

    Attributes[0] = ADBlobAttribute;
    Attributes[1] = NULL;

    Timeout.tv_sec = DfsServerGlobalData.LdapTimeOut;


    DFS_TRACE_LOW(ADBLOB, 
                 "DfsGetPktBlob, calling LDAP Search ObjectDN: %ws, init ldap status %x\n",
                  ObjectDN, Status);
    LdapStatus = ldap_search_ext_sW( pLdap,
                                     ObjectDN,
                                     LDAP_SCOPE_BASE,
                                     L"(objectClass=*)",
                                     Attributes,
                                     0,            // attributes only
                                     NULL,         // server controls
                                     NULL,         // client controls
                                     &Timeout,
                                     0,            // size limit
                                     &pLdapSearchedObject);

    DFS_TRACE_LOW(ADBLOB, 
                 "DfsGetPktBlob, returning from LDAP Search ObjectDN: %ws, init ldap status %x\n",
                  ObjectDN, Status);
    if (LdapStatus == LDAP_SUCCESS)
    {
        pLdapObject = ldap_first_entry( pLdap,
                                        pLdapSearchedObject );

        if (pLdapObject != NULL)
        {
            pLdapPktAttr = ldap_get_values_len( pLdap,
                                                pLdapObject,
                                                Attributes[0] );
            if (pLdapPktAttr != NULL)
            {
                *ppBlob = pLdapPktAttr[0]->bv_val;
                *pBlobSize = pLdapPktAttr[0]->bv_len;

                *ppHandle = (PVOID)pLdapPktAttr;
                *ppHandle1 = (PVOID)pLdapSearchedObject;
                pLdapSearchedObject = NULL;

                Status = ERROR_SUCCESS;
            }
            else
            {
                if(pLdap->ld_errno == LDAP_NO_SUCH_ATTRIBUTE)
                {
                    Status = ERROR_ACCESS_DENIED;
                }
                else
                {
                    Status = LdapMapErrorToWin32( pLdap->ld_errno );
                }


                DFS_TRACE_ERROR_HIGH( Status, ADBLOB, "DfsADBlobCache::DfsGetPktBlob1 (ldap error %x) status %x\n", 
                                      pLdap->ld_errno, Status);
            }
        }
    }
    else
    {
        Status = LdapMapErrorToWin32(LdapStatus);

        DFS_TRACE_ERROR_HIGH( Status, ADBLOB, "DfsADBlobCache::DfsGetPktBlob2 (ldap error %x) status %x\n", 
                              LdapStatus, Status);
    }

    if (pLdapSearchedObject != NULL)
    {
        ldap_msgfree( pLdapSearchedObject );
    }

    return Status;
}

VOID
DfsADBlobCache::DfsReleasePktBlob(
    PVOID pHandle,
    PVOID pHandle1 )
{
    PLDAP_BERVAL *pLdapPktAttr = (PLDAP_BERVAL *)pHandle;
    PLDAPMessage pLdapSearchedObject = (PLDAPMessage)pHandle1;

    ldap_value_free_len( pLdapPktAttr );
    ldap_msgfree( pLdapSearchedObject );
}


DFSSTATUS
DfsADBlobCache::UpdateCacheWithDSBlob(
    PVOID pHandle )

{
    DFSSTATUS Status = ERROR_SUCCESS;
    BYTE *pBuffer = NULL;
    ULONG Length = 0;
    PVOID pHandle1, pHandle2;

    LDAP *pLdap = (LDAP *)pHandle;
    UNICODE_STRING ObjectDN;

    
    DFS_TRACE_LOW( ADBLOB, "Cache %p: updating cache with blob \n", this);

    Status = GetObjectDN(&ObjectDN);

    if (Status == ERROR_SUCCESS)
    {
        Status = DfsGetPktBlob( pLdap,
                                ObjectDN.Buffer,
                                (PVOID *)&pBuffer,
                                &Length,
                                &pHandle1,
                                &pHandle2);
    }

    if ( Status == ERROR_SUCCESS)
    {
        Status = UnpackBlob( pBuffer, &Length, NULL );

        DFS_TRACE_ERROR_LOW( Status, REFERRAL_SERVER, "Unpack blob done with status %x\n", Status);

        DfsReleasePktBlob( pHandle1, pHandle2);
    }

    DFS_TRACE_ERROR_LOW( Status, ADBLOB, "cache %p: Updated cache (length %x) status %x\n", 
                         this, Length, Status);

    return Status;
}




DFSSTATUS
DfsADBlobCache::GetObjectPktGuid(
    PVOID pHandle,
    GUID *pGuid )
{

    DFSSTATUS Status = ERROR_SUCCESS;
    DFSSTATUS LdapStatus = ERROR_SUCCESS;
    PLDAPMessage pLdapSearchedObject = NULL;
    PLDAPMessage pLdapObject = NULL;
    PLDAP_BERVAL *pLdapGuidAttr = NULL;
    ULONG CopySize = 0;    
    LDAP *pLdap = (LDAP *)pHandle;
    UNICODE_STRING ObjectDN;
    struct l_timeval Timeout;
    LPWSTR Attributes[2];

    Status = GetObjectDN(&ObjectDN);
    if (Status != ERROR_SUCCESS)
    {
        return Status;
    }

    Status = ERROR_ACCESS_DENIED;     // fix this after we understand
                                      // ldap error correctly. When
                                      // ldap_get_values_len returns NULL
                                      // the old code return no more mem.


    Attributes[0] = ADBlobPktGuidAttribute;
    Attributes[1] = NULL;

    Timeout.tv_sec = DfsServerGlobalData.LdapTimeOut;



    DFS_TRACE_LOW(ADBLOB, 
                 "DfsGetPktGuid, calling LDAP Search - ldap status %x\n",
                  Status);
    LdapStatus = ldap_search_ext_sW( pLdap,
                                     ObjectDN.Buffer,
                                     LDAP_SCOPE_BASE,
                                     L"(objectClass=*)",
                                     Attributes,
                                     0,            // attributes only
                                     NULL,         // server controls
                                     NULL,         // client controls
                                     &Timeout,
                                     0,            // size limit
                                     &pLdapSearchedObject);

    DFS_TRACE_LOW(ADBLOB, 
                 "DfsGetPktGuid, returning from LDAP Search - ldap status %x\n",
                  Status);

    if (LdapStatus == LDAP_SUCCESS)
    {
        pLdapObject = ldap_first_entry( pLdap,
                                        pLdapSearchedObject );

        if (pLdapObject != NULL)
        {
            pLdapGuidAttr = ldap_get_values_len( pLdap,
                                                 pLdapObject,
                                                 Attributes[0] );
            if (pLdapGuidAttr != NULL)
            {
                CopySize = min( pLdapGuidAttr[0]->bv_len, sizeof(GUID));
                RtlCopyMemory( pGuid, pLdapGuidAttr[0]->bv_val, CopySize );

                ldap_value_free_len( pLdapGuidAttr );
                Status = ERROR_SUCCESS;
            }
            else
            {
                if(pLdap->ld_errno == LDAP_NO_SUCH_ATTRIBUTE)
                {
                    Status = ERROR_ACCESS_DENIED;
                }
                else
                {
                    Status = LdapMapErrorToWin32( pLdap->ld_errno );
                }


                DFS_TRACE_ERROR_HIGH( Status, ADBLOB, "DfsADBlobCache::GetObjectPktGuid1 (ldap error %x) status %x\n", 
                                      LdapStatus, Status);
            }
        }
    }
    else
    {
        Status = LdapMapErrorToWin32(LdapStatus);

        DFS_TRACE_ERROR_HIGH( Status, ADBLOB, "DfsADBlobCache::GetObjectPktGuid2 (ldap error %x) status %x\n", 
                              pLdap->ld_errno, Status);
    }

    if (pLdapSearchedObject != NULL)
    {
        ldap_msgfree( pLdapSearchedObject );
    }

    return Status;
}

DFSSTATUS
DfsADBlobCache::DfsSetPktBlobAndPktGuid ( 
    LDAP *pLdap,
    LPWSTR ObjectDN,
    PVOID pBlob,
    ULONG BlobSize,
    GUID *pGuid )
{
    LDAP_BERVAL  LdapPkt, LdapPktGuid;
    PLDAP_BERVAL  pLdapPktValues[2], pLdapPktGuidValues[2];
    LDAPModW LdapPktMod, LdapPktGuidMod;
    PLDAPModW pLdapDfsMod[3];
    DFSSTATUS Status = ERROR_SUCCESS;
    DFSSTATUS LdapStatus;

    LDAPControlW    LazyCommitControl =
                    {
                        LDAP_SERVER_LAZY_COMMIT_OID_W,  // the control
                        { 0, NULL},                     // no associated data
                        FALSE                           // control isn't mandatory
                    };

    PLDAPControlW   ServerControls[2] =
                    {
                        &LazyCommitControl,
                        NULL
                    };


    LdapPkt.bv_len = BlobSize;
    LdapPkt.bv_val = (PCHAR)pBlob;
    pLdapPktValues[0] = &LdapPkt;
    pLdapPktValues[1] = NULL;
    LdapPktMod.mod_op = LDAP_MOD_REPLACE | LDAP_MOD_BVALUES;
    LdapPktMod.mod_type = ADBlobAttribute;
    LdapPktMod.mod_vals.modv_bvals = pLdapPktValues;

    LdapPktGuid.bv_len = sizeof(GUID);
    LdapPktGuid.bv_val = (PCHAR)pGuid;
    pLdapPktGuidValues[0] = &LdapPktGuid;
    pLdapPktGuidValues[1] = NULL;
    LdapPktGuidMod.mod_op = LDAP_MOD_REPLACE | LDAP_MOD_BVALUES;
    LdapPktGuidMod.mod_type = ADBlobPktGuidAttribute;
    LdapPktGuidMod.mod_vals.modv_bvals = pLdapPktGuidValues;

    pLdapDfsMod[0] = &LdapPktMod;
    pLdapDfsMod[1] = &LdapPktGuidMod;
    pLdapDfsMod[2] = NULL;


    DFS_TRACE_LOW(ADBLOB, 
                 "DfsSetPktBlobAndPktGuid, calling LDAP ldap_modify_ext_sW ObjectDN: %ws, init ldap status %x\n",
                  ObjectDN, Status);
    LdapStatus = ldap_modify_ext_sW( pLdap,
                                     ObjectDN,
                                     pLdapDfsMod,
                                     (PLDAPControlW *)ServerControls,
                                     NULL );

    DFS_TRACE_LOW(ADBLOB, 
                 "DfsSetPktBlobAndPktGuid, returning from LDAP ldap_modify_ext_sW ObjectDN: %ws, init ldap status %x\n",
                  ObjectDN, Status);

    if (LdapStatus != LDAP_SUCCESS)
    {
        Status = LdapMapErrorToWin32(LdapStatus);
    }



    DFS_TRACE_ERROR_LOW( Status, ADBLOB, "DfsADBlobCache::DfsSetPktBlobAndPktGuid cache %p: LDAP Status %x, Win32 status %x\n", 
                         this, LdapStatus, Status);
    return Status;

}

DFSSTATUS 
DfsADBlobCache::UpdateDSBlobFromCache(
    PVOID pHandle,
    GUID *pGuid )
{
    LDAP *pLdap = (LDAP *)pHandle;
    UNICODE_STRING ObjectDN;
    BYTE *pBuffer = NULL;
    ULONG Length = 0;
    ULONG UseLength = 0;
    ULONG TotalBlobBytes = 0;
    DFSSTATUS Status = ERROR_SUCCESS;

    Status = GetObjectDN(&ObjectDN);
    if (Status != ERROR_SUCCESS)
    {
        DFS_TRACE_ERROR_LOW( Status, ADBLOB, "DfsADBlobCache::UpdateDSBlobFromCache cache %p: GetObjectDN failed status %x\n", 
                             this, Status);
        return Status;
    }

    UseLength = ADBlobDefaultBlobPackSize;
retry:
    Length = UseLength;
    pBuffer =  (BYTE *) HeapAlloc(GetProcessHeap(), 0, Length );
    if(pBuffer != NULL)
    {
        Status = PackBlob(pBuffer, &Length, &TotalBlobBytes); 
        if (Status != ERROR_SUCCESS)
        {
            HeapFree(GetProcessHeap(), 0, pBuffer);

            if (Status == ERROR_BUFFER_OVERFLOW)
            {
                if (UseLength < ADBlobMaximumBlobPackSize)
                {
                    UseLength *= 2;
                }
                goto retry;
            }
        }

        if(Status == STATUS_SUCCESS)
        {

            Status = DfsSetPktBlobAndPktGuid( pLdap,
                                              ObjectDN.Buffer,
                                              pBuffer,
                                              TotalBlobBytes,
                                              pGuid );
            DFS_TRACE_ERROR_LOW(Status, ADBLOB, "Cache %p: DfsADBlobCache::UpdateDSBlobFromCache (Buffer Len %x, Length %x) Status %x\n",
                                this, UseLength, Length, Status);


            HeapFree(GetProcessHeap(), 0, pBuffer);
        }
    }
    else
    {
        Status = ERROR_NOT_ENOUGH_MEMORY;
    }
    return Status;
}
#endif

DFSSTATUS
DfsADBlobCache::CacheRefresh(BOOLEAN fForceSync,
                             BOOLEAN fFromPDC)

{
    PVOID           pHandle = NULL;
    DFSSTATUS       Status = ERROR_SUCCESS;
    LONG            PreviousState = DFS_DS_NOERROR;
    LONG            RefreshCount = 0;
    PUNICODE_STRING pUseShare = NULL;
    GUID            CurrentGuid;
    const TCHAR * apszSubStrings[4];

    LPWSTR UseDC = NULL;
    DfsString *pPDC = NULL;
    DFSSTATUS PDCStatus = ERROR_SUCCESS;



    DFS_TRACE_LOW( ADBLOB, "Cache %p: from pdc? %d cache refresh\n", this, fFromPDC);

    if (fFromPDC == TRUE) 
    {
        PDCStatus = DfsGetBlobPDCName( &pPDC, 0 );
        //
        // Ignore the status here. If this call fails, we dont care
        // since we will just use a non-pdc dc. All callers who 
        // *really* care have already got the object with the right dc.
        // see apiprologue for more details.
        //
        if (PDCStatus == ERROR_SUCCESS) 
        {
            UseDC = pPDC->GetString();
        }
    }

    DFS_TRACE_LOW(ADBLOB, "Cache refresh using dc %ws\n", UseDC);

    Status = GetADObject( &pHandle, UseDC);
    
    if ((Status != ERROR_SUCCESS) &&
        (UseDC != NULL))
    {
        //
        // hmmm. failed here going to pdc. Just get the object without bothering
        // about the PDC.
        //
        Status = GetADObject(&pHandle, NULL);

    }

    if (Status == ERROR_SUCCESS)
    {
        // fFromPDC == FALSE means root scalability mode..every so often, get a new
        // Blob and guid
        if(fFromPDC == FALSE)
        {
            RefreshCount = InterlockedIncrement(&m_RefreshCount);
            if(RefreshCount >= DFS_ROOTSCALABILTY_FORCED_REFRESH_INTERVAL)
            {
                fForceSync = TRUE;
                InterlockedExchange((LPLONG volatile ) &m_RefreshCount, 0);
            }
        }

        Status = GetObjectPktGuid( pHandle, &CurrentGuid );

        if(Status == ERROR_SUCCESS)
        {
            if(!fForceSync)
            {
                //
                // here we pass the 2 guids by reference...
                //
                if (IsEqualGUID( CurrentGuid, m_BlobAttributePktGuid) == FALSE)
                {
                    Status = UpdateCacheWithDSBlob( pHandle );
                    if (Status == ERROR_SUCCESS)
                    {
                        m_BlobAttributePktGuid = CurrentGuid;
                    }
                }
            }
            else
            {
                Status = UpdateCacheWithDSBlob( pHandle );
                if (Status == ERROR_SUCCESS)
                {
                    m_BlobAttributePktGuid = CurrentGuid;
                }
            }
        }

        ReleaseADObject( pHandle );

    }

    DfsReleaseBlobPDCName( pPDC );

    if(Status != ERROR_SUCCESS)
    {
        PreviousState = InterlockedCompareExchange(&m_ErrorOccured, DFS_DS_ERROR, DFS_DS_NOERROR); 
        if(PreviousState == DFS_DS_NOERROR)
        {
            pUseShare = m_pRootFolder->GetRootPhysicalShareName();
            apszSubStrings[0] = pUseShare->Buffer;
            DfsLogDfsEvent(DFS_ERROR_NO_DFS_DATA, 1, apszSubStrings, Status);
        }
    }
    else
    {
        PreviousState = InterlockedCompareExchange(&m_ErrorOccured, DFS_DS_NOERROR, DFS_DS_ERROR); 
        if(PreviousState == DFS_DS_ERROR)
        {
            pUseShare  = m_pRootFolder->GetRootPhysicalShareName();
            apszSubStrings[0] = pUseShare->Buffer;
            DfsLogDfsEvent(DFS_INFO_RECONNECT_DATA, 1, apszSubStrings, Status);
        }

    }
    
    DFS_TRACE_ERROR_LOW( Status, ADBLOB, "Cache %p: CacheRefresh status 0x%x\n", this, Status);
    return Status;
}



void 
DfsADBlobCache::InvalidateCache()
{
    PDFSBLOB_DATA pBlobData;
    DFSBOB_ITER Iter;
    DFS_TRACE_LOW( ADBLOB, "Cache %p: invalidate cache\n", this);
    pBlobData = FindFirstBlob(&Iter);
    while (pBlobData != NULL)
    {
        DFSSTATUS RemoveStatus;

        RemoveStatus = RemoveNamedBlob(&pBlobData->BlobName);
        DFS_TRACE_ERROR_LOW( RemoveStatus, REFERRAL_SERVER, "BlobCache %p, invalidate cache, remove blob status %x\n",
                             this, RemoveStatus);
        pBlobData = FindNextBlob(&Iter);
    }

    FindCloseBlob(&Iter);
    DFS_TRACE_LOW( ADBLOB, "Cache %p: invalidate cache done\n", this);
}

DFSSTATUS
DfsADBlobCache::PutBinaryIntoVariant(VARIANT * ovData, BYTE * pBuf,
                                     unsigned long cBufLen)
{
     DFSSTATUS Status = ERROR_INVALID_PARAMETER;
     void * pArrayData = NULL;
     VARIANT var;
     SAFEARRAYBOUND  rgsabound[1];

     VariantInit(&var);  //Initialize our variant

     var.vt = VT_ARRAY | VT_UI1;

     rgsabound[0].cElements = cBufLen;
     rgsabound[0].lLbound = 0;

     var.parray = SafeArrayCreate(VT_UI1,1,rgsabound);

     if(var.parray != NULL)
     {
        //Get a safe pointer to the array
        SafeArrayAccessData(var.parray,&pArrayData);

        //Copy bitmap to it
        memcpy(pArrayData, pBuf, cBufLen);

        //Unlock the variant data
        SafeArrayUnaccessData(var.parray);

        *ovData = var;  

        Status = STATUS_SUCCESS;
     }
     else
     {
         Status = ERROR_NOT_ENOUGH_MEMORY;
        DFS_TRACE_HIGH( REFERRAL_SERVER, "PutBinaryIntoVariant failed error %d\n", Status);
     }


     return Status;
}



DFSSTATUS
DfsADBlobCache::GetBinaryFromVariant(VARIANT *ovData, BYTE ** ppBuf,
                                     unsigned long * pcBufLen)
{
     DFSSTATUS Status = ERROR_INVALID_PARAMETER;
     void * pArrayData = NULL;

     //Binary data is stored in the variant as an array of unsigned char
     if(ovData->vt == (VT_ARRAY|VT_UI1))  
     {
        //Retrieve size of array
        *pcBufLen = ovData->parray->rgsabound[0].cElements;

        *ppBuf = new BYTE[*pcBufLen]; //Allocate a buffer to store the data
        if(*ppBuf != NULL)
        {
            //Obtain safe pointer to the array
            SafeArrayAccessData(ovData->parray,&pArrayData);

            //Copy the bitmap into our buffer
            memcpy(*ppBuf, pArrayData, *pcBufLen);

            //Unlock the variant data
            SafeArrayUnaccessData(ovData->parray);

            Status = ERROR_SUCCESS;
        }
        else
        {
            Status = ERROR_NOT_ENOUGH_MEMORY;
        }
     }

     return Status;
}


DFSSTATUS 
DfsADBlobCache::CacheFlush(
    PVOID pHandle )
{
    DFSSTATUS Status = ERROR_SUCCESS;
    GUID NewGuid;

    IADs *pObject  = (IADs *)pHandle;

    DFS_TRACE_LOW( ADBLOB, "Cache %p: cache flush\n", this);
    Status = UuidCreate(&NewGuid);
    if (Status == ERROR_SUCCESS)
    {
        m_BlobAttributePktGuid = NewGuid;

        Status = UpdateDSBlobFromCache( pObject,
                                        &NewGuid );

    }
    DFS_TRACE_ERROR_LOW( Status, ADBLOB, "Cache %p: cache flush, Status %x\n", this, Status);
    return Status;
}


DFSSTATUS
DfsADBlobCache::UnpackBlob(
    BYTE *pBuffer,
    PULONG pLength,
    PDFSBLOB_DATA * pRetBlob )
{
    ULONG Discard = 0;
    DFSSTATUS Status = ERROR_SUCCESS;
    BYTE *pUseBuffer = NULL;
    ULONG BufferSize = 0;
    ULONG ObjNdx = 0;
    ULONG TotalObjects = 0;
    ULONG  BlobSize = 0;
    BYTE *BlobBuffer = NULL;
    PDFSBLOB_DATA pLocalBlob = NULL;
    UNICODE_STRING BlobName;
    UNICODE_STRING SiteRoot;
    UNICODE_STRING BlobRoot;
     
    pUseBuffer = pBuffer;
    BufferSize = *pLength;

    DFS_TRACE_LOW( ADBLOB, "BlobCache %p, UnPackBlob \n", this);
    UNREFERENCED_PARAMETER(pRetBlob);

    Status = DfsRtlInitUnicodeStringEx( &SiteRoot, ADBlobSiteRoot );
    if (Status != ERROR_SUCCESS)
    {
        goto done;
    }

    Status = DfsRtlInitUnicodeStringEx( &BlobRoot, ADBlobMetaDataNamePrefix);
    if (Status != ERROR_SUCCESS)
    {
        goto done;
    }

    //
    // Note the size of the whole blob.
    //
    m_BlobSize = BufferSize;
    
    //
    // dfsdev: we should not need an interlocked here: this code
    // is already mutex'd by the caller.
    //
    InterlockedIncrement( &m_CurrentSequenceNumber );
    
    //
    // dfsdev: investigate what the first ulong is and add comment
    // here as to why we are discarding it.
    //
    Status = PackGetULong( &Discard, (PVOID *) &pUseBuffer, &BufferSize ); 
    if (Status != ERROR_SUCCESS)
    {
        goto done;
    }

    if(BufferSize == 0)
    {
        goto done;
    }

    Status = PackGetULong(&TotalObjects, (PVOID *) &pUseBuffer, &BufferSize);
    if (Status != ERROR_SUCCESS) 
    {
        goto done;
    }


    for (ObjNdx = 0; ObjNdx < TotalObjects; ObjNdx++)
    {
        BOOLEAN FoundSite = FALSE;
        BOOLEAN FoundRoot = FALSE;

        Status = GetSubBlob( &BlobName,
                             &BlobBuffer,
                             &BlobSize,
                             &pUseBuffer,
                             &BufferSize );

        if (Status == ERROR_SUCCESS)
        {
            if (!FoundSite &&
               (RtlCompareUnicodeString( &BlobName, &SiteRoot, TRUE ) == 0))
            {
               FoundSite = TRUE;
               Status = CreateBlob(&BlobName, 
                                   BlobBuffer, 
                                   BlobSize,
                                   &pLocalBlob
                                   );
               if(Status == STATUS_SUCCESS)
               {
                   DeallocateShashData(m_pBlob);
                   m_pBlob = pLocalBlob;
               }

                continue;
            }
            if (!FoundRoot &&
               (RtlCompareUnicodeString( &BlobName, &BlobRoot, TRUE ) == 0))
            {
               FoundRoot = TRUE;
               UNICODE_STRING RootName;
               RtlInitUnicodeString(&RootName, NULL);
               Status = CreateBlob(&RootName,
                                   BlobBuffer, 
                                   BlobSize,
                                   &pLocalBlob
                                   );
               if(Status == STATUS_SUCCESS)
               {
                   DeallocateShashData(m_pRootBlob);
                   m_pRootBlob = pLocalBlob;
               }

                continue;
            }

            Status = StoreBlobInCache( &BlobName, BlobBuffer, BlobSize);
            if (Status != ERROR_SUCCESS)
            {
                break;
            }
        }
    }


done:

    DFS_TRACE_ERROR_LOW( Status, ADBLOB, "BlobCache %p: UnPackBlob status %x\n", this, Status);
    return Status;
}


DFSSTATUS
DfsADBlobCache::CreateBlob(PUNICODE_STRING BlobName, 
                           PBYTE pBlobBuffer, 
                           ULONG BlobSize,
                           PDFSBLOB_DATA *pNewBlob )
{
    PDFSBLOB_DATA BlobStructure = NULL;
    DFSSTATUS Status = ERROR_SUCCESS;
    PBYTE NextBufOffset;

    ULONG TotalSize = sizeof(DFSBLOB_DATA) + 
                      BlobName->Length + sizeof(WCHAR) +
                      BlobSize;

    BlobStructure = (PDFSBLOB_DATA) AllocateShashData( TotalSize );

    if (BlobStructure != NULL)
    {
        RtlZeroMemory(BlobStructure, sizeof(DFSBLOB_DATA));
        NextBufOffset = (PBYTE)(BlobStructure + 1);

        BlobStructure->Header.RefCount = 1;
        BlobStructure->Header.pvKey = &BlobStructure->BlobName;
        BlobStructure->Header.pData = (PVOID)BlobStructure;

        BlobStructure->SequenceNumber = m_CurrentSequenceNumber;        
        BlobStructure->BlobName.Length = BlobName->Length;
        BlobStructure->BlobName.MaximumLength = BlobName->Length + sizeof(WCHAR);
        BlobStructure->BlobName.Buffer = (WCHAR *) (NextBufOffset);

        NextBufOffset = (PBYTE)((ULONG_PTR)(NextBufOffset) + 
                                BlobName->Length + 
                                sizeof(WCHAR));

        if (BlobName->Length != 0)
        {
            RtlCopyMemory(BlobStructure->BlobName.Buffer, 
                          BlobName->Buffer, 
                          BlobName->Length);
        }
        BlobStructure->BlobName.Buffer[BlobName->Length/sizeof(WCHAR)] = UNICODE_NULL;


        BlobStructure->Size = BlobSize;    
        BlobStructure->pBlob = (PBYTE)(NextBufOffset);

        if(pBlobBuffer && BlobSize)
        {
            RtlCopyMemory(BlobStructure->pBlob, pBlobBuffer, BlobSize);
        }
    }
    else
    {
        Status = ERROR_NOT_ENOUGH_MEMORY;
    }
    *pNewBlob = BlobStructure;

    return Status;
}

DFSSTATUS
DfsADBlobCache::CreateSiteBlobIfNecessary(void)
{
    DFSSTATUS Status = ERROR_SUCCESS;
    PDFSBLOB_DATA pSiteBlob = NULL;
    PBYTE pBuffer = NULL;
    PVOID pUseBuffer = NULL;
    ULONG SiteBlobSize = 0;
    ULONG SizeRemaining = 0;
    GUID NewGuid;
    UNICODE_STRING SiteMetadataName;

    if(m_pBlob == NULL)
    {
        do
        {
            Status = UuidCreate(&NewGuid);
            if (Status != ERROR_SUCCESS)
            {
              break;
            }

            Status = DfsRtlInitUnicodeStringEx(&SiteMetadataName, ADBlobSiteRoot);
            if(Status != ERROR_SUCCESS)
            {
                break;
            }

            SiteBlobSize = sizeof(ULONG) + sizeof(GUID);

            pBuffer = (PBYTE) AllocateShashData (SiteBlobSize);
            if (pBuffer == NULL)
            {
                Status = ERROR_NOT_ENOUGH_MEMORY;
                break;
            }

            pUseBuffer = pBuffer;
            SizeRemaining = SiteBlobSize;

            Status = PackSetGuid( &NewGuid, &pUseBuffer, &SizeRemaining);
            if (Status != ERROR_SUCCESS)
            {
                break;
            }

            Status = PackSetULong( 0, &pUseBuffer, &SizeRemaining );
            if (Status != ERROR_SUCCESS)
            {
                break;
            }

            Status = CreateBlob(&SiteMetadataName, 
                                pBuffer, 
                                SiteBlobSize,
                                &pSiteBlob);
            if(Status != ERROR_SUCCESS)
             {
                break;
             }

            m_pBlob = pSiteBlob;

        }while(0);
    }

    if (pBuffer) 
    {
       DeallocateShashData(pBuffer);
    }

    return Status;

}

//
// UpdateSiteBlob: This routine takes a binary stream and stuffs that
// as the site information. It gets rid of the old site information.
// No update is done here, the caller has to call us back to say
// that data needs to be written out.
//
//
DFSSTATUS
DfsADBlobCache::UpdateSiteBlob(PVOID pBuffer, ULONG Size)
{
    DFSSTATUS Status = ERROR_SUCCESS;
    PDFSBLOB_DATA pSiteBlob = NULL;
    UNICODE_STRING SiteMetadataName;

    Status = DfsRtlInitUnicodeStringEx(&SiteMetadataName, ADBlobSiteRoot);
    if(Status == ERROR_SUCCESS)
    {
        Status = CreateBlob(&SiteMetadataName, 
                            (PBYTE)pBuffer,
                            Size,
                            &pSiteBlob);
        if(Status == ERROR_SUCCESS)
        {
            if (m_pBlob != NULL)
            {
                DeallocateShashData(m_pBlob);
            }
            m_pBlob = pSiteBlob;
        }
    }

    return Status;

}


DFSSTATUS
DfsADBlobCache::StoreBlobInCache(PUNICODE_STRING BlobName, 
                                 PBYTE pBlobBuffer, 
                                 ULONG BlobSize)
{
    PDFSBLOB_DATA BlobStructure = NULL;
    NTSTATUS NtStatus = STATUS_SUCCESS;
    DFSSTATUS Status = ERROR_SUCCESS;

    DFS_TRACE_LOW( ADBLOB, "cache %p: Storing Blob %wZ in cache, size %x\n",
                   this, BlobName, BlobSize );

    Status = CreateBlob( BlobName,
                         pBlobBuffer,
                         BlobSize,
                         &BlobStructure );

    if (Status == ERROR_SUCCESS)
    {
        if (IsEmptyString(BlobStructure->BlobName.Buffer))
        {
            ReleaseRootBlob();
            m_pRootBlob = BlobStructure;
        }
        else
        {
            NtStatus = SHashInsertKey(m_pTable, 
                                      BlobStructure, 
                                      &BlobStructure->BlobName, 
                                      SHASH_REPLACE_IFFOUND);
            if(NtStatus == STATUS_SUCCESS)
            {
                InterlockedDecrement(&BlobStructure->Header.RefCount);
            }
            else
            {
                DeallocateShashData( BlobStructure );
                Status = RtlNtStatusToDosError(NtStatus);
            }
        }
    }
    DFS_TRACE_LOW( ADBLOB, "cache %p: storing Blob %wZ done, status %x\n",
                   this, BlobName, Status);

    return Status;
}




DFSSTATUS
DfsADBlobCache::WriteBlobToAd(
    BOOLEAN ForceFlush)
{
    DFSSTATUS Status = STATUS_SUCCESS;
    PVOID pHandle = NULL;

    DFS_TRACE_LOW(ADBLOB, "cache %p: writing blob to ad\n", this);

    //
    //  ForceFlush flag is currently implemented as a DirectMode
    //  specific functionality. Essentially all normal flushes that happen
    //  during direct-mode operations are no-ops unless the ForceFlush
    //  flag is specified. All regular mode operations are left unaffected.
    //
    if (DfsCheckDirectMode() && !ForceFlush) 
    {
        DFS_TRACE_LOW(ADBLOB, "cache %p: WriteBlobToAd is a NOOP\n", this);
        return Status;
    }

    Status = GetCachedADObject ( &pHandle );

    if(Status == ERROR_SUCCESS)
    {
        Status = CacheFlush(pHandle);

        ReleaseADObject( pHandle );
    }
    DFS_TRACE_ERROR_LOW(Status, ADBLOB, "cache %p: writing blob to ad, status %x\n",
                        this, Status);

    return Status;

}



//+-------------------------------------------------------------------------
//
//  Function:   GetSubBlob
//
//  Arguments:    
//
//   PUNICODE_STRING pBlobName (name of the sub blob)
//   BYTE **ppBlobBuffer - holds pointer to sub blob buffer
//   PULONG  pBlobSize -  holds the blob size
//   BYTE **ppBuffer -  holds the pointer to the main blob buffer 
//   PULONG pSize  - holds size of the main blob stream.
//
//  Returns:    Status: Success or Error status code
//
//  Description: This routine reads the next stream in the main blob, and
//               returns all the information necessary to unravel the
//               sub blob held within the main blob,
//               It adjusts the main blob buffer and size appropriately
//               to point to the next stream or sub-blob within the main
//               blob.
//
//--------------------------------------------------------------------------

DFSSTATUS
DfsADBlobCache::GetSubBlob(
    PUNICODE_STRING pName,
    BYTE **ppBlobBuffer,
    PULONG  pBlobSize,
    BYTE **ppBuffer,
    PULONG pSize )
{
    DFSSTATUS Status = ERROR_SUCCESS;

    //
    // ppbuffer is the main blob, and it is point to a stream at this
    // point
    // the first part is the name of the sub-blob.
    //
    Status = PackGetString( pName, (PVOID *) ppBuffer, pSize );
    if (Status == ERROR_SUCCESS)
    {
        //
        // now get the size of the sub blob.
        //
        Status = PackGetULong( pBlobSize, (PVOID *) ppBuffer, pSize );
        if (Status == ERROR_SUCCESS)
        {
            if(*pBlobSize > *pSize)
            {
                Status = ERROR_INVALID_DATA ;
            }
            else
            {

                //
                // At this point the main blob is point to the sub-blob itself.
                // So copy the pointer of the main blob so we can return it
                // as the sub blob.
                //
                *ppBlobBuffer = *ppBuffer;

                //
                // update the main blob pointer to point to the next stream
                // in the blob.

                *ppBuffer = (BYTE *)*ppBuffer + *pBlobSize;
                *pSize -= *pBlobSize;
            }
        }
    }

    return Status;
}


DFSSTATUS
DfsADBlobCache::GetNamedBlob(PUNICODE_STRING pBlobName,
                             PDFSBLOB_DATA *pBlobStructure)
{
    DFSSTATUS Status = ERROR_SUCCESS;
    NTSTATUS NtStatus = STATUS_SUCCESS;

    if (IsEmptyString(pBlobName->Buffer))
    {
        *pBlobStructure = AcquireRootBlob();
        if (*pBlobStructure == NULL)
        {
            Status = ERROR_NOT_FOUND;
        }
    }
    else
    {
        NtStatus = SHashGetDataFromTable(m_pTable, 
                                         (void *)pBlobName,
                                         (void **) pBlobStructure);
        Status = RtlNtStatusToDosError(NtStatus);
    }

    return Status;
}


DFSSTATUS
DfsADBlobCache::SetNamedBlob(PDFSBLOB_DATA pBlobStructure)
{
    NTSTATUS NtStatus = STATUS_SUCCESS;
    DFSSTATUS Status = ERROR_SUCCESS;

    if (IsEmptyString(pBlobStructure->BlobName.Buffer))
    {
        ReleaseRootBlob();
        m_pRootBlob = pBlobStructure;
        AcquireRootBlob();
    }
    else
    {
        NtStatus = SHashInsertKey(m_pTable, 
                                  pBlobStructure, 
                                  &pBlobStructure->BlobName, 
                                  SHASH_REPLACE_IFFOUND);

        Status = RtlNtStatusToDosError(NtStatus);
    }
    
    return Status;
}

DFSSTATUS
DfsADBlobCache::RemoveNamedBlob(PUNICODE_STRING pBlobName )
{
    NTSTATUS NtStatus;
    DFSSTATUS Status = ERROR_SUCCESS;

    if (IsEmptyString(pBlobName->Buffer))
    {
        if (m_pRootBlob == NULL)
        {
            Status = ERROR_NOT_FOUND;
        }
        ReleaseRootBlob();
    }
    else
    {
        NtStatus = SHashRemoveKey(m_pTable, 
                                  pBlobName,
                                  NULL );

        Status = RtlNtStatusToDosError(NtStatus);
    }
    
    return Status;

}

DWORD 
PackBlobEnumerator( PSHASH_HEADER   pEntry,
                    void*  pContext ) 
{
    NTSTATUS Status = STATUS_SUCCESS;
    PUNICODE_STRING pBlobName = (PUNICODE_STRING) pEntry->pvKey;
    PDFSBLOB_DATA pBlobStructure = (PDFSBLOB_DATA) pEntry;
    PPACKBLOB_ENUMCTX pEnumCtx = (PPACKBLOB_ENUMCTX) pContext;

    if (pBlobStructure->SequenceNumber == pEnumCtx->SequenceNumber)
    {
        Status = AddStreamToBlob( pBlobName,
                                  pBlobStructure->pBlob,
                                  pBlobStructure->Size,
                                  &pEnumCtx->pBuffer,
                                  &pEnumCtx->Size );

    
        pEnumCtx->NumItems++;
        pEnumCtx->CurrentSize += (pBlobName->Length + 
                                  sizeof(USHORT) +
                                  sizeof(ULONG) +
                                  pBlobStructure->Size);
    }
    return Status;
}

//
// skeleton of pack blob. 
// The buffer is passed in. The length is passed in.
// If the information does not fit the buffer, return required length.
// Required for API implementation.
//
//
DFSSTATUS
DfsADBlobCache::PackBlob(
    BYTE *pBuffer,
    PULONG pLength,
    PULONG TotalBlobBytes )

{
    ULONG Discard = 0;
    DFSSTATUS Status = ERROR_SUCCESS;
    NTSTATUS NtStatus = STATUS_SUCCESS;
    BYTE *pUseBuffer = NULL;
    BYTE *pSavedBuffer = NULL;
    ULONG SavedBufferSize = 0;
    ULONG BufferSize = 0;
    PACKBLOB_ENUMCTX EnumCtx;
     
    pUseBuffer = pBuffer;
    BufferSize = *pLength;

    DFS_TRACE_LOW(ADBLOB, "BlobCache %p: packing blob\n", this);
    //
    // dfsdev: investigate what the first ulong is and add comment
    // here as to why we are setting it to 0.
    //
    Status = PackSetULong( 0, (PVOID *) &pUseBuffer, &BufferSize ); 
    if (Status == ERROR_SUCCESS)
    {

        //save the place where we should write back the number of objects
        pSavedBuffer = pUseBuffer;
        SavedBufferSize = BufferSize;
        //
        // the next argument is the number of objects in the blob.
        // set 0 until we find how many blobs there are
        //
        Status = PackSetULong(0, (PVOID *) &pUseBuffer, &BufferSize);
    }

    if (Status == ERROR_SUCCESS) 
    {

        EnumCtx.pBuffer = pUseBuffer;
        EnumCtx.Size = BufferSize;
        EnumCtx.NumItems = 0;
        EnumCtx.SequenceNumber = m_CurrentSequenceNumber;
        EnumCtx.CurrentSize = sizeof(ULONG);
    }

    if (Status == ERROR_SUCCESS)
    {
        if (m_pRootBlob != NULL)
        {
            PDFSBLOB_DATA pRootBlob;
            UNICODE_STRING RootMetadataName;

            Status = DfsRtlInitUnicodeStringEx(&RootMetadataName, ADBlobMetaDataNamePrefix);
            if(Status == ERROR_SUCCESS)
            {
                Status = CreateBlob( &RootMetadataName,
                                     m_pRootBlob->pBlob,
                                     m_pRootBlob->Size,
                                     &pRootBlob);

                if (Status == ERROR_SUCCESS)
                {
                    Status = PackBlobEnumerator( (SHASH_HEADER *)pRootBlob, (PVOID) &EnumCtx);    

                    DeallocateShashData(pRootBlob);
                }
            }

        }
    }


    
    if (Status == ERROR_SUCCESS)
    {
        NtStatus = ShashEnumerateItems(m_pTable, 
                                       PackBlobEnumerator, 
                                       &EnumCtx);

        //dfsdev: make sure that shash enumerate retuns NTSTATUS.
        // it does not appear to do so... I think it is the packblobenumerator
        // that is returning a non-ntstatus. Till we fix it dont convert err

        //    Status = RtlNtStatusToDosError(NtStatus);
        Status = NtStatus;
    }

    //
    // Now add the site blob as the LAST blob. It appears that old
    // downlevel dfs may rely on this.
    //
    if (Status == ERROR_SUCCESS) 
    {
        CreateSiteBlobIfNecessary();

        if (m_pBlob != NULL)
        {
            Status = PackBlobEnumerator( (SHASH_HEADER *) m_pBlob, (PVOID) &EnumCtx);    
        }
    }

    if (Status == ERROR_SUCCESS)
    {

        if (EnumCtx.NumItems > 0)
        {
            Status = PackSetULong(EnumCtx.NumItems, 
                                  (PVOID *) &pSavedBuffer, 
                                  &SavedBufferSize);

            EnumCtx.CurrentSize += sizeof(ULONG);
        }

        *TotalBlobBytes = EnumCtx.CurrentSize;
    }

    m_BlobSize = *TotalBlobBytes;
    //*TotalBlobBytes =  (ULONG) (EnumCtx.pBuffer -  pBuffer);

    DFS_TRACE_ERROR_LOW( Status, ADBLOB, "BlobCache %p, PackBlob status %x\n",
                         this, Status);
    return Status;
}

ULONG
DfsADBlobCache::GetBlobSize()
{
    return m_BlobSize;
}



PDFSBLOB_DATA
DfsADBlobCache::FindFirstBlob(PDFSBLOB_ITER pIter)
{
    PDFSBLOB_DATA pBlob = NULL;

    pIter->RootReferenced = AcquireRootBlob();
    if (pIter->RootReferenced != NULL) 
    {
        pIter->Started = FALSE;
        pBlob = pIter->RootReferenced;
    }
    else
    {
        pIter->Started = TRUE;
        pBlob = (PDFSBLOB_DATA) SHashStartEnumerate(&pIter->Iter, m_pTable);
    }

    return pBlob;
}


PDFSBLOB_DATA
DfsADBlobCache::FindNextBlob(PDFSBLOB_ITER pIter)
{
    PDFSBLOB_DATA pBlob = NULL;

    if (pIter->Started == FALSE)
    {
        pIter->Started = TRUE;
        pBlob = (PDFSBLOB_DATA) SHashStartEnumerate(&pIter->Iter, m_pTable);
    }
    else
    {
        pBlob = (PDFSBLOB_DATA) SHashNextEnumerate(&pIter->Iter, m_pTable);
    }

    return pBlob;
}



void
DfsADBlobCache::FindCloseBlob(PDFSBLOB_ITER pIter)
{

    if (pIter->RootReferenced)
    {
        ReleaseBlobReference(pIter->RootReferenced);
        pIter->RootReferenced = NULL;
    }
    if (pIter->Started)
    {
        SHashFinishEnumerate(&pIter->Iter, m_pTable);    
        pIter->Started = FALSE;
    }
}


DFSSTATUS
AddStreamToBlob(PUNICODE_STRING BlobName,
                BYTE *pBlobBuffer,
                ULONG  BlobSize,
                BYTE ** pUseBuffer,
                ULONG *BufferSize )
{
    DFSSTATUS Status = ERROR_SUCCESS;

    Status = PackSetString(BlobName, (PVOID *) pUseBuffer, BufferSize);
    if(Status == ERROR_SUCCESS)
    {
         Status = PackSetULong(BlobSize, (PVOID *) pUseBuffer, BufferSize);
         if(Status == ERROR_SUCCESS)
         {
             if ( *BufferSize >= BlobSize )
             {
                 RtlCopyMemory((*pUseBuffer), pBlobBuffer, BlobSize);

                 *pUseBuffer = (BYTE *)((ULONG_PTR)*pUseBuffer + BlobSize);
                 *BufferSize -= BlobSize;
             }
             else
             {
                 Status = ERROR_BUFFER_OVERFLOW;
             }
         }

    }
    if (Status == ERROR_INVALID_DATA)
    {
        Status = ERROR_BUFFER_OVERFLOW;
    }

    return Status;
}

PVOID
AllocateShashData(ULONG Size )
{
    PVOID RetValue = NULL;

    if (Size)
    {
        RetValue = (PVOID) new BYTE[Size];
    }
    return RetValue;
}

VOID
DeallocateShashData(PVOID pPointer )
{
    if(pPointer)
    {
        delete [] (PBYTE)pPointer;
    }
}

DFSSTATUS 
DfsADBlobCache::DfsDoesUserHaveAccess(DWORD DesiredAccess)
{
    DFSSTATUS Status = ERROR_SUCCESS;
    PSECURITY_DESCRIPTOR pSD = NULL;
    PVOID pHandle = NULL;
    UNICODE_STRING ObjectDN;
    
    DFS_TRACE_LOW(ADBLOB, "BlobCache %p: DfsADBlobCache::DfsDoesUserHaveAccess\n", this);

    do
    {

        Status = GetCachedADObject ( &pHandle );
        if(Status != ERROR_SUCCESS)
        {
            DFS_TRACE_ERROR_HIGH( Status, ADBLOB, "DfsADBlobCache::DfsDoesUserHaveAccess: GetCachedADObject failed status %x\n", 
                                 Status);
            break;
        }

        Status = GetObjectDN(&ObjectDN);
        if(Status != ERROR_SUCCESS)
        {
            DFS_TRACE_ERROR_HIGH( Status, ADBLOB, "DfsADBlobCache::DfsDoesUserHaveAccess: GetObjectDN failed status %x\n", 
                                 Status);
            break;
        }


        Status = DfsGetObjSecurity((LDAP *) pHandle,
                                   ObjectDN.Buffer,
                                   &pSD);

        if(Status != ERROR_SUCCESS)
        {
            DFS_TRACE_ERROR_HIGH( Status, ADBLOB, "DfsADBlobCache::DfsDoesUserHaveAccess: DfsGetObjectSecurity failed status %x\n", 
                                 Status);
            break;
        }

        Status = DfsDoesUserHaveDesiredAccessToAd(DesiredAccess, 
                                                  pSD);
        if(Status != ERROR_SUCCESS)
        {
            DFS_TRACE_ERROR_HIGH( Status, ADBLOB, "DfsADBlobCache::DfsDoesUserHaveAccess: DfsDoesUserHaveDesiredAccessToAd failed status %x\n", 
                                 Status);
            break;
        }

    }while (0);

    if(pHandle != NULL)
    {
        ReleaseADObject( pHandle );
    }

    if(pSD)
    {
        DfsDeallocateSecurityData (pSD);
    }

    return Status;
}