// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. #include "precomp.h" #include #define ARRAY_GROWTH_SIZE 2 const LPSTR INVALID_CAB_NAME = ".cab"; // structs typedef struct _BURN_CAB_CONTEXT { HANDLE hFile; DWORD64 qwOffset; DWORD64 qwSize; HANDLE hThread; HANDLE hBeginOperationEvent; HANDLE hOperationCompleteEvent; BURN_CAB_OPERATION operation; HRESULT hrError; LPWSTR* psczStreamName; LPCWSTR wzTargetFile; HANDLE hTargetFile; BYTE* pbTargetBuffer; DWORD cbTargetBuffer; DWORD iTargetBuffer; } BURN_CAB_CONTEXT; // internal function declarations static HRESULT BeginAndWaitForOperation( __in BURN_CONTAINER_CONTEXT* pContext ); static HRESULT WaitForOperation( __in BURN_CONTAINER_CONTEXT* pContext ); static DWORD WINAPI ExtractThreadProc( __in LPVOID lpThreadParameter ); static INT_PTR DIAMONDAPI CabNotifyCallback( __in FDINOTIFICATIONTYPE iNotification, __inout FDINOTIFICATION *pFDINotify ); static INT_PTR CopyFileCallback( __in BURN_CONTAINER_CONTEXT* pContext, __inout FDINOTIFICATION *pFDINotify ); static INT_PTR CloseFileInfoCallback( __in BURN_CONTAINER_CONTEXT* pContext, __inout FDINOTIFICATION *pFDINotify ); static HRESULT PrepareTargetFile( __in long cb, __in HANDLE hFile ); static void BestEffortSetFileTime( __in FDINOTIFICATION* pFDINotify, __in HANDLE hFile ); static LPVOID DIAMONDAPI CabAlloc( __in DWORD dwSize ); static void DIAMONDAPI CabFree( __in LPVOID pvData ); static INT_PTR FAR DIAMONDAPI CabOpen( __in char FAR *pszFile, __in int /* oflag */, __in int /* pmode */ ); static UINT FAR DIAMONDAPI CabRead( __in INT_PTR hf, __out void FAR *pv, __in UINT cb ); static UINT FAR DIAMONDAPI CabWrite( __in INT_PTR hf, __in void FAR *pv, __in UINT cb ); static long FAR DIAMONDAPI CabSeek( __in INT_PTR hf, __in long dist, __in int seektype ); static int FAR DIAMONDAPI CabClose( __in INT_PTR hf ); static HRESULT AddVirtualFilePointer( __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, __in HANDLE hFile, __in LONGLONG llInitialFilePointer ); static HRESULT ReadIfVirtualFilePointer( __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, __in HANDLE hFile, __in DWORD cbRead ); static BOOL SetIfVirtualFilePointer( __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, __in HANDLE hFile, __in LONGLONG llDistance, __out LONGLONG* pllNewPostion, __in DWORD dwSeekType ); static HRESULT CloseIfVirturalFilePointer( __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, __in HANDLE hFile ); static BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* GetVirtualFilePointer( __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, __in HANDLE hFile ); // internal variables __declspec(thread) static BURN_CONTAINER_CONTEXT* vpContext; // function definitions extern "C" void CabExtractInitialize() { } extern "C" HRESULT CabExtractOpen( __in BURN_CONTAINER_CONTEXT* pContext, __in LPCWSTR wzFilePath ) { HRESULT hr = S_OK; // initialize context pContext->Cabinet.hTargetFile = INVALID_HANDLE_VALUE; hr = StrAllocString(&pContext->Cabinet.sczFile, wzFilePath, 0); ExitOnFailure(hr, "Failed to copy file name."); // create events pContext->Cabinet.hBeginOperationEvent = ::CreateEventW(NULL, TRUE, FALSE, NULL); ExitOnNullWithLastError(pContext->Cabinet.hBeginOperationEvent, hr, "Failed to create begin operation event."); pContext->Cabinet.hOperationCompleteEvent = ::CreateEventW(NULL, TRUE, FALSE, NULL); ExitOnNullWithLastError(pContext->Cabinet.hOperationCompleteEvent, hr, "Failed to create operation complete event."); // create extraction thread pContext->Cabinet.hThread = ::CreateThread(NULL, 0, ExtractThreadProc, pContext, 0, NULL); ExitOnNullWithLastError(pContext->Cabinet.hThread, hr, "Failed to create extraction thread."); // wait for operation to complete hr = WaitForOperation(pContext); ExitOnFailure(hr, "Failed to wait for operation complete."); LExit: return hr; } extern "C" HRESULT CabExtractNextStream( __in BURN_CONTAINER_CONTEXT* pContext, __inout_z LPWSTR* psczStreamName ) { HRESULT hr = S_OK; // set operation to move to next stream pContext->Cabinet.operation = BURN_CAB_OPERATION_NEXT_STREAM; pContext->Cabinet.psczStreamName = psczStreamName; // begin operation and wait hr = BeginAndWaitForOperation(pContext); if (E_ABORT != hr && E_NOMOREITEMS != hr) { ExitOnFailure(hr, "Failed to begin and wait for operation."); } LExit: return hr; } extern "C" HRESULT CabExtractStreamToFile( __in BURN_CONTAINER_CONTEXT* pContext, __in_z LPCWSTR wzFileName ) { HRESULT hr = S_OK; // set operation to move to next stream pContext->Cabinet.operation = BURN_CAB_OPERATION_STREAM_TO_FILE; pContext->Cabinet.wzTargetFile = wzFileName; // begin operation and wait hr = BeginAndWaitForOperation(pContext); ExitOnFailure(hr, "Failed to begin and wait for operation."); // clear file name pContext->Cabinet.wzTargetFile = NULL; LExit: return hr; } extern "C" HRESULT CabExtractStreamToBuffer( __in BURN_CONTAINER_CONTEXT* pContext, __out BYTE** ppbBuffer, __out SIZE_T* pcbBuffer ) { HRESULT hr = S_OK; // set operation to move to next stream pContext->Cabinet.operation = BURN_CAB_OPERATION_STREAM_TO_BUFFER; // begin operation and wait hr = BeginAndWaitForOperation(pContext); ExitOnFailure(hr, "Failed to begin and wait for operation."); // return values *ppbBuffer = pContext->Cabinet.pbTargetBuffer; *pcbBuffer = pContext->Cabinet.cbTargetBuffer; // clear buffer variables pContext->Cabinet.pbTargetBuffer = NULL; pContext->Cabinet.cbTargetBuffer = 0; pContext->Cabinet.iTargetBuffer = 0; LExit: return hr; } extern "C" HRESULT CabExtractStreamToHandle( __in BURN_CONTAINER_CONTEXT* pContext, __in HANDLE hFile ) { HRESULT hr = S_OK; // set operation to move to next stream pContext->Cabinet.operation = BURN_CAB_OPERATION_STREAM_TO_HANDLE; pContext->Cabinet.hTargetFile = hFile; // begin operation and wait hr = BeginAndWaitForOperation(pContext); ExitOnFailure(hr, "Failed to begin and wait for operation."); // clear file handle pContext->Cabinet.hTargetFile = INVALID_HANDLE_VALUE; LExit: return hr; } extern "C" HRESULT CabExtractSkipStream( __in BURN_CONTAINER_CONTEXT* pContext ) { HRESULT hr = S_OK; // set operation to move to next stream pContext->Cabinet.operation = BURN_CAB_OPERATION_SKIP_STREAM; // begin operation and wait hr = BeginAndWaitForOperation(pContext); ExitOnFailure(hr, "Failed to begin and wait for operation."); LExit: return hr; } extern "C" HRESULT CabExtractClose( __in BURN_CONTAINER_CONTEXT* pContext ) { HRESULT hr = S_OK; // terminate worker thread if (pContext->Cabinet.hThread) { // set operation to move to close pContext->Cabinet.operation = BURN_CAB_OPERATION_CLOSE; // set begin operation event if (!::SetEvent(pContext->Cabinet.hBeginOperationEvent)) { ExitWithLastError(hr, "Failed to set begin operation event."); } // wait for thread to terminate hr = AppWaitForSingleObject(pContext->Cabinet.hThread, INFINITE); ExitOnFailure(hr, "Failed to wait for thread to terminate."); } LExit: ReleaseHandle(pContext->Cabinet.hThread); ReleaseHandle(pContext->Cabinet.hBeginOperationEvent); ReleaseHandle(pContext->Cabinet.hOperationCompleteEvent); ReleaseMem(pContext->Cabinet.rgVirtualFilePointers); ReleaseStr(pContext->Cabinet.sczFile); return hr; } // internal helper functions static HRESULT BeginAndWaitForOperation( __in BURN_CONTAINER_CONTEXT* pContext ) { HRESULT hr = S_OK; // set begin operation event if (!::SetEvent(pContext->Cabinet.hBeginOperationEvent)) { ExitWithLastError(hr, "Failed to set begin operation event."); } // wait for operation to complete hr = WaitForOperation(pContext); LExit: return hr; } static HRESULT WaitForOperation( __in BURN_CONTAINER_CONTEXT* pContext ) { HRESULT hr = S_OK; HANDLE rghWait[2] = { }; DWORD dwSignaledIndex = 0; // wait for operation complete event rghWait[0] = pContext->Cabinet.hOperationCompleteEvent; rghWait[1] = pContext->Cabinet.hThread; hr = AppWaitForMultipleObjects(countof(rghWait), rghWait, FALSE, INFINITE, &dwSignaledIndex); ExitOnFailure(hr, "Failed to wait for operation complete event."); switch (dwSignaledIndex) { case 0: if (!::ResetEvent(pContext->Cabinet.hOperationCompleteEvent)) { ExitWithLastError(hr, "Failed to reset operation complete event."); } break; case 1: if (!::GetExitCodeThread(pContext->Cabinet.hThread, (DWORD*)&hr)) { ExitWithLastError(hr, "Failed to get extraction thread exit code."); } ExitFunction(); } // clear operation pContext->Cabinet.operation = BURN_CAB_OPERATION_NONE; LExit: return hr; } static DWORD WINAPI ExtractThreadProc( __in LPVOID lpThreadParameter ) { HRESULT hr = S_OK; BURN_CONTAINER_CONTEXT* pContext = (BURN_CONTAINER_CONTEXT*)lpThreadParameter; BOOL fComInitialized = FALSE; HFDI hfdi = NULL; ERF erf = { }; // initialize COM hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED); ExitOnFailure(hr, "Failed to initialize COM."); fComInitialized = TRUE; // save context in TLS storage vpContext = pContext; // create FDI context hfdi = ::FDICreate(CabAlloc, CabFree, CabOpen, CabRead, CabWrite, CabClose, CabSeek, cpuUNKNOWN, &erf); ExitOnNull(hfdi, hr, E_FAIL, "Failed to initialize cabinet.dll."); // begin CAB extraction if (!::FDICopy(hfdi, INVALID_CAB_NAME, "", 0, CabNotifyCallback, NULL, NULL)) { hr = pContext->Cabinet.hrError; if (E_ABORT == hr || E_NOMOREITEMS == hr) { ExitFunction(); } else if (SUCCEEDED(hr)) { if (ERROR_SUCCESS != erf.erfType) { hr = HRESULT_FROM_WIN32(erf.erfType); } else { switch (erf.erfOper) { case FDIERROR_NONE: hr = E_UNEXPECTED; break; case FDIERROR_CABINET_NOT_FOUND: hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); break; case FDIERROR_NOT_A_CABINET: hr = HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION); break; case FDIERROR_UNKNOWN_CABINET_VERSION: hr = HRESULT_FROM_WIN32(ERROR_VERSION_PARSE_ERROR); break; case FDIERROR_CORRUPT_CABINET: hr = HRESULT_FROM_WIN32(ERROR_FILE_CORRUPT); break; case FDIERROR_ALLOC_FAIL: hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY); break; case FDIERROR_BAD_COMPR_TYPE: hr = HRESULT_FROM_WIN32(ERROR_UNSUPPORTED_COMPRESSION); break; case FDIERROR_MDI_FAIL: hr = HRESULT_FROM_WIN32(ERROR_BAD_COMPRESSION_BUFFER); break; case FDIERROR_TARGET_FILE: hr = HRESULT_FROM_WIN32(ERROR_WRITE_FAULT); break; case FDIERROR_RESERVE_MISMATCH: hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); break; case FDIERROR_WRONG_CABINET: hr = HRESULT_FROM_WIN32(ERROR_DATATYPE_MISMATCH); break; case FDIERROR_USER_ABORT: hr = E_ABORT; break; default: hr = E_FAIL; break; } } } ExitOnFailure(hr, "Failed to extract all files from container, erf: %d:%X:%d", erf.fError, erf.erfOper, erf.erfType); } // set operation complete event if (!::SetEvent(pContext->Cabinet.hOperationCompleteEvent)) { ExitWithLastError(hr, "Failed to set operation complete event."); } // wait for begin operation event hr = AppWaitForSingleObject(pContext->Cabinet.hBeginOperationEvent, INFINITE); ExitOnFailure(hr, "Failed to wait for begin operation event."); if (!::ResetEvent(pContext->Cabinet.hBeginOperationEvent)) { ExitWithLastError(hr, "Failed to reset begin operation event."); } // read operation switch (pContext->Cabinet.operation) { case BURN_CAB_OPERATION_NEXT_STREAM: ExitFunction1(hr = E_NOMOREITEMS); break; case BURN_CAB_OPERATION_CLOSE: ExitFunction1(hr = S_OK); default: hr = E_INVALIDSTATE; ExitOnRootFailure(hr, "Invalid operation for this state."); } LExit: if (hfdi) { ::FDIDestroy(hfdi); } if (fComInitialized) { ::CoUninitialize(); } return (DWORD)hr; } static INT_PTR DIAMONDAPI CabNotifyCallback( __in FDINOTIFICATIONTYPE iNotification, __inout FDINOTIFICATION *pFDINotify ) { BURN_CONTAINER_CONTEXT* pContext = vpContext; INT_PTR ipResult = 0; // result to return on success switch (iNotification) { case fdintCOPY_FILE: ipResult = CopyFileCallback(pContext, pFDINotify); break; case fdintCLOSE_FILE_INFO: // resource extraction complete ipResult = CloseFileInfoCallback(pContext, pFDINotify); break; case fdintPARTIAL_FILE: __fallthrough; // no action needed for these messages case fdintNEXT_CABINET: __fallthrough; case fdintENUMERATE: __fallthrough; case fdintCABINET_INFO: break; default: AssertSz(FALSE, "CabExtractCallback() - unknown FDI notification command"); }; //LExit: return ipResult; } static INT_PTR CopyFileCallback( __in BURN_CONTAINER_CONTEXT* pContext, __inout FDINOTIFICATION* pFDINotify ) { HRESULT hr = S_OK; INT_PTR ipResult = 1; // result to return on success // set operation complete event if (!::SetEvent(pContext->Cabinet.hOperationCompleteEvent)) { ExitWithLastError(hr, "Failed to set operation complete event."); } // wait for begin operation event hr = AppWaitForSingleObject(pContext->Cabinet.hBeginOperationEvent, INFINITE); ExitOnFailure(hr, "Failed to wait for begin operation event."); if (!::ResetEvent(pContext->Cabinet.hBeginOperationEvent)) { ExitWithLastError(hr, "Failed to reset begin operation event."); } // read operation switch (pContext->Cabinet.operation) { case BURN_CAB_OPERATION_NEXT_STREAM: break; case BURN_CAB_OPERATION_CLOSE: ExitFunction1(hr = E_ABORT); default: hr = E_INVALIDSTATE; ExitOnRootFailure(hr, "Invalid operation for this state."); } // copy stream name hr = StrAllocStringAnsi(pContext->Cabinet.psczStreamName, pFDINotify->psz1, 0, CP_UTF8); ExitOnFailure(hr, "Failed to copy stream name: %hs", pFDINotify->psz1); // set operation complete event if (!::SetEvent(pContext->Cabinet.hOperationCompleteEvent)) { ExitWithLastError(hr, "Failed to set operation complete event."); } // wait for begin operation event hr = AppWaitForSingleObject(pContext->Cabinet.hBeginOperationEvent, INFINITE); ExitOnFailure(hr, "Failed to wait for begin operation event."); if (!::ResetEvent(pContext->Cabinet.hBeginOperationEvent)) { ExitWithLastError(hr, "Failed to reset begin operation event."); } // read operation switch (pContext->Cabinet.operation) { case BURN_CAB_OPERATION_STREAM_TO_FILE: // create file pContext->Cabinet.hTargetFile = ::CreateFileW(pContext->Cabinet.wzTargetFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == pContext->Cabinet.hTargetFile) { ExitWithLastError(hr, "Failed to create file: %ls", pContext->Cabinet.wzTargetFile); } hr = PrepareTargetFile(pFDINotify->cb, pContext->Cabinet.hTargetFile); ExitOnFailure(hr, "Failed to prepare target file."); break; case BURN_CAB_OPERATION_STREAM_TO_HANDLE: hr = PrepareTargetFile(pFDINotify->cb, pContext->Cabinet.hTargetFile); ExitOnFailure(hr, "Failed to prepare target file."); break; case BURN_CAB_OPERATION_STREAM_TO_BUFFER: // allocate buffer for stream pContext->Cabinet.pbTargetBuffer = (BYTE*)MemAlloc(pFDINotify->cb, TRUE); ExitOnNull(pContext->Cabinet.pbTargetBuffer, hr, E_OUTOFMEMORY, "Failed to allocate buffer for stream."); // set buffer size and write position pContext->Cabinet.cbTargetBuffer = pFDINotify->cb; pContext->Cabinet.iTargetBuffer = 0; break; case BURN_CAB_OPERATION_SKIP_STREAM: ipResult = 0; break; case BURN_CAB_OPERATION_CLOSE: ExitFunction1(hr = E_ABORT); default: hr = E_INVALIDSTATE; ExitOnRootFailure(hr, "Invalid operation for this state."); } LExit: pContext->Cabinet.hrError = hr; return SUCCEEDED(hr) ? ipResult : -1; } static INT_PTR CloseFileInfoCallback( __in BURN_CONTAINER_CONTEXT* pContext, __inout FDINOTIFICATION *pFDINotify ) { HRESULT hr = S_OK; INT_PTR ipResult = 1; // result to return on success // read operation switch (pContext->Cabinet.operation) { case BURN_CAB_OPERATION_STREAM_TO_FILE: BestEffortSetFileTime(pFDINotify, pContext->Cabinet.hTargetFile); // close file ReleaseFile(pContext->Cabinet.hTargetFile); break; case BURN_CAB_OPERATION_STREAM_TO_HANDLE: BestEffortSetFileTime(pFDINotify, pContext->Cabinet.hTargetFile); // Do NOT close file. pContext->Cabinet.hTargetFile = INVALID_HANDLE_VALUE; break; case BURN_CAB_OPERATION_STREAM_TO_BUFFER: break; case BURN_CAB_OPERATION_CLOSE: ExitFunction1(hr = E_ABORT); default: hr = E_INVALIDSTATE; ExitOnRootFailure(hr, "Invalid operation for this state."); } //if (pContext->pfnProgress) //{ // hr = StrAllocFormatted(&pwzPath, L"%s%ls", pContext->wzRootPath, pFDINotify->psz1); // ExitOnFailure(hr, "Failed to calculate file path from: %ls and %s", pContext->wzRootPath, pFDINotify->psz1); // if (SUCCEEDED(hr)) // { // hr = pContext->pfnProgress(BOX_PROGRESS_DECOMPRESSION_END, pwzPath, 0, pContext->pvContext); // if (S_OK != hr) // { // pContext->hrError = hr; // ExitFunction(); // } // } //} LExit: pContext->Cabinet.hrError = hr; return SUCCEEDED(hr) ? ipResult : -1; } static HRESULT PrepareTargetFile( __in long cb, __in HANDLE hFile ) { HRESULT hr = S_OK; LARGE_INTEGER li = { }; // set file size li.QuadPart = cb; if (!::SetFilePointerEx(hFile, li, NULL, FILE_BEGIN)) { ExitWithLastError(hr, "Failed to set file pointer to end of file."); } if (!::SetEndOfFile(hFile)) { ExitWithLastError(hr, "Failed to set end of file."); } li.QuadPart = 0; if (!::SetFilePointerEx(hFile, li, NULL, FILE_BEGIN)) { ExitWithLastError(hr, "Failed to set file pointer to beginning of file."); } LExit: return hr; } static void BestEffortSetFileTime( __in FDINOTIFICATION* pFDINotify, __in HANDLE hFile ) { FILETIME ftLocal = { }; FILETIME ft = { }; // Make a best effort to set the time on the new file before // we close it. if (::DosDateTimeToFileTime(pFDINotify->date, pFDINotify->time, &ftLocal)) { if (::LocalFileTimeToFileTime(&ftLocal, &ft)) { ::SetFileTime(hFile, &ft, &ft, &ft); } } } static LPVOID DIAMONDAPI CabAlloc( __in DWORD dwSize ) { return MemAlloc(dwSize, FALSE); } static void DIAMONDAPI CabFree( __in LPVOID pvData ) { MemFree(pvData); } static INT_PTR FAR DIAMONDAPI CabOpen( __in char FAR * pszFile, __in int /* oflag */, __in int /* pmode */ ) { HRESULT hr = S_OK; BURN_CONTAINER_CONTEXT* pContext = vpContext; HANDLE hFile = INVALID_HANDLE_VALUE; // If this is the invalid cab name, use our file handle. if (CSTR_EQUAL == ::CompareStringA(LOCALE_NEUTRAL, 0, INVALID_CAB_NAME, -1, pszFile, -1)) { if (!::DuplicateHandle(::GetCurrentProcess(), pContext->hFile, ::GetCurrentProcess(), &hFile, 0, FALSE, DUPLICATE_SAME_ACCESS)) { ExitWithLastError(hr, "Failed to duplicate handle to cab container."); } // Use a virtual file pointer since duplicated file handles share their file pointer. Seek to container offset // to start. hr = AddVirtualFilePointer(&pContext->Cabinet, hFile, pContext->qwOffset); ExitOnFailure(hr, "Failed to add virtual file pointer for cab container."); } else // open file requested. This is used in the rare cases where the CAB API wants to create a temp file. { hFile = ::CreateFileA(pszFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); ExitOnInvalidHandleWithLastError(hFile, hr, "Failed to open cabinet file: %hs", pszFile); } LExit: pContext->Cabinet.hrError = hr; return FAILED(hr) ? -1 : (INT_PTR)hFile; } static UINT FAR DIAMONDAPI CabRead( __in INT_PTR hf, __out void FAR *pv, __in UINT cb ) { HRESULT hr = S_OK; BURN_CONTAINER_CONTEXT* pContext = vpContext; HANDLE hFile = (HANDLE)hf; DWORD cbRead = 0; ReadIfVirtualFilePointer(&pContext->Cabinet, hFile, cb); if (!::ReadFile(hFile, pv, cb, &cbRead, NULL)) { ExitWithLastError(hr, "Failed to read during cabinet extraction."); } LExit: pContext->Cabinet.hrError = hr; return FAILED(hr) ? -1 : cbRead; } static UINT FAR DIAMONDAPI CabWrite( __in INT_PTR /* hf */, __in void FAR *pv, __in UINT cb ) { HRESULT hr = S_OK; BURN_CONTAINER_CONTEXT* pContext = vpContext; DWORD cbWrite = 0; switch (pContext->Cabinet.operation) { case BURN_CAB_OPERATION_STREAM_TO_FILE: __fallthrough; case BURN_CAB_OPERATION_STREAM_TO_HANDLE: // write file if (!::WriteFile(pContext->Cabinet.hTargetFile, pv, cb, &cbWrite, NULL)) { ExitWithLastError(hr, "Failed to write during cabinet extraction."); } break; case BURN_CAB_OPERATION_STREAM_TO_BUFFER: // copy to target buffer memcpy_s(pContext->Cabinet.pbTargetBuffer + pContext->Cabinet.iTargetBuffer, pContext->Cabinet.cbTargetBuffer - pContext->Cabinet.iTargetBuffer, pv, cb); pContext->Cabinet.iTargetBuffer += cb; cbWrite = cb; break; default: hr = E_INVALIDSTATE; ExitOnFailure(hr, "Unexpected call to CabWrite()."); } LExit: pContext->Cabinet.hrError = hr; return FAILED(hr) ? -1 : cbWrite; } static long FAR DIAMONDAPI CabSeek( __in INT_PTR hf, __in long dist, __in int seektype ) { HRESULT hr = S_OK; BURN_CONTAINER_CONTEXT* pContext = vpContext; HANDLE hFile = (HANDLE)hf; LARGE_INTEGER liDistance = { }; LARGE_INTEGER liNewPointer = { }; DWORD dwSeekType = 0; // We assume that CabSeek() will only be called to seek the // cabinet itself so we have to offset the seek operations to // where the internal cabinet starts. switch (seektype) { case FILE_BEGIN: liDistance.QuadPart = pContext->qwOffset + dist; dwSeekType = FILE_BEGIN; break; case FILE_CURRENT: liDistance.QuadPart = dist; dwSeekType = FILE_CURRENT; break; case FILE_END: liDistance.QuadPart = pContext->qwOffset + pContext->qwSize + dist; dwSeekType = FILE_BEGIN; break; default: hr = E_INVALIDARG; ExitOnFailure(hr, "Invalid seek type.");; } if (SetIfVirtualFilePointer(&pContext->Cabinet, hFile, liDistance.QuadPart, &liNewPointer.QuadPart, seektype)) { // set file pointer if (!::SetFilePointerEx(hFile, liDistance, &liNewPointer, seektype)) { ExitWithLastError(hr, "Failed to move file pointer 0x%x bytes.", dist); } } liNewPointer.QuadPart -= pContext->qwOffset; LExit: pContext->Cabinet.hrError = hr; return FAILED(hr) ? -1 : liNewPointer.LowPart; } static int FAR DIAMONDAPI CabClose( __in INT_PTR hf ) { BURN_CONTAINER_CONTEXT* pContext = vpContext; HANDLE hFile = (HANDLE)hf; CloseIfVirturalFilePointer(&pContext->Cabinet, hFile); ReleaseFileHandle(hFile); return 0; } static HRESULT AddVirtualFilePointer( __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, __in HANDLE hFile, __in LONGLONG llInitialFilePointer ) { HRESULT hr = S_OK; hr = MemEnsureArraySize(reinterpret_cast(&pCabinetContext->rgVirtualFilePointers), pCabinetContext->cVirtualFilePointers, sizeof(BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER), ARRAY_GROWTH_SIZE); ExitOnFailure(hr, "Failed to allocate memory for the virtual file pointer array."); pCabinetContext->rgVirtualFilePointers[pCabinetContext->cVirtualFilePointers].hFile = hFile; pCabinetContext->rgVirtualFilePointers[pCabinetContext->cVirtualFilePointers].liPosition.QuadPart = llInitialFilePointer; ++pCabinetContext->cVirtualFilePointers; LExit: return hr; } static HRESULT ReadIfVirtualFilePointer( __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, __in HANDLE hFile, __in DWORD cbRead ) { HRESULT hr = E_NOTFOUND; BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* pVfp = GetVirtualFilePointer(pCabinetContext, hFile); if (pVfp) { // Set the file handle to the virtual file pointer. if (!::SetFilePointerEx(hFile, pVfp->liPosition, NULL, FILE_BEGIN)) { ExitWithLastError(hr, "Failed to move to virtual file pointer."); } pVfp->liPosition.QuadPart += cbRead; // add the amount that will be read to advance the pointer. hr = S_OK; } LExit: return hr; } static BOOL SetIfVirtualFilePointer( __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, __in HANDLE hFile, __in LONGLONG llDistance, __out LONGLONG* pllNewPostion, __in DWORD dwSeekType ) { BOOL fFound = FALSE; BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* pVfp = GetVirtualFilePointer(pCabinetContext, hFile); if (pVfp) { switch (dwSeekType) { case FILE_BEGIN: pVfp->liPosition.QuadPart = llDistance; break; case FILE_CURRENT: pVfp->liPosition.QuadPart += llDistance; break; case FILE_END: __fallthrough; default: AssertSz(FALSE, "Unsupported seek type."); break; } *pllNewPostion = pVfp->liPosition.QuadPart; fFound = TRUE; } return fFound; } static HRESULT CloseIfVirturalFilePointer( __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, __in HANDLE hFile ) { HRESULT hr = E_NOTFOUND; BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* pVfp = GetVirtualFilePointer(pCabinetContext, hFile); if (pVfp) { pVfp->hFile = INVALID_HANDLE_VALUE; pVfp->liPosition.QuadPart = 0; hr = S_OK; } return hr; } static BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* GetVirtualFilePointer( __in BURN_CONTAINER_CONTEXT_CABINET* pCabinetContext, __in HANDLE hFile ) { for (DWORD i = 0; i < pCabinetContext->cVirtualFilePointers; ++i) { BURN_CONTAINER_CONTEXT_CABINET_VIRTUAL_FILE_POINTER* pVfp = pCabinetContext->rgVirtualFilePointers + i; if (pVfp->hFile == hFile) { return pVfp; } } return NULL; }