mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
d4eb7d5782
--HG-- extra : rebase_source : da55a4937383eda2baf7c9a362501da8ee664146
281 lines
10 KiB
C++
281 lines
10 KiB
C++
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include <shlwapi.h>
|
|
#include "servicebase.h"
|
|
#include "updatehelper.h"
|
|
#include "nsWindowsHelpers.h"
|
|
#define MAX_KEY_LENGTH 255
|
|
|
|
/**
|
|
* Writes a registry DWORD with a value of 1 at BASE_SERVICE_REG_KEY
|
|
* if the prefetch was cleared successfully.
|
|
*
|
|
* @return TRUE if successful.
|
|
*/
|
|
BOOL
|
|
WritePrefetchClearedReg()
|
|
{
|
|
HKEY baseKeyRaw;
|
|
LONG retCode = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
|
BASE_SERVICE_REG_KEY, 0,
|
|
KEY_WRITE | KEY_WOW64_64KEY, &baseKeyRaw);
|
|
if (retCode != ERROR_SUCCESS) {
|
|
LOG(("Could not open key for prefetch. (%d)\n", retCode));
|
|
return FALSE;
|
|
}
|
|
nsAutoRegKey baseKey(baseKeyRaw);
|
|
DWORD disabledValue = 1;
|
|
if (RegSetValueExW(baseKey, L"FFPrefetchDisabled", 0, REG_DWORD,
|
|
reinterpret_cast<LPBYTE>(&disabledValue),
|
|
sizeof(disabledValue)) != ERROR_SUCCESS) {
|
|
LOG(("Could not write prefetch cleared value to registry. (%d)\n",
|
|
GetLastError()));
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL
|
|
PrefetchFileAlreadyCleared(LPCWSTR prefetchPath)
|
|
{
|
|
DWORD attributes = GetFileAttributes(prefetchPath);
|
|
BOOL alreadyCleared = attributes != INVALID_FILE_ATTRIBUTES &&
|
|
attributes & FILE_ATTRIBUTE_READONLY;
|
|
|
|
nsAutoHandle prefetchFile(CreateFile(prefetchPath, GENERIC_READ, 0, NULL,
|
|
OPEN_EXISTING, 0, NULL));
|
|
LARGE_INTEGER fileSize = { 0, 0 };
|
|
alreadyCleared &= prefetchFile != INVALID_HANDLE_VALUE &&
|
|
GetFileSizeEx(prefetchFile, &fileSize) &&
|
|
fileSize.QuadPart == 0;
|
|
return alreadyCleared;
|
|
}
|
|
|
|
/**
|
|
* We found that prefetch actually causes large applications like Firefox
|
|
* to startup slower. This will get rid of the Windows prefetch files for
|
|
* applications like firefox (FIREFOX-*.pf files) and instead replace them
|
|
* with 0 byte files which are read only. Windows will not use prefetch
|
|
* if the associated prefetch file is a 0 byte read only file.
|
|
* Some of the problems with prefetch relating to Windows and Firefox:
|
|
* - Prefetch reads in a lot before we even run (fonts, plugins, etc...)
|
|
* - Prefetch happens before our code runs, so it delays UI showing.
|
|
* - Prefetch does not use windows readahead.
|
|
* Previous test data on an acer i7 laptop with a 5400rpm HD showed startup
|
|
* time difference was 1.6s (without prefetch) vs 2.6s+ with prefetch.
|
|
* The "Windows Internals" book mentions that prefetch can be disabled for
|
|
* the whole system only, so there is no other application specific way to
|
|
* disable it.
|
|
*
|
|
* @param prefetchProcessName The name of the process who's prefetch files
|
|
* should be cleared.
|
|
* @return TRUE if no errors occurred during the clear operation.
|
|
*/
|
|
BOOL
|
|
ClearPrefetch(LPCWSTR prefetchProcessName)
|
|
{
|
|
LOG(("Clearing prefetch files...\n"));
|
|
size_t prefetchProcessNameLength = wcslen(prefetchProcessName);
|
|
|
|
// Make sure we were passed in a safe value for the prefetch process name
|
|
// because it will be appended to a filepath and deleted.
|
|
// We check for < 2 to avoid things like "." as the path
|
|
// We check for a max path of MAX_PATH - 10 because we add \\ and '.EXE-*.pf'
|
|
if (wcsstr(prefetchProcessName, L"..") ||
|
|
wcsstr(prefetchProcessName, L"\\") ||
|
|
wcsstr(prefetchProcessName, L"*") ||
|
|
wcsstr(prefetchProcessName, L"/") ||
|
|
prefetchProcessNameLength < 2 ||
|
|
prefetchProcessNameLength >= (MAX_PATH - 10)) {
|
|
LOG(("Prefetch path to clear is not safe\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
// Quick shortcut to make sure we don't try to clear multiple times for
|
|
// different FIREFOX installs.
|
|
static WCHAR lastPrefetchProcessName[MAX_PATH - 10] = { '\0' };
|
|
if (!wcscmp(lastPrefetchProcessName, prefetchProcessName)) {
|
|
LOG(("Already processed process name\n"));
|
|
return FALSE;
|
|
}
|
|
wcscpy(lastPrefetchProcessName, prefetchProcessName);
|
|
|
|
// Obtain the windows prefetch directory path.
|
|
WCHAR prefetchPath[MAX_PATH + 1];
|
|
if (!GetWindowsDirectoryW(prefetchPath,
|
|
sizeof(prefetchPath) /
|
|
sizeof(prefetchPath[0]))) {
|
|
LOG(("Could not obtain windows directory\n"));
|
|
return FALSE;
|
|
}
|
|
if (!PathAppendSafe(prefetchPath, L"prefetch")) {
|
|
LOG(("Could not obtain prefetch directory\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
size_t prefetchDirLen = wcslen(prefetchPath);
|
|
WCHAR prefetchSearchFile[MAX_PATH + 1];
|
|
// We know this is safe based on the check at the start of this function
|
|
wsprintf(prefetchSearchFile, L"\\%s.EXE-*.pf", prefetchProcessName);
|
|
// Append the search file to the full path
|
|
wcscpy(prefetchPath + prefetchDirLen, prefetchSearchFile);
|
|
|
|
// Find the first path matching and get a find handle for future calls.
|
|
WIN32_FIND_DATAW findFileData;
|
|
HANDLE findHandle = FindFirstFileW(prefetchPath, &findFileData);
|
|
if (INVALID_HANDLE_VALUE == findHandle) {
|
|
if (GetLastError() == ERROR_FILE_NOT_FOUND) {
|
|
LOG(("No files matching firefox.exe prefetch path.\n"));
|
|
return TRUE;
|
|
} else {
|
|
LOG(("Error finding firefox.exe prefetch files. (%d)\n",
|
|
GetLastError()));
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
BOOL deletedAllFFPrefetch = TRUE;
|
|
size_t deletedCount = 0;
|
|
do {
|
|
// Reset back to the prefetch directory, we know from an above check that
|
|
// we aren't exceeding MAX_PATH + 1 characters with prefetchDirLen + 1.
|
|
// From above we know: prefetchPath[prefetchDirLen] == L'\\';
|
|
prefetchPath[prefetchDirLen + 1] = L'\0';
|
|
|
|
// Get the new file's prefetch path
|
|
LPWSTR filenameOffsetInBuffer = prefetchPath + prefetchDirLen + 1;
|
|
if (wcslen(findFileData.cFileName) + prefetchDirLen + 1 > MAX_PATH) {
|
|
LOG(("Error appending prefetch path %ls, path is too long. (%d)\n",
|
|
findFileData.cFileName, GetLastError()));
|
|
deletedAllFFPrefetch = FALSE;
|
|
continue;
|
|
}
|
|
if (!PathAppendSafe(filenameOffsetInBuffer, findFileData.cFileName)) {
|
|
LOG(("Error appending prefetch path %ls. (%d)\n", findFileData.cFileName,
|
|
GetLastError()));
|
|
deletedAllFFPrefetch = FALSE;
|
|
continue;
|
|
}
|
|
|
|
if (PrefetchFileAlreadyCleared(prefetchPath)) {
|
|
++deletedCount;
|
|
LOG(("Prefetch file already cleared: %ls\n", prefetchPath));
|
|
continue;
|
|
}
|
|
|
|
// Delete the prefetch file and replace it with a blank read only file
|
|
HANDLE prefetchFile =
|
|
CreateFile(prefetchPath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
|
|
if (INVALID_HANDLE_VALUE == prefetchFile) {
|
|
LOG(("Error replacing prefetch path %ls. (%d)\n", findFileData.cFileName,
|
|
GetLastError()));
|
|
deletedAllFFPrefetch = FALSE;
|
|
continue;
|
|
}
|
|
|
|
CloseHandle(prefetchFile);
|
|
|
|
DWORD attributes = GetFileAttributes(prefetchPath);
|
|
if (INVALID_FILE_ATTRIBUTES == attributes) {
|
|
LOG(("Could not get/set attributes on prefetch file: %ls. (%d)\n",
|
|
findFileData.cFileName, GetLastError()));
|
|
continue;
|
|
}
|
|
|
|
if (!SetFileAttributes(prefetchPath,
|
|
attributes | FILE_ATTRIBUTE_READONLY)) {
|
|
LOG(("Could not set read only on prefetch file: %ls. (%d)\n",
|
|
findFileData.cFileName, GetLastError()));
|
|
continue;
|
|
}
|
|
|
|
++deletedCount;
|
|
LOG(("Prefetch file cleared and set to read-only successfully: %ls\n",
|
|
prefetchPath));
|
|
} while (FindNextFileW(findHandle, &findFileData));
|
|
LOG(("Done searching prefetch paths. (%d)\n", GetLastError()));
|
|
|
|
// Cleanup after ourselves.
|
|
FindClose(findHandle);
|
|
|
|
if (deletedAllFFPrefetch && deletedCount > 0) {
|
|
WritePrefetchClearedReg();
|
|
}
|
|
|
|
return deletedAllFFPrefetch;
|
|
}
|
|
|
|
/**
|
|
* Clears all prefetch files specified in the installation dirctories
|
|
* @return FALSE if there was an error clearing the known prefetch directories
|
|
*/
|
|
BOOL
|
|
ClearKnownPrefetch()
|
|
{
|
|
// The service always uses the 64-bit registry key
|
|
HKEY baseKeyRaw;
|
|
LONG retCode = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
|
BASE_SERVICE_REG_KEY, 0,
|
|
KEY_READ | KEY_WOW64_64KEY, &baseKeyRaw);
|
|
if (retCode != ERROR_SUCCESS) {
|
|
LOG(("Could not open maintenance service base key. (%d)\n", retCode));
|
|
return FALSE;
|
|
}
|
|
nsAutoRegKey baseKey(baseKeyRaw);
|
|
|
|
// Get the number of subkeys.
|
|
DWORD subkeyCount = 0;
|
|
retCode = RegQueryInfoKeyW(baseKey, NULL, NULL, NULL, &subkeyCount, NULL,
|
|
NULL, NULL, NULL, NULL, NULL, NULL);
|
|
if (retCode != ERROR_SUCCESS) {
|
|
LOG(("Could not query info key: %d\n", retCode));
|
|
return FALSE;
|
|
}
|
|
|
|
// Enumerate the subkeys, each subkey represents an install
|
|
for (DWORD i = 0; i < subkeyCount; i++) {
|
|
WCHAR subkeyBuffer[MAX_KEY_LENGTH];
|
|
DWORD subkeyBufferCount = MAX_KEY_LENGTH;
|
|
retCode = RegEnumKeyExW(baseKey, i, subkeyBuffer,
|
|
&subkeyBufferCount, NULL,
|
|
NULL, NULL, NULL);
|
|
if (retCode != ERROR_SUCCESS) {
|
|
LOG(("Could not enum installations: %d\n", retCode));
|
|
return FALSE;
|
|
}
|
|
|
|
// Open the subkey
|
|
HKEY subKeyRaw;
|
|
retCode = RegOpenKeyExW(baseKey,
|
|
subkeyBuffer,
|
|
0,
|
|
KEY_READ | KEY_WOW64_64KEY,
|
|
&subKeyRaw);
|
|
nsAutoRegKey subKey(subKeyRaw);
|
|
if (retCode != ERROR_SUCCESS) {
|
|
LOG(("Could not open subkey: %d\n", retCode));
|
|
continue; // Try the next subkey
|
|
}
|
|
|
|
const int MAX_CHAR_COUNT = 256;
|
|
DWORD valueBufSize = MAX_CHAR_COUNT * sizeof(WCHAR);
|
|
WCHAR prefetchProcessName[MAX_CHAR_COUNT] = { L'\0' };
|
|
|
|
// Get the prefetch process name from the registry
|
|
retCode = RegQueryValueExW(subKey, L"prefetchProcessName", 0, NULL,
|
|
(LPBYTE)prefetchProcessName, &valueBufSize);
|
|
if (retCode != ERROR_SUCCESS) {
|
|
LOG(("Could not obtain process name from registry: %d\n", retCode));
|
|
continue; // Try the next subkey
|
|
}
|
|
|
|
// The value for prefetch process name comes from HKLM so is trusted.
|
|
// We will do some sanity checks on it though inside ClearPrefetch.
|
|
ClearPrefetch(prefetchProcessName);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|