/******************************************************************* PipeConnectionUninitialize - free data in a pipe connection. *******************************************************************/ void PipeConnectionUninitialize( __in BURN_PIPE_CONNECTION* pConnection ) { ReleaseFileHandle(pConnection->hCachePipe); ReleaseFileHandle(pConnection->hPipe); ReleaseHandle(pConnection->hProcess); ReleaseStr(pConnection->sczSecret); ReleaseStr(pConnection->sczName); memset(pConnection, 0, sizeof(BURN_PIPE_CONNECTION)); pConnection->hPipe = INVALID_HANDLE_VALUE; pConnection->hCachePipe = INVALID_HANDLE_VALUE; }
static HRESULT InitializeResume( __in LPCWSTR wzDestinationPath, __out LPWSTR* psczResumePath, __out HANDLE* phResumeFile, __out DWORD64* pdw64ResumeOffset ) { HRESULT hr = S_OK; HANDLE hResumeFile = INVALID_HANDLE_VALUE; DWORD cbTotalReadResumeData = 0; DWORD cbReadData = 0; *pdw64ResumeOffset = 0; hr = CacheGetResumePath(wzDestinationPath, psczResumePath); ExitOnFailure1(hr, "Failed to calculate resume path from working path: %ls", wzDestinationPath); hResumeFile = ::CreateFileW(*psczResumePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == hResumeFile) { ExitWithLastError1(hr, "Failed to create resume file: %ls", *psczResumePath); } do { if (!::ReadFile(hResumeFile, reinterpret_cast<BYTE*>(pdw64ResumeOffset) + cbTotalReadResumeData, sizeof(DWORD64) - cbTotalReadResumeData, &cbReadData, NULL)) { ExitWithLastError1(hr, "Failed to read resume file: %ls", *psczResumePath); } cbTotalReadResumeData += cbReadData; } while (cbReadData && sizeof(DWORD64) > cbTotalReadResumeData); // Start over if we couldn't get a resume offset. if (cbTotalReadResumeData != sizeof(DWORD64)) { *pdw64ResumeOffset = 0; } *phResumeFile = hResumeFile; hResumeFile = INVALID_HANDLE_VALUE; LExit: ReleaseFileHandle(hResumeFile); return hr; }
// --------------------------------------------------------------------------- // Name: RawDataReader::Close // Description: Close the raw file and release all handles and memory // Author: Rajeeb Barman // Date: 26/09/2013 // --------------------------------------------------------------------------- AMDTResult RawDataReader::Close() { AMDTInt32 ret = AMDT_STATUS_OK; if (!g_isOnline && (NULL != m_hRawFile)) { PwrCloseFile(m_hRawFile); m_hRawFile = NULL; } if (m_pBufferList && m_bufferCnt) { for (AMDTUInt16 cnt = 0; cnt < m_bufferCnt; cnt++) { RawBufferInfo* pBuff = m_pBufferList + cnt; AMDTUInt8* pMem = (AMDTUInt8*)(pBuff->uliBuffer.QuadPart); free(pMem); pBuff->ulvalidLength = 0; } } ReleaseFileHandle(&m_rawDataHdl); return ret; }
static DWORD WINAPI LoadThreadProc( __in LPVOID pvContext ) { HRESULT hr = S_OK; LOAD_THREAD_CONTEXT* pContext = static_cast<LOAD_THREAD_CONTEXT*>(pvContext); LPWSTR sczThemePath = pContext->sczThemePath; HWND hWnd = pContext->hWnd; // We can signal the initialization event as soon as we have copied the context // values into local variables. ::SetEvent(pContext->hInit); BOOL fComInitialized = FALSE; HANDLE hDirectory = INVALID_HANDLE_VALUE; LPWSTR sczDirectory = NULL; LPWSTR wzFileName = NULL; THEME* pTheme = NULL; HANDLE_THEME* pHandle = NULL; hr = ::CoInitialize(NULL); ExitOnFailure(hr, "Failed to initialize COM on load thread."); fComInitialized = TRUE; // Open a handle to the directory so we can put a notification on it. hr = PathGetDirectory(sczThemePath, &sczDirectory); ExitOnFailure(hr, "Failed to get path directory."); wzFileName = PathFile(sczThemePath); hDirectory = ::CreateFileW(sczDirectory, FILE_LIST_DIRECTORY, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (INVALID_HANDLE_VALUE == hDirectory) { ExitWithLastError(hr, "Failed to open directory: %ls", sczDirectory); } BOOL fUpdated = FALSE; do { // Get the last modified time on the file we're loading for verification that the // file actually gets changed down below. FILETIME ftModified = { }; FileGetTime(sczThemePath, NULL, NULL, &ftModified); // Try to load the theme file. hr = ThemeLoadFromFile(sczThemePath, &pTheme); if (FAILED(hr)) { ::SendMessageW(hWnd, WM_THMVWR_THEME_LOAD_ERROR, 0, hr); } else { hr = AllocHandleTheme(pTheme, &pHandle); ExitOnFailure(hr, "Failed to allocate handle to theme"); ::SendMessageW(hWnd, WM_THMVWR_NEW_THEME, 0, reinterpret_cast<LPARAM>(pHandle)); pHandle = NULL; } fUpdated = FALSE; do { DWORD rgbNotifications[1024]; DWORD cbRead = 0; if (!::ReadDirectoryChangesW(hDirectory, rgbNotifications, sizeof(rgbNotifications), FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE, &cbRead, NULL, NULL)) { ExitWithLastError(hr, "Failed while watching directory: %ls", sczDirectory); } // Wait for half a second to let all the file handles get closed to minimize access // denied errors. ::Sleep(500); FILE_NOTIFY_INFORMATION* pNotification = reinterpret_cast<FILE_NOTIFY_INFORMATION*>(rgbNotifications); while (pNotification) { // If our file was updated, check to see if the modified time really changed. The notifications // are often trigger happy thinking the file changed two or three times in a row. Maybe it's AV // software creating the problems but actually checking the modified date works well. if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pNotification->FileName, pNotification->FileNameLength / sizeof(WCHAR), wzFileName, -1)) { FILETIME ft = { }; FileGetTime(sczThemePath, NULL, NULL, &ft); fUpdated = (ftModified.dwHighDateTime < ft.dwHighDateTime) || (ftModified.dwHighDateTime == ft.dwHighDateTime && ftModified.dwLowDateTime < ft.dwLowDateTime); break; } pNotification = pNotification->NextEntryOffset ? reinterpret_cast<FILE_NOTIFY_INFORMATION*>(reinterpret_cast<BYTE*>(pNotification) + pNotification->NextEntryOffset) : NULL; } } while (!fUpdated); } while(fUpdated); LExit: if (fComInitialized) { ::CoUninitialize(); } ReleaseFileHandle(hDirectory); ReleaseStr(sczDirectory); ReleaseStr(sczThemePath); return hr; }
/******************************************************************* PipeCreatePipes - create the pipes and event to signal child process. *******************************************************************/ extern "C" HRESULT PipeCreatePipes( __in BURN_PIPE_CONNECTION* pConnection, __in BOOL fCreateCachePipe, __out HANDLE* phEvent ) { Assert(pConnection->sczName); Assert(INVALID_HANDLE_VALUE == pConnection->hPipe); Assert(INVALID_HANDLE_VALUE == pConnection->hCachePipe); HRESULT hr = S_OK; PSECURITY_DESCRIPTOR psd = NULL; SECURITY_ATTRIBUTES sa = { }; LPWSTR sczFullPipeName = NULL; HANDLE hPipe = INVALID_HANDLE_VALUE; HANDLE hCachePipe = INVALID_HANDLE_VALUE; // Only the grant special rights when the pipe is being used for "embedded" // scenarios (aka: there is no cache pipe). if (!fCreateCachePipe) { // Create the security descriptor that grants read/write/sync access to Everyone. // TODO: consider locking down "WD" to LogonIds (logon session) LPCWSTR wzSddl = L"D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRGW0x00100000;;;WD)"; if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW(wzSddl, SDDL_REVISION_1, &psd, NULL)) { ExitWithLastError(hr, "Failed to create the security descriptor for the connection event and pipe."); } sa.nLength = sizeof(sa); sa.lpSecurityDescriptor = psd; sa.bInheritHandle = FALSE; } // Create the pipe. hr = StrAllocFormatted(&sczFullPipeName, PIPE_NAME_FORMAT_STRING, pConnection->sczName); ExitOnFailure1(hr, "Failed to allocate full name of pipe: %ls", pConnection->sczName); // TODO: consider using overlapped IO to do waits on the pipe and still be able to cancel and such. hPipe = ::CreateNamedPipeW(sczFullPipeName, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, PIPE_64KB, PIPE_64KB, 1, psd ? &sa : NULL); if (INVALID_HANDLE_VALUE == hPipe) { ExitWithLastError1(hr, "Failed to create pipe: %ls", sczFullPipeName); } if (fCreateCachePipe) { // Create the cache pipe. hr = StrAllocFormatted(&sczFullPipeName, CACHE_PIPE_NAME_FORMAT_STRING, pConnection->sczName); ExitOnFailure1(hr, "Failed to allocate full name of cache pipe: %ls", pConnection->sczName); hCachePipe = ::CreateNamedPipeW(sczFullPipeName, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, PIPE_64KB, PIPE_64KB, 1, NULL); if (INVALID_HANDLE_VALUE == hCachePipe) { ExitWithLastError1(hr, "Failed to create pipe: %ls", sczFullPipeName); } } pConnection->hCachePipe = hCachePipe; hCachePipe = INVALID_HANDLE_VALUE; pConnection->hPipe = hPipe; hPipe = INVALID_HANDLE_VALUE; // TODO: remove the following *phEvent = NULL; LExit: ReleaseFileHandle(hCachePipe); ReleaseFileHandle(hPipe); ReleaseStr(sczFullPipeName); if (psd) { ::LocalFree(psd); } return hr; }
static HRESULT DownloadResource( __in BURN_USER_EXPERIENCE* pUX, __in_z LPCWSTR wzPackageOrContainerId, __in_z LPCWSTR wzPayloadId, __in HINTERNET hSession, __inout_z LPWSTR* psczUrl, __in_z_opt LPCWSTR wzUser, __in_z_opt LPCWSTR wzPassword, __in_z LPCWSTR wzDestinationPath, __in DWORD64 dw64AuthoredResourceLength, __in DWORD64 dw64ResourceLength, __in DWORD64 dw64ResumeOffset, __in HANDLE hResumeFile, __in_opt BURN_CACHE_CALLBACK* pCallback ) { HRESULT hr = S_OK; HANDLE hPayloadFile = INVALID_HANDLE_VALUE; DWORD cbMaxData = 64 * 1024; // 64 KB BYTE* pbData = NULL; BOOL fRangeRequestsAccepted = TRUE; LPWSTR sczRangeRequestHeader = NULL; HINTERNET hConnect = NULL; HINTERNET hUrl = NULL; LONGLONG llLength = 0; hPayloadFile = ::CreateFileW(wzDestinationPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == hPayloadFile) { ExitWithLastError1(hr, "Failed to create download destination file: %ls", wzDestinationPath); } // Allocate a memory block on a page boundary in case we want to do optimal writing. pbData = static_cast<BYTE*>(::VirtualAlloc(NULL, cbMaxData, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE)); ExitOnNullWithLastError(pbData, hr, "Failed to allocate buffer to download files into."); // Let's try downloading the file assuming that range requests are accepted. If range requests // are not supported we'll have to start over and accept the fact that we only get one shot // downloading the file however big it is. Hopefully, not more than 2 GB since wininet doesn't // like files that big. while (fRangeRequestsAccepted && (0 == dw64ResourceLength || dw64ResumeOffset < dw64ResourceLength)) { hr = AllocateRangeRequestHeader(dw64ResumeOffset, 0 == dw64ResourceLength ? dw64AuthoredResourceLength : dw64ResourceLength, &sczRangeRequestHeader); ExitOnFailure(hr, "Failed to allocate range request header."); ReleaseNullInternet(hConnect); ReleaseNullInternet(hUrl); hr = MakeRequest(pUX, wzPackageOrContainerId, wzPayloadId, hSession, psczUrl, L"GET", sczRangeRequestHeader, wzUser, wzPassword, &hConnect, &hUrl, &fRangeRequestsAccepted); ExitOnFailure1(hr, "Failed to request URL for download: %ls", *psczUrl); // If we didn't get the size of the resource from the initial "HEAD" request // then let's try to get the size from this "GET" request. if (0 == dw64ResourceLength) { hr = InternetGetSizeByHandle(hUrl, &llLength); if (SUCCEEDED(hr)) { dw64ResourceLength = llLength; } else // server didn't tell us the resource length. { // Fallback to the authored size of the resource. However, since we // don't really know the size on the server, don't try to use // range requests either. dw64ResourceLength = dw64AuthoredResourceLength; fRangeRequestsAccepted = FALSE; } } // If we just tried to do a range request and found out that it isn't supported, start over. if (!fRangeRequestsAccepted) { // TODO: log a message that the server did not accept range requests. dw64ResumeOffset = 0; } hr = WriteToFile(hUrl, hPayloadFile, &dw64ResumeOffset, hResumeFile, dw64ResourceLength, pbData, cbMaxData, pCallback); ExitOnFailure1(hr, "Failed while reading from internet and writing to: %ls", wzDestinationPath); } LExit: ReleaseInternet(hUrl); ReleaseInternet(hConnect); ReleaseStr(sczRangeRequestHeader); if (pbData) { ::VirtualFree(pbData, 0, MEM_RELEASE); } ReleaseFileHandle(hPayloadFile); return hr; }
extern "C" HRESULT WininetDownloadUrl( __in BURN_USER_EXPERIENCE* pUX, __in BURN_CACHE_CALLBACK* pCallback, __in_z LPCWSTR wzPackageOrContainerId, __in_z LPCWSTR wzPayloadId, __in BURN_DOWNLOAD_SOURCE* pDownloadSource, __in DWORD64 dw64AuthoredDownloadSize, __in LPCWSTR wzDestinationPath ) { HRESULT hr = S_OK; LPWSTR sczUrl = NULL; HINTERNET hSession = NULL; DWORD dwTimeout = 0; LPWSTR sczResumePath = NULL; HANDLE hResumeFile = INVALID_HANDLE_VALUE; DWORD64 dw64ResumeOffset = 0; DWORD64 dw64Size = 0; FILETIME ftCreated = { }; // Copy the download source into a working variable to handle redirects then // open the internet session. hr = StrAllocString(&sczUrl, pDownloadSource->sczUrl, 0); ExitOnFailure(hr, "Failed to copy download source URL."); hSession = ::InternetOpenW(L"Burn", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); ExitOnNullWithLastError(hSession, hr, "Failed to open internet session"); // Make a best effort to set the download timeouts to 2 minutes or whatever policy says. PolcReadNumber(BURN_POLICY_REGISTRY_PATH, L"DownloadTimeout", 2 * 60, &dwTimeout); if (0 < dwTimeout) { dwTimeout *= 1000; // convert to milliseconds. ::InternetSetOptionW(hSession, INTERNET_OPTION_CONNECT_TIMEOUT, &dwTimeout, sizeof(dwTimeout)); ::InternetSetOptionW(hSession, INTERNET_OPTION_RECEIVE_TIMEOUT, &dwTimeout, sizeof(dwTimeout)); ::InternetSetOptionW(hSession, INTERNET_OPTION_SEND_TIMEOUT, &dwTimeout, sizeof(dwTimeout)); } // Get the resource size and creation time from the internet. hr = GetResourceMetadata(pUX, wzPackageOrContainerId, wzPayloadId, hSession, &sczUrl, pDownloadSource->sczUser, pDownloadSource->sczPassword, &dw64Size, &ftCreated); ExitOnFailure1(hr, "Failed to get size and time for URL: %ls", sczUrl); // Ignore failure to initialize resume because we will fall back to full download then // download. InitializeResume(wzDestinationPath, &sczResumePath, &hResumeFile, &dw64ResumeOffset); hr = DownloadResource(pUX, wzPackageOrContainerId, wzPayloadId, hSession, &sczUrl, pDownloadSource->sczUser, pDownloadSource->sczPassword, wzDestinationPath, dw64AuthoredDownloadSize, dw64Size, dw64ResumeOffset, hResumeFile, pCallback); ExitOnFailure1(hr, "Failed to download URL: %ls", sczUrl); // Cleanup the resume file because we successfully downloaded the whole file. if (sczResumePath && *sczResumePath) { ::DeleteFileW(sczResumePath); } LExit: ReleaseFileHandle(hResumeFile); ReleaseStr(sczResumePath); ReleaseInternet(hSession); ReleaseStr(sczUrl); return hr; }
/******************************************************************* Dutil_AssertMsg *******************************************************************/ extern "C" void DAPI Dutil_AssertMsg( __in_z LPCSTR szMessage ) { static BOOL fInAssert = FALSE; // TODO: make this thread safe (this is a cheap hack to prevent re-entrant Asserts) HRESULT hr = S_OK; DWORD er = ERROR_SUCCESS; int id = IDRETRY; HKEY hkDebug = NULL; HANDLE hAssertFile = INVALID_HANDLE_VALUE; char szPath[MAX_PATH] = { }; DWORD cch = 0; if (fInAssert) { return; } fInAssert = TRUE; char szMsg[DUTIL_STRING_BUFFER]; hr = ::StringCchCopyA(szMsg, countof(szMsg), szMessage); ExitOnFailure(hr, "failed to copy message while building assert message"); if (Dutil_pfnDisplayAssert) { // call custom function to display the assert string if (!Dutil_pfnDisplayAssert(szMsg)) { ExitFunction(); } } else { OutputDebugStringA(szMsg); } if (!Dutil_fNoAsserts) { er = ::RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Delivery\\Debug", 0, KEY_QUERY_VALUE, &hkDebug); if (ERROR_SUCCESS == er) { cch = countof(szPath); er = ::RegQueryValueExA(hkDebug, "DeliveryAssertsLog", NULL, NULL, reinterpret_cast<BYTE*>(szPath), &cch); szPath[countof(szPath) - 1] = '\0'; // ensure string is null terminated since registry won't guarantee that. if (ERROR_SUCCESS == er) { hAssertFile = ::CreateFileA(szPath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE != hAssertFile) { ::SetFilePointer(hAssertFile, 0, 0, FILE_END); ::StringCchCatA(szMsg, countof(szMsg), "\r\n"); ::WriteFile(hAssertFile, szMsg, lstrlenA(szMsg), &cch, NULL); } } } // if anything went wrong while fooling around with the registry, just show the usual assert dialog box if (ERROR_SUCCESS != er) { hr = ::StringCchCatA(szMsg, countof(szMsg), "\nAbort=Debug, Retry=Skip, Ignore=Skip all"); ExitOnFailure(hr, "failed to concat string while building assert message"); id = ::MessageBoxA(0, szMsg, "Debug Assert Message", MB_SERVICE_NOTIFICATION | MB_TOPMOST | MB_DEFBUTTON2 | MB_ABORTRETRYIGNORE); } } if (id == IDABORT) { if (Dutil_hAssertModule) { ::GetModuleFileNameA(Dutil_hAssertModule, szPath, countof(szPath)); hr = ::StringCchPrintfA(szMsg, countof(szMsg), "Module is running from: %s\nIf you are not using pdb-stamping, place your PDB near the module and attach to process id: %d (0x%x)", szPath, ::GetCurrentProcessId(), ::GetCurrentProcessId()); if (SUCCEEDED(hr)) { ::MessageBoxA(0, szMsg, "Debug Assert Message", MB_SERVICE_NOTIFICATION | MB_TOPMOST | MB_OK); } } ::DebugBreak(); } else if (id == IDIGNORE) { Dutil_fNoAsserts = TRUE; } LExit: ReleaseFileHandle(hAssertFile); ReleaseRegKey(hkDebug); fInAssert = FALSE; }