/*++

Copyright (C) 2000-2001 Microsoft Corporation

--*/

#include <wbemcomn.h>
#include <reposit.h>
#include <sync.h>
#include <malloc.h>
#include "longstg.h"


#define A51_INSTRUCTION_TYPE_WRITEFILE 1
#define A51_INSTRUCTION_TYPE_SETENDOFFILE 2

CTempMemoryManager g_LongFileCacheManager;

__int64 CWriteFileInstruction::mstatic_lNextZOrder = 0;

DWORD g_dwFailureCount = 0;
DWORD g_dwFailureFrequency = 0;
DWORD g_dwLastFailureCheck = 0;
#define FAILURE_INJECTION_CHECK_INTERVAL 10000

CLongFileInstruction::CLongFileInstruction(CLongFileStagingFile* pFile) 
    : CStageInstruction(pFile)
{
}

CLongFileInstruction::CLongFileInstruction(CLongFileStagingFile* pFile, 
                int nFileId, TFileOffset lStartOffset)
    : CStageInstruction(pFile), m_Location(nFileId, lStartOffset)
{
}


DWORD CLongFileInstruction::ComputeSpaceForLocation()
{
    return (sizeof(BYTE) + sizeof(m_Location.m_lStartOffset));
}

long CLongFileInstruction::RecoverLocation(HANDLE hFile)
{
    DWORD dwRead;

    BYTE nFileId;
    if(!ReadFile(hFile, &nFileId, sizeof(BYTE), &dwRead, NULL))
        return GetLastError();

    m_Location.m_nFileId = nFileId;

    if(!ReadFile(hFile, (BYTE*)&m_Location.m_lStartOffset, 
                    sizeof m_Location.m_lStartOffset, &dwRead, NULL))
    {
        return GetLastError();
    }

    return ERROR_SUCCESS;
}

BYTE* CLongFileInstruction::WriteLocation(BYTE* pBuffer)
{
    *pBuffer = (BYTE)m_Location.m_nFileId;
    memcpy(pBuffer + 1, (BYTE*)&m_Location.m_lStartOffset, 
            sizeof m_Location.m_lStartOffset);
    return pBuffer + 1 + sizeof m_Location.m_lStartOffset;
}

void CLongFileInstruction::Dump()
{
    ERRORTRACE((LOG_WBEMCORE, "File %d, Start %d, stage offset %d\n",
                    (int)m_Location.m_nFileId, (int)m_Location.m_lStartOffset,
                    (int)m_lStageOffset));
}

void CWriteFileInstruction::Dump()
{
    ERRORTRACE((LOG_WBEMCORE, "File %d, Start %d, Len %d, stage offset %d\n",
                    (int)m_Location.m_nFileId, (int)m_Location.m_lStartOffset,
                    (int)m_dwLen, (int)m_lStageOffset));
}
void CWriteFileInstruction::MakeTopmost()
{
	m_lZOrder = mstatic_lNextZOrder++;
}
DWORD CWriteFileInstruction::ComputeNeededSpace()
{
	if (!m_bReuse)
		return sizeof(BYTE) + // for the type
					ComputeSpaceForLocation() + 
					sizeof(DWORD) + // for the length of data
					m_dwLen + // for the data
					A51_TAIL_SIZE; // for the trailer
	else
		return sizeof(BYTE) + // for the type
					ComputeSpaceForLocation() + 
					sizeof(DWORD) + // for the length of data
					m_dwLen;  // for the data
					//NO TAIL if we are re-using the old write instruction as they are overwritten
					//by next instruction in log
}

TFileOffset CWriteFileInstruction::ComputeOriginalOffset()
{
    return m_lStageOffset - sizeof(BYTE) - ComputeSpaceForLocation() - 
            sizeof(DWORD);
}
                    
long CWriteFileInstruction::Write(TFileOffset lOffset, BYTE* pBuffer)
{
    _ASSERT(m_Location.m_lStartOffset >= 0 && m_Location.m_lStartOffset < 0x70000000, L"");

    //
    // Construct an in-memory buffer large enough for the whole thing
    //

    DWORD dwNeededSpace = ComputeNeededSpace();
    BYTE* pWholeBuffer = (BYTE*)TempAlloc(dwNeededSpace);
    if(pWholeBuffer == NULL)
        return ERROR_OUTOFMEMORY;
    CTempFreeMe vdm(pWholeBuffer, dwNeededSpace);

    BYTE* pCurrent = pWholeBuffer;

    //
    // Write instruction type
    //

    *pCurrent = A51_INSTRUCTION_TYPE_WRITEFILE;
    pCurrent++;

    //
    // Write location
    //

    pCurrent = WriteLocation(pCurrent);

    //
    // Write the length of the data for the file
    //

    memcpy(pCurrent, (void*)&m_dwLen, sizeof(DWORD));
    pCurrent += sizeof(DWORD);

    //
    // Write the data itself and record its offset
    //

    memcpy(pCurrent, pBuffer, m_dwLen);
    m_lStageOffset = lOffset + (pCurrent - pWholeBuffer);

    //
    // Write the trailer - only if this is an original instruction.  In the
	// case of a reused instruction we ignore the tail because it had already
	// (probably) been overwritten
    //

	if (!m_bReuse)
		memset(pCurrent + m_dwLen, 0, sizeof(DWORD));
    
    // 
    // Write it 
    //

    return m_pManager->WriteInstruction(lOffset, pWholeBuffer, dwNeededSpace, m_bReuse);
}

long CWriteFileInstruction::RecoverData(HANDLE hFile)
{
    //
    // Recover the file name first
    //

    long lRes = RecoverLocation(hFile);
    if(lRes != ERROR_SUCCESS)
        return lRes;

    //
    // Read the length of the data from the file
    //

    DWORD dwRead;

    if(!ReadFile(hFile, (BYTE*)&m_dwLen, sizeof(DWORD), &dwRead, NULL))
        return GetLastError();

    if(dwRead != sizeof(DWORD))
        return ERROR_HANDLE_EOF;

    //
    // We do not need to actually read the data from the file --- we keep it
    // there until it is time to flush.  But we do need to skip it.  At the same
    // time, we need to record the position in the file where this data resides
    //

    LARGE_INTEGER liFileLen;
    liFileLen.QuadPart = m_dwLen;
    
    LARGE_INTEGER liNewPosition;
    if(!SetFilePointerEx(hFile, liFileLen, &liNewPosition, FILE_CURRENT))
        return GetLastError();

    _ASSERT(liNewPosition.HighPart == 0, L"Staging file too long!");

    m_lStageOffset = (long)(liNewPosition.QuadPart - m_dwLen);
    return ERROR_SUCCESS;
}

void CWriteFileInstruction::GetEnd(CFileLocation* pLocation)
{
    pLocation->m_nFileId = m_Location.m_nFileId;
    pLocation->m_lStartOffset = m_Location.m_lStartOffset + m_dwLen - 1;
}

long CWriteFileInstruction::GetData(HANDLE hFile, long lExtraOffset, 
                                    DWORD dwLen, BYTE* pBuffer)
{
	//
	// Lock the file
	//

	CInCritSec ics(m_pManager->GetLock());

	_ASSERT(m_pManager->GetFirstFreeOffset() >= m_lStageOffset, 
            L"Instruction points to empty space in stage file");

    long lRes = A51ReadFromFileSync(hFile, m_lStageOffset + lExtraOffset, 
                                    pBuffer, dwLen);
    if(lRes != ERROR_SUCCESS)
    {
        return lRes;
    }

    return ERROR_SUCCESS;
}

    
long CWriteFileInstruction::Execute()
{
    long lRes;

    //
    // Read the data from the staging file
    //

    BYTE* pBuffer = (BYTE*)TempAlloc(m_dwLen);
    if(pBuffer == NULL)
        return ERROR_OUTOFMEMORY;
    CTempFreeMe tfm(pBuffer);

    lRes = GetData(m_pManager->GetHandle(), 0, m_dwLen, pBuffer);
    if(lRes != ERROR_SUCCESS)
        return lRes;

    lRes = ((CLongFileStagingFile*)m_pManager)->WriteToActualFile(
                    m_Location.m_nFileId, m_Location.m_lStartOffset,
                    pBuffer, m_dwLen);
    if(lRes != ERROR_SUCCESS)
    {
        return lRes;
    }

    return ERROR_SUCCESS;
}


DWORD CSetEndOfFileInstruction::ComputeNeededSpace()
{
    return sizeof(BYTE) + // for instruction type
            ComputeSpaceForLocation() + 
            A51_TAIL_SIZE; // for the trailer
}

long CSetEndOfFileInstruction::Write(TFileOffset lOffset)
{
    //
    // Construct an in-memory buffer large enough for the whole thing
    //

    DWORD dwNeededSpace = ComputeNeededSpace();
    BYTE* pWholeBuffer = (BYTE*)TempAlloc(dwNeededSpace);
    if(pWholeBuffer == NULL)
        return ERROR_OUTOFMEMORY;
    CTempFreeMe vdm(pWholeBuffer, dwNeededSpace);

    BYTE* pCurrent = pWholeBuffer;

    //
    // Write the instruction type
    //
    
    *pCurrent = A51_INSTRUCTION_TYPE_SETENDOFFILE;
    pCurrent++;
    
    //
    // Write the file name
    //

    pCurrent = WriteLocation(pCurrent);
    m_lStageOffset = lOffset + (pCurrent - pWholeBuffer);

    //
    // Write the trailer
    //

    memset(pCurrent, 0, sizeof(DWORD));

    // 
    // Write it 
    //

    return m_pManager->WriteInstruction(lOffset, pWholeBuffer, dwNeededSpace);
}

long CSetEndOfFileInstruction::RecoverData(HANDLE hFile)
{
    long lRes = RecoverLocation(hFile);
    if(lRes != ERROR_SUCCESS)
        return lRes;

    LARGE_INTEGER liZero;
    liZero.QuadPart = 0;
    LARGE_INTEGER liPosition;
    if(!SetFilePointerEx(hFile, liZero, &liPosition, FILE_CURRENT))
        return GetLastError();

    _ASSERT(liPosition.HighPart == 0, L"Staging file too long!");

    m_lStageOffset = (long)(liPosition.QuadPart);
    return ERROR_SUCCESS;
}
    
long CSetEndOfFileInstruction::Execute()
{
    long lRes = ((CLongFileStagingFile*)m_pManager)->SetEndOfActualFile(
                    m_Location.m_nFileId, m_Location.m_lStartOffset);
    return lRes;
}

//
//        CStageManager
//
//              |
//
//    CExecutableStageManager
//
//              |
//
//     CLongFileStagingFile
//
/////////////////////////////////////////////////////////////////////////////

CLongFileStagingFile::CLongFileStagingFile(long lMaxFileSize,
                                                long lAbortTransactionFileSize)
    : CExecutableStageManager(lMaxFileSize, lAbortTransactionFileSize),
        m_mapStarts(TMap::key_compare(), 
                        TMap::allocator_type(&g_LongFileCacheManager)),
        m_mapEnds(TMap::key_compare(), 
                        TMap::allocator_type(&g_LongFileCacheManager))
{
    for(int i = 0; i < A51_MAX_FILES; i++)
        m_aFiles[i].m_h = NULL;
}

CLongFileStagingFile::~CLongFileStagingFile()
{
}

long CLongFileStagingFile::Create(LPCWSTR wszStagingFileName)
{
   return CExecutableStageManager::Create(wszStagingFileName);
};

long CLongFileStagingFile::Initialize()
{
    CInCritSec ics(&m_cs);
    
    return CExecutableStageManager::Start();
};

long CLongFileStagingFile::Uninitialize(DWORD dwShutDownFlags)
{
    
    // do not hold the CritSec here, since the FlusherThread needs it
    
    CExecutableStageManager::Stop(dwShutDownFlags);

    CInCritSec ics(&m_cs);
    
    CloseAllFiles();

    m_mapStarts.clear();
    m_mapEnds.clear();
    
    return ERROR_SUCCESS;
};

long CLongFileStagingFile::RemoveInstructionFromMap(
                                                    CStageInstruction* pRawInst)
{
    CInCritSec ics(&m_cs);

    CLongFileInstruction* pInst = (CLongFileInstruction*)pRawInst;
    if(!pInst->IsWrite())
        return ERROR_SUCCESS;

/*
    ERRORTRACE((LOG_WBEMCORE, "Remove instruction\n"));
    pInst->Dump();
*/

    TIterator it = m_mapStarts.find(pInst->m_Location);
    while(it != m_mapStarts.end() && it->second != pInst)
        it++;

    if(it == m_mapStarts.end())
    {
        return ERROR_FILE_NOT_FOUND;
    }

    it->second->Release();
    m_mapStarts.erase(it);

    CFileLocation Location;
    ((CWriteFileInstruction*)pInst)->GetEnd(&Location);

    it = m_mapEnds.find(Location);
    while(it != m_mapEnds.end() && it->second != pInst)
        it++;

    if(it == m_mapEnds.end())
    {        
        return ERROR_FILE_NOT_FOUND;
    }

    it->second->Release();
    m_mapEnds.erase(it);

    return ERROR_SUCCESS;
}

void CLongFileStagingFile::FlushDataFiles()
{
    CInCritSec ics(&m_cs);
    for(int i = 0; i < A51_MAX_FILES; i++)
    {
        HANDLE h = m_aFiles[i].m_h;
        if(h)
        {
            FlushFileBuffers(h);
        }
    }

}
long CLongFileStagingFile::WriteFile(int nFileId, DWORD dwStartOffset, 
                                BYTE* pBuffer, DWORD dwLen, DWORD* pdwWritten)
{
    CInCritSec ics(&m_cs);
    long lRes;

#ifdef DBG
    if(InjectFailure())
    {
        ERRORTRACE((LOG_WBEMCORE, "FAIL: File %d, offset %d, len %d\n",
                    (int)nFileId, (int)dwStartOffset, (int)dwLen));
        return ERROR_SECTOR_NOT_FOUND;
    }
#endif

    if(pdwWritten)
		*pdwWritten = dwLen;

    if(DoesSupportOverwrites(nFileId))
    {
        //
        // For this file, it is considered efficient to look for another 
        // instruction within the same transaction.  It is guaranteed that 
        // writes within this file never intersect!
        //

        CFileLocation StartLocation;
        StartLocation.m_nFileId = nFileId;
        StartLocation.m_lStartOffset = (TFileOffset)dwStartOffset;
    
        CWriteFileInstruction* pLatestMatch = NULL;

        TIterator itStart = m_mapStarts.lower_bound(StartLocation);
        while(itStart != m_mapStarts.end()
                && itStart->first.m_nFileId == nFileId
                && itStart->first.m_lStartOffset == (TFileOffset)dwStartOffset)
        {
            CWriteFileInstruction* pInst = itStart->second;
            if(pInst->m_dwLen == dwLen && !pInst->IsCommitted())
            {
                //
                // Exact match. Compare to the other matches
                //

                if(pLatestMatch == NULL ||
                    pInst->m_lZOrder > pLatestMatch->m_lZOrder)
                {
                    pLatestMatch = pInst;
                }

            }
    
            itStart++;
        }

        if(pLatestMatch)
        {
            //
            // Exact match. All we need to do is overwrite the original
            // instruction.  Of course, we also need to afjust the hash!!
            //

			pLatestMatch->SetReuseFlag();
            lRes = pLatestMatch->Write(pLatestMatch->ComputeOriginalOffset(), 
                                            pBuffer);
            if(lRes)
                return lRes;

/*
            ERRORTRACE((LOG_WBEMCORE, "Replaced instruction:\n"));
            pLatestMatch->Dump();
*/

            //
            // No need to make sure this instruction comes up in the Z-order!
            // After all, it's already topmost by selection criteria!
            //

            return ERROR_SUCCESS;
        }
    }


    //
    // No match --- add a new instruction
    //

    CWriteFileInstruction* pInst = 
        new CWriteFileInstruction(this, nFileId, dwStartOffset, dwLen);
    if(pInst == NULL)
        return ERROR_OUTOFMEMORY;
    pInst->AddRef();
    CTemplateReleaseMe<CLongFileInstruction> rm1(pInst);

    DWORD dwSpaceNeeded = pInst->ComputeNeededSpace();

    if(!CanWriteInTransaction(dwSpaceNeeded))
        return ERROR_NOT_ENOUGH_QUOTA;

    {
        CInCritSec ics(&m_cs);

        //
        // Write all the data into the staging area
        //
    
        lRes = pInst->Write(m_lFirstFreeOffset, pBuffer);
        if(lRes)
            return lRes;
    
        m_lFirstFreeOffset += dwSpaceNeeded - A51_TAIL_SIZE;
    
        lRes = AddInstruction(pInst);
    }
    return lRes;
}

long CLongFileStagingFile::SetFileLength(int nFileId, DWORD dwLen)
{
    CInCritSec ics(&m_cs);
    long lRes;


    CSetEndOfFileInstruction* pInst = 
        new CSetEndOfFileInstruction(this, nFileId, dwLen);
    if(pInst == NULL)
        return ERROR_OUTOFMEMORY;
    pInst->AddRef();
    CTemplateReleaseMe<CSetEndOfFileInstruction> rm1(pInst);

    DWORD dwSpaceNeeded = pInst->ComputeNeededSpace();

    if(!CanWriteInTransaction(dwSpaceNeeded))
        return ERROR_NOT_ENOUGH_QUOTA;

    {
        CInCritSec ics(&m_cs);

        //
        // Write all the data into the staging area
        //
    
        lRes = pInst->Write(m_lFirstFreeOffset);
        if(lRes)
            return lRes;
    
        //
        // Write the new offset into the offset file
        //
    
        m_lFirstFreeOffset += dwSpaceNeeded - A51_TAIL_SIZE;
    
        lRes = AddInstruction(pInst);
    }
    return lRes;
}

long CLongFileStagingFile::AddInstructionToMap(CStageInstruction* pRawInst,
                                            CStageInstruction** ppUndoInst)
{
    CInCritSec ics(&m_cs);

    if(ppUndoInst)
        *ppUndoInst = NULL;

    CLongFileInstruction* pLongInst = (CLongFileInstruction*)pRawInst;
    if(!pLongInst->IsWrite())
        return ERROR_SUCCESS;

    CWriteFileInstruction* pInst = (CWriteFileInstruction*)pLongInst;

/*
    ERRORTRACE((LOG_WBEMCORE, "Add instruction\n"));
    pInst->Dump();
*/

    pInst->AddRef();
    TIterator itStart;

    try
    {
        itStart = m_mapStarts.insert(TValue(pInst->m_Location, pInst));
    }
    catch(...)
    {
        //
        // Put everything back as well as we can
        //

        pInst->Release();
        return ERROR_OUTOFMEMORY;
    }

    pInst->AddRef();

    try
    {
        CFileLocation Location;
        pInst->GetEnd(&Location);

        m_mapEnds.insert(TValue(Location, pInst));
    }
    catch(...)
    {
        //
        // Put everything back as well as we can
        //

        pInst->Release();
        
        m_mapStarts.erase(itStart);

        pInst->Release();
    }
            
    return ERROR_SUCCESS;
}

bool CLongFileStagingFile::IsStillCurrent(CStageInstruction* pRawInst)
{
    CInCritSec ics(&m_cs);

    CLongFileInstruction* pInst = (CLongFileInstruction*)pRawInst;
    if(!pInst->IsWrite())
        return true;

    _ASSERT(m_mapStarts.find(pInst->m_Location) != m_mapStarts.end(),
                L"Why would we be asking about an instruction that is "
                        "not even there?");
    return true;
}

long CLongFileStagingFile::ReadFile(int nFileId, DWORD dwStartOffset, 
                BYTE* pBuffer, DWORD dwLen, DWORD* pdwRead)
{
	if(pdwRead)
		*pdwRead = dwLen;
    long lRes;

    CInCritSec ics(&m_cs);

    // 
    // Search for the file
    //

    CFileLocation StartLocation;
    StartLocation.m_nFileId = nFileId;
    StartLocation.m_lStartOffset = (TFileOffset)dwStartOffset;

    bool bComplete = false;
    CRefedPointerArray<CWriteFileInstruction> apRelevant;

    TIterator itStart = m_mapStarts.lower_bound(StartLocation);
    while(itStart != m_mapStarts.end()
            && itStart->first.m_nFileId == nFileId
            && itStart->first.m_lStartOffset < 
                    (TFileOffset)dwStartOffset + dwLen)
    {
        CWriteFileInstruction* pInst = itStart->second;

		if(pInst->m_Location.m_lStartOffset == dwStartOffset &&
			pInst->m_dwLen >= dwLen)
		{
			bComplete = true;
		}
        apRelevant.Add(pInst);
		itStart++;
    }
        
    TIterator itEnd = m_mapEnds.lower_bound(StartLocation);

    while(itEnd != m_mapEnds.end()
            && itEnd->first.m_nFileId == nFileId
            && itEnd->second->m_Location.m_lStartOffset <  
                (TFileOffset)dwStartOffset)
    {
        CWriteFileInstruction* pInst = itEnd->second;
        if(itEnd->first.m_lStartOffset >= 
                (TFileOffset)dwStartOffset + dwLen - 1)
        {
            //
            // Completely covers us!
            //

            bComplete = true;
        }

        apRelevant.Add(pInst);
		itEnd++;
    }

    if(!bComplete)
    {
        //
        // Read from the real file
        //

        lRes = A51ReadFromFileSync(m_aFiles[nFileId].m_h, dwStartOffset, 
                    pBuffer, dwLen);
        if(lRes != ERROR_SUCCESS)
        {
            return lRes;
        }
    }

    //
    // Now, sort all the instructions by z-order
    //

    int i = 0;
    while(i < apRelevant.GetSize() - 1)
    {
        CWriteFileInstruction* pInst1 = apRelevant[i];
        CWriteFileInstruction* pInst2 = apRelevant[i+1];
        if(pInst1->m_lZOrder > pInst2->m_lZOrder)
        {
            apRelevant.Swap(i, i+1);
            if(i > 0)
                i--;
        }
        else
        {
            i++;
        }
    }

    //
    // Apply them in this order
    //

/*
    if(apRelevant.GetSize() > 0)
    {
        ERRORTRACE((LOG_WBEMCORE, "Using instructions to read %d bytes "
                        "from file %d starting at %d:\n",
                        (int)dwStartOffset, (int)nFileId, (int)dwLen));
    }
*/

    for(i = 0; i < apRelevant.GetSize(); i++)
    {
        CWriteFileInstruction* pInstruction = apRelevant[i];
        // pInstruction->Dump();

        long lIntersectionStart = max(pInstruction->m_Location.m_lStartOffset,
                                        dwStartOffset);

        long lIntersectionEnd = 
            min(pInstruction->m_Location.m_lStartOffset + pInstruction->m_dwLen,
                dwStartOffset + dwLen);
        
        DWORD dwReadLen = (DWORD)(lIntersectionEnd - lIntersectionStart);
        long lInstructionReadOffset = 
                lIntersectionStart - pInstruction->m_Location.m_lStartOffset;

        long lDestinationBufferOffset = lIntersectionStart - dwStartOffset;

        long lRes = pInstruction->GetData(m_hFile, lInstructionReadOffset,
                        dwReadLen, pBuffer + lDestinationBufferOffset);
        if(lRes != ERROR_SUCCESS)
        {
            return lRes;
        }
    }

    return ERROR_SUCCESS;
}

long CLongFileStagingFile::GetFileLength(int nFileId, DWORD* pdwLength)
{
    _ASSERT(pdwLength, L"Invalid parameter");

    *pdwLength = 0;
    long lRes;

    CInCritSec ics(&m_cs);

    //
    // Find the instruction that ends as far as we can see --- that would be
    // the last one in the map of Ends
    //


	CFileLocation Location;
	Location.m_nFileId = nFileId+1;
	Location.m_lStartOffset = 0;

    TIterator itEnd = m_mapEnds.lower_bound(Location);
	if(itEnd != m_mapEnds.begin())
	{
		itEnd--;
		if(itEnd->first.m_nFileId == nFileId)
		{
			*pdwLength = itEnd->first.m_lStartOffset+1;
		}
	}

    //
    // Now, check out the length of the actual file
    //

    BY_HANDLE_FILE_INFORMATION fi;
    if(!GetFileInformationByHandle(m_aFiles[nFileId].m_h, &fi))
    {
        long lRes = GetLastError();
        _ASSERT(lRes != ERROR_SUCCESS, L"Success from failure");
        return lRes;
    }
        
    _ASSERT(fi.nFileSizeHigh == 0, L"Free file too long");

    if(fi.nFileSizeLow > *pdwLength)
        *pdwLength = fi.nFileSizeLow;

    return ERROR_SUCCESS;
}

long CLongFileStagingFile::ConstructInstructionFromType(int nType,
                                CStageInstruction** ppInst)
{
    CLongFileInstruction* pInst = NULL;
    switch(nType)
    {
        case A51_INSTRUCTION_TYPE_WRITEFILE:
            pInst = new CWriteFileInstruction(this);
            break;
        case A51_INSTRUCTION_TYPE_SETENDOFFILE:
            pInst = new CSetEndOfFileInstruction(this);
            break;
        default:
			return ERROR_RXACT_INVALID_STATE;
    }

    if(pInst == NULL)
        return WBEM_E_OUT_OF_MEMORY;
    pInst->AddRef();
    *ppInst = pInst;
    return ERROR_SUCCESS;
}

long CLongFileStagingFile::WriteToActualFile(int nFileId, 
                            TFileOffset lFileOffset, BYTE* pBuffer, DWORD dwLen)
{
    CInCritSec ics(&m_cs);

    long lRet = A51WriteToFileSync(m_aFiles[nFileId].m_h, lFileOffset, 
                    pBuffer, dwLen);
//    FlushFileBuffers(m_aFiles[nFileId].m_h);
    return lRet;
}

long CLongFileStagingFile::SetEndOfActualFile(int nFileId, 
                                                TFileOffset lFileLength)
{
    CInCritSec ics(&m_cs);

    LARGE_INTEGER liEnd;
    liEnd.QuadPart = lFileLength;

    if(!SetFilePointerEx(m_aFiles[nFileId].m_h, liEnd, NULL, FILE_BEGIN))
        return GetLastError();

    if(!SetEndOfFile(m_aFiles[nFileId].m_h))
        return GetLastError();

    return ERROR_SUCCESS;
}

long CLongFileStagingFile::CloseAllFiles()
{
    CInCritSec ics(&m_cs);

    for(int i = 0; i < A51_MAX_FILES; i++)
    {
        if(m_aFiles[i].m_h != NULL)
        {
            CloseHandle(m_aFiles[i].m_h);
        }
        m_aFiles[i].m_h = NULL;
        m_aFiles[i].m_bSupportsOverwrites = false;
    }

    return ERROR_SUCCESS;
}
    
long CLongFileStagingFile::RegisterFile(int nFileId, HANDLE hFile,
                                    bool bSupportsOverwrites)
{
    _ASSERT(nFileId < A51_MAX_FILES, L"File ID is too large");

    if(m_aFiles[nFileId].m_h != NULL)
        CloseHandle(m_aFiles[nFileId].m_h);

    m_aFiles[nFileId].m_h = hFile;
    m_aFiles[nFileId].m_bSupportsOverwrites = bSupportsOverwrites;

    return ERROR_SUCCESS;
}

bool CLongFileStagingFile::DoesSupportOverwrites(int nFileId)
{
    return m_aFiles[nFileId].m_bSupportsOverwrites;
}

long CLongFileStagingFile::WriteEmpty()
{
    _ASSERT(m_mapStarts.size() == 0 && m_mapEnds.size() == 0, L"");
    FlushDataFiles();
    return CExecutableStageManager::WriteEmpty();
}


bool CLongFileStagingFile::InjectFailure()
{
#ifdef A51_INJECT_FAILURE
    if(GetTickCount() > g_dwLastFailureCheck + FAILURE_INJECTION_CHECK_INTERVAL)
    {
        HKEY hKey;
        long lRes = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
                        L"SOFTWARE\\Microsoft\\WBEM\\CIMOM",
                        0, KEY_READ | KEY_WRITE, &hKey);
        if(lRes)
            return false;
        CRegCloseMe cm(hKey);
    
        DWORD dwLen = sizeof(DWORD);
        lRes = RegQueryValueExW(hKey, L"Failure Frequency", NULL, NULL,
                    (LPBYTE)&g_dwFailureFrequency, &dwLen);
        if(lRes != ERROR_SUCCESS)
            g_dwFailureFrequency = 0;

        g_dwLastFailureCheck = GetTickCount();
    }

    if(g_dwFailureFrequency && ++g_dwFailureCount == g_dwFailureFrequency)
    {
        g_dwFailureCount = 0;
        m_bMustFail = true;
        return true;
    }
    else
    {
        return false;
    }
#else
    return false;
#endif
}

void CLongFileStagingFile::Dump(FILE* f)
{
    fprintf(f, "BEGINS:\n");

    TIterator itStart = m_mapStarts.begin();
    while(itStart != m_mapStarts.end())
    {
        CWriteFileInstruction* pInst = itStart->second;
        fprintf(f, "File %d (%d-%d): instruction %p\n",
            (int)pInst->m_Location.m_nFileId, 
            (int)pInst->m_Location.m_lStartOffset,
            (int)pInst->m_Location.m_lStartOffset + pInst->m_dwLen, 
            pInst);
        itStart++;
    }

    fprintf(f, "IN ORDER:\n");

    int nSize = m_qToWrite.size();
    for(int i = 0; i < nSize; i++)
    {
        CLongFileInstruction* pInstruction = 
             (CLongFileInstruction*)m_qToWrite.front();
        if(pInstruction->IsWrite())
        {
            CWriteFileInstruction* pInst = (CWriteFileInstruction*)pInstruction;
            fprintf(f, "File %d (%d-%d): instruction %p\n",
                (int)pInst->m_Location.m_nFileId, 
                (int)pInst->m_Location.m_lStartOffset,
                (int)pInst->m_Location.m_lStartOffset + pInst->m_dwLen, 
                pInst);
        }
        else
        {
            CSetEndOfFileInstruction* pInst = (CSetEndOfFileInstruction*)pInstruction;
            fprintf(f, "Truncate file %d at %d: instruction %p\n",
                (int)pInst->m_Location.m_nFileId, 
                (int)pInst->m_Location.m_lStartOffset,
                pInst);
        }

        m_qToWrite.pop_front();
        m_qToWrite.push_back(pInstruction);
    }
}