Signed-off-by: Dmitry Timoshkov dmitry@baikal.ru --- dlls/advapi32/registry.c | 244 ++++++++++++++++++++++++++++++++- dlls/advapi32/tests/registry.c | 17 +-- 2 files changed, 249 insertions(+), 12 deletions(-)
diff --git a/dlls/advapi32/registry.c b/dlls/advapi32/registry.c index 087d47f772..ac75e7b577 100644 --- a/dlls/advapi32/registry.c +++ b/dlls/advapi32/registry.c @@ -2,6 +2,7 @@ * Registry management * * Copyright (C) 1999 Alexandre Julliard + * Copyright (C) 2017 Dmitry Timoshkov * * Based on misc/registry.c code * Copyright (C) 1996 Marcus Meissner @@ -36,6 +37,7 @@ #include "winreg.h" #include "winerror.h" #include "winternl.h" +#include "winperf.h" #include "winuser.h" #include "sddl.h" #include "advapi32_misc.h" @@ -1480,6 +1482,234 @@ LONG WINAPI RegSetKeyValueA( HKEY hkey, LPCSTR subkey, LPCSTR name, DWORD type, return ret; }
+struct perf_provider +{ + HMODULE perflib; + PM_OPEN_PROC *pOpen; + PM_CLOSE_PROC *pClose; + PM_COLLECT_PROC *pCollect; +}; + +static void *get_provider_entry(HKEY perf, HMODULE perflib, const char *name) +{ + char buf[MAX_PATH]; + DWORD err, type, len; + + len = sizeof(buf) - 1; + err = RegQueryValueExA(perf, name, NULL, &type, (BYTE *)buf, &len); + if (err != ERROR_SUCCESS || type != REG_SZ) + return NULL; + + buf[len] = 0; + TRACE("Loading function pointer for %s: %s\n", name, debugstr_a(buf)); + + return GetProcAddress(perflib, buf); +} + +static BOOL load_provider(HKEY root, const WCHAR *name, struct perf_provider *provider) +{ + static const WCHAR performanceW[] = { 'P','e','r','f','o','r','m','a','n','c','e',0 }; + static const WCHAR libraryW[] = { 'L','i','b','r','a','r','y',0 }; + WCHAR buf[MAX_PATH], buf2[MAX_PATH]; + DWORD err, type, len; + HKEY service, perf; + + err = RegOpenKeyExW(root, name, 0, KEY_READ, &service); + if (err != ERROR_SUCCESS) + return FALSE; + + err = RegOpenKeyExW(service, performanceW, 0, KEY_READ, &perf); + RegCloseKey(service); + if (err != ERROR_SUCCESS) + return FALSE; + + len = sizeof(buf) - sizeof(WCHAR); + err = RegQueryValueExW(perf, libraryW, NULL, &type, (BYTE *)buf, &len); + if (err != ERROR_SUCCESS || !(type == REG_SZ || type == REG_EXPAND_SZ)) + goto error; + + buf[len / sizeof(WCHAR)] = 0; + if (type == REG_EXPAND_SZ) + { + len = ExpandEnvironmentStringsW(buf, buf2, MAX_PATH); + if (!len || len > MAX_PATH) goto error; + strcpyW(buf, buf2); + } + + if (!(provider->perflib = LoadLibraryW(buf))) + { + WARN("Failed to load %s\n", debugstr_w(buf)); + goto error; + } + + GetModuleFileNameW(provider->perflib, buf, MAX_PATH); + TRACE("Loaded provider %s\n", wine_dbgstr_w(buf)); + + provider->pOpen = get_provider_entry(perf, provider->perflib, "Open"); + provider->pClose = get_provider_entry(perf, provider->perflib, "Close"); + provider->pCollect = get_provider_entry(perf, provider->perflib, "Collect"); + if (provider->pOpen && provider->pClose && provider->pCollect) + { + RegCloseKey(perf); + return TRUE; + } + + TRACE("Provider is missing required exports\n"); + FreeLibrary(provider->perflib); + +error: + RegCloseKey(perf); + return FALSE; +} + +static DWORD collect_data(struct perf_provider *provider, const WCHAR *query, void **data, DWORD *size, DWORD *obj_count) +{ + DWORD err; + + err = provider->pOpen(NULL); + if (err != ERROR_SUCCESS) + { + TRACE("Open error %u (%#x)\n", err, err); + return err; + } + + *obj_count = 0; + err = provider->pCollect((WCHAR *)query, data, size, obj_count); + if (err != ERROR_SUCCESS) + { + TRACE("Collect error %u (%#x)\n", err, err); + *obj_count = 0; + } + + provider->pClose(); + return err; +} + +#define MAX_SERVICE_NAME 260 + +static DWORD query_perf_data(const WCHAR *query, DWORD *type, void *data, DWORD *ret_size) +{ + static const WCHAR SZ_SERVICES_KEY[] = { 'S','y','s','t','e','m','\', + 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\', + 'S','e','r','v','i','c','e','s',0 }; + DWORD err, i, data_size; + HKEY root; + PERF_DATA_BLOCK *pdb; + + if (!ret_size) + return ERROR_INVALID_PARAMETER; + + data_size = *ret_size; + *ret_size = 0; + + if (type) + *type = REG_BINARY; + + if (!data || data_size < sizeof(*pdb)) + return ERROR_MORE_DATA; + + pdb = data; + + pdb->Signature[0] = 'P'; + pdb->Signature[1] = 'E'; + pdb->Signature[2] = 'R'; + pdb->Signature[3] = 'F'; +#ifdef WORDS_BIGENDIAN + pdb->LittleEndian = FALSE; +#else + pdb->LittleEndian = TRUE; +#endif + pdb->Version = PERF_DATA_VERSION; + pdb->Revision = PERF_DATA_REVISION; + pdb->TotalByteLength = 0; + pdb->HeaderLength = sizeof(*pdb); + pdb->NumObjectTypes = 0; + pdb->DefaultObject = 0; + QueryPerformanceCounter(&pdb->PerfTime); + QueryPerformanceFrequency(&pdb->PerfFreq); + + data = pdb + 1; + pdb->SystemNameOffset = sizeof(*pdb); + pdb->SystemNameLength = (data_size - sizeof(*pdb)) / sizeof(WCHAR); + if (!GetComputerNameW(data, &pdb->SystemNameLength)) + return ERROR_MORE_DATA; + + pdb->SystemNameLength++; + pdb->SystemNameLength *= sizeof(WCHAR); + + pdb->HeaderLength += pdb->SystemNameLength; + + /* align to 8 bytes */ + if (pdb->SystemNameLength & 7) + pdb->HeaderLength += 8 - (pdb->SystemNameLength & 7); + + if (data_size < pdb->HeaderLength) + return ERROR_MORE_DATA; + + pdb->TotalByteLength = pdb->HeaderLength; + + data_size -= pdb->HeaderLength; + data = (char *)data + pdb->HeaderLength; + + err = RegOpenKeyExW(HKEY_LOCAL_MACHINE, SZ_SERVICES_KEY, 0, KEY_READ, &root); + if (err != ERROR_SUCCESS) + return err; + + i = 0; + for (;;) + { + DWORD collected_size = data_size, obj_count = 0; + struct perf_provider provider; + WCHAR name[MAX_SERVICE_NAME]; + void *collected_data = data; + + err = RegEnumKeyW(root, i++, name, MAX_SERVICE_NAME); + if (err == ERROR_NO_MORE_ITEMS) + { + err = ERROR_SUCCESS; + break; + } + + if (err != ERROR_SUCCESS) + continue; + + if (!load_provider(root, name, &provider)) + continue; + + err = collect_data(&provider, query, &collected_data, &collected_size, &obj_count); + FreeLibrary(provider.perflib); + + if (err == ERROR_MORE_DATA) + break; + + if (err == ERROR_SUCCESS) + { + PERF_OBJECT_TYPE *obj = (PERF_OBJECT_TYPE *)data; + + TRACE("Collect: obj->TotalByteLength %u, collected_size %u\n", + obj->TotalByteLength, collected_size); + + data_size -= collected_size; + data = collected_data; + + pdb->TotalByteLength += collected_size; + pdb->NumObjectTypes += obj_count; + } + } + + RegCloseKey(root); + + if (err == ERROR_SUCCESS) + { + *ret_size = pdb->TotalByteLength; + + GetSystemTime(&pdb->SystemTime); + GetSystemTimeAsFileTime((FILETIME *)&pdb->PerfTime100nSec); + } + + return err; +} + /****************************************************************************** * RegQueryValueExW [ADVAPI32.@] * @@ -1500,6 +1730,10 @@ LSTATUS WINAPI RegQueryValueExW( HKEY hkey, LPCWSTR name, LPDWORD reserved, LPDW (count && data) ? *count : 0 );
if ((data && !count) || reserved) return ERROR_INVALID_PARAMETER; + + if (hkey == HKEY_PERFORMANCE_DATA) + return query_perf_data(name, type, data, count); + if (!(hkey = get_special_root_hkey( hkey, 0 ))) return ERROR_INVALID_HANDLE;
RtlInitUnicodeString( &name_str, name ); @@ -1590,7 +1824,8 @@ LSTATUS WINAPI DECLSPEC_HOTPATCH RegQueryValueExA( HKEY hkey, LPCSTR name, LPDWO hkey, debugstr_a(name), reserved, type, data, count, count ? *count : 0 );
if ((data && !count) || reserved) return ERROR_INVALID_PARAMETER; - if (!(hkey = get_special_root_hkey( hkey, 0 ))) return ERROR_INVALID_HANDLE; + if (hkey != HKEY_PERFORMANCE_DATA && !(hkey = get_special_root_hkey( hkey, 0 ))) + return ERROR_INVALID_HANDLE;
if (count) datalen = *count; if (!data && count) *count = 0; @@ -1602,6 +1837,13 @@ LSTATUS WINAPI DECLSPEC_HOTPATCH RegQueryValueExA( HKEY hkey, LPCSTR name, LPDWO if ((status = RtlAnsiStringToUnicodeString( &nameW, &nameA, TRUE ))) return RtlNtStatusToDosError(status);
+ if (hkey == HKEY_PERFORMANCE_DATA) + { + DWORD ret = query_perf_data( nameW.Buffer, type, data, count ); + RtlFreeUnicodeString( &nameW ); + return ret; + } + status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, buffer, sizeof(buffer), &total_size ); if (status && status != STATUS_BUFFER_OVERFLOW) goto done; diff --git a/dlls/advapi32/tests/registry.c b/dlls/advapi32/tests/registry.c index 1573b6c03b..5e3535354c 100644 --- a/dlls/advapi32/tests/registry.c +++ b/dlls/advapi32/tests/registry.c @@ -3652,10 +3652,10 @@ static void test_RegQueryValueExPerformanceData(void)
/* Test with data == NULL */ dwret = RegQueryValueExA( HKEY_PERFORMANCE_DATA, "Global", NULL, NULL, NULL, &cbData ); - todo_wine ok( dwret == ERROR_MORE_DATA, "expected ERROR_MORE_DATA, got %d\n", dwret ); + ok( dwret == ERROR_MORE_DATA, "expected ERROR_MORE_DATA, got %d\n", dwret );
dwret = RegQueryValueExW( HKEY_PERFORMANCE_DATA, globalW, NULL, NULL, NULL, &cbData ); - todo_wine ok( dwret == ERROR_MORE_DATA, "expected ERROR_MORE_DATA, got %d\n", dwret ); + ok( dwret == ERROR_MORE_DATA, "expected ERROR_MORE_DATA, got %d\n", dwret );
/* Test ERROR_MORE_DATA, start with small buffer */ len = 10; @@ -3663,8 +3663,7 @@ static void test_RegQueryValueExPerformanceData(void) cbData = len; type = 0xdeadbeef; dwret = RegQueryValueExA( HKEY_PERFORMANCE_DATA, "Global", NULL, &type, value, &cbData ); - todo_wine ok( dwret == ERROR_MORE_DATA, "expected ERROR_MORE_DATA, got %d\n", dwret ); -todo_wine + ok( dwret == ERROR_MORE_DATA, "expected ERROR_MORE_DATA, got %d\n", dwret ); ok(type == REG_BINARY, "got %u\n", type); while( dwret == ERROR_MORE_DATA && limit) { @@ -3677,14 +3676,13 @@ todo_wine } ok(limit > 0, "too many times ERROR_MORE_DATA returned\n");
- todo_wine ok(dwret == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %d\n", dwret); -todo_wine + ok(dwret == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %d\n", dwret); ok(type == REG_BINARY, "got %u\n", type);
/* Check returned data */ if (dwret == ERROR_SUCCESS) { - todo_wine ok(len >= sizeof(PERF_DATA_BLOCK), "got size %d\n", len); + ok(len >= sizeof(PERF_DATA_BLOCK), "got size %d\n", len); if (len >= sizeof(PERF_DATA_BLOCK)) { pdb = (PERF_DATA_BLOCK*) value; ok(pdb->Signature[0] == 'P', "expected Signature[0] = 'P', got 0x%x\n", pdb->Signature[0]); @@ -3701,13 +3699,11 @@ todo_wine { cbData = 0xdeadbeef; dwret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, names[i], NULL, NULL, NULL, &cbData); -todo_wine ok(dwret == ERROR_MORE_DATA, "%u/%s: got %u\n", i, names[i], dwret); ok(cbData == 0, "got %u\n", cbData);
cbData = 0; dwret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, names[i], NULL, NULL, NULL, &cbData); -todo_wine ok(dwret == ERROR_MORE_DATA, "%u/%s: got %u\n", i, names[i], dwret); ok(cbData == 0, "got %u\n", cbData);
@@ -3740,9 +3736,7 @@ todo_wine type = 0xdeadbeef; cbData = sizeof(buf); dwret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "invalid counter name", NULL, &type, buf, &cbData); -todo_wine ok(dwret == ERROR_SUCCESS, "got %u\n", dwret); -todo_wine ok(type == REG_BINARY, "got %u\n", type); if (dwret == ERROR_SUCCESS) { @@ -3772,6 +3766,7 @@ todo_wine ok(pdb->TotalByteLength == len, "got %u vs %u\n", pdb->TotalByteLength, len); ok(pdb->HeaderLength == pdb->TotalByteLength, "got %u\n", pdb->HeaderLength); ok(pdb->NumObjectTypes == 0, "got %u\n", pdb->NumObjectTypes); +todo_wine ok(pdb->DefaultObject != 0, "got %u\n", pdb->DefaultObject); ok(pdb->SystemTime.wYear == st.wYear, "got %u\n", pdb->SystemTime.wYear); ok(pdb->SystemTime.wMonth == st.wMonth, "got %u\n", pdb->SystemTime.wMonth);