From: Sven Baars sbaars@codeweavers.com
--- dlls/advapi32/tests/registry.c | 2 +- dlls/kernelbase/registry.c | 104 ++++++++++++++++++++++++++++++++- 2 files changed, 102 insertions(+), 4 deletions(-)
diff --git a/dlls/advapi32/tests/registry.c b/dlls/advapi32/tests/registry.c index b4f0398b358..ecc71ec8db8 100644 --- a/dlls/advapi32/tests/registry.c +++ b/dlls/advapi32/tests/registry.c @@ -2778,7 +2778,7 @@ static void test_redirection(void)
/* verify subkey is not present in native mode */ err = RegOpenKeyExA(native, "AWineTest", 0, KEY_ALL_ACCESS, &key); - ok(err == ERROR_FILE_NOT_FOUND, "got %li\n", err); + todo_wine_if(ptr_size == 64) ok(err == ERROR_FILE_NOT_FOUND, "got %li\n", err);
err = pRegDeleteKeyExA(op_key, "AWineTest", opposite, 0); ok(err == ERROR_SUCCESS, "got %li\n", err); diff --git a/dlls/kernelbase/registry.c b/dlls/kernelbase/registry.c index d7f69ed687c..bd2d665c705 100644 --- a/dlls/kernelbase/registry.c +++ b/dlls/kernelbase/registry.c @@ -103,7 +103,52 @@ static BOOL is_wow6432node( const UNICODE_STRING *name ) return (name->Length >= 11 * sizeof(WCHAR) && !wcsnicmp( name->Buffer, L"Wow6432Node", 11 )); }
-/* open the Wow6432Node subkey of the specified key */ +static BOOL is_classes_root( const UNICODE_STRING *name ) +{ + return (name->Length >= wcslen(root_key_names[0]) * sizeof(WCHAR) && !wcsnicmp( name->Buffer, root_key_names[0], wcslen(root_key_names[0]) )); +} + +static BOOL is_classes_wow6432node( HKEY key ) +{ + char buffer[256], *buf_ptr = buffer; + KEY_NAME_INFORMATION *info = (KEY_NAME_INFORMATION *)buffer; + UNICODE_STRING name; + NTSTATUS status; + DWORD len = sizeof(buffer); + BOOL ret = FALSE; + + /* Obtain the name of the root key */ + status = NtQueryKey( key, KeyNameInformation, info, len, &len ); + if (status && status != STATUS_BUFFER_OVERFLOW) return FALSE; + + /* Retry with a dynamically allocated buffer */ + while (status == STATUS_BUFFER_OVERFLOW) + { + if (buf_ptr != buffer) heap_free( buf_ptr ); + if (!(buf_ptr = heap_alloc( len ))) return FALSE; + info = (KEY_NAME_INFORMATION *)buf_ptr; + status = NtQueryKey( key, KeyNameInformation, info, len, &len ); + } + + /* Check if the key ends in Wow6432Node and if the root is the Classes key*/ + if (!status && info->NameLength / sizeof(WCHAR) >= 11) + { + name.Buffer = info->Name + info->NameLength / sizeof(WCHAR) - 11; + name.Length = 11 * sizeof(WCHAR); + if (is_wow6432node( &name )) + { + name.Buffer = info->Name; + name.Length = info->NameLength; + ret = is_classes_root( &name ); + } + } + + if (buf_ptr != buffer) heap_free( buf_ptr ); + + return ret; +} + +/* Open the Wow6432Node subkey of the specified key */ static HANDLE open_wow6432node( HANDLE key ) { OBJECT_ATTRIBUTES attr; @@ -117,7 +162,25 @@ static HANDLE open_wow6432node( HANDLE key ) attr.SecurityDescriptor = NULL; attr.SecurityQualityOfService = NULL; RtlInitUnicodeString( &nameW, L"Wow6432Node" ); - if (NtOpenKeyEx( &ret, MAXIMUM_ALLOWED, &attr, 0 )) ret = 0; + if (NtOpenKeyEx( &ret, MAXIMUM_ALLOWED, &attr, 0 )) return key; + return ret; +} + +/* Open HKCR, which should already exist because it's used when we're in its Wow6432Node child */ +static HANDLE open_classes_root( void ) +{ + OBJECT_ATTRIBUTES attr; + UNICODE_STRING nameW; + HANDLE ret = 0; + + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.ObjectName = &nameW; + attr.Attributes = 0; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + RtlInitUnicodeString( &nameW, root_key_names[0] ); + NtOpenKeyEx( &ret, MAXIMUM_ALLOWED, &attr, 0 ); return ret; }
@@ -177,6 +240,21 @@ static NTSTATUS open_subkey( HKEY *subkey, HKEY root, UNICODE_STRING *name, DWOR }
status = NtOpenKeyEx( (HANDLE *)subkey, access_64, &attr, options ); + if (status == STATUS_OBJECT_NAME_NOT_FOUND && root && is_wow64_key) + { + /* Try to open the shared parent if we can't find the key in the Wow6432Node */ + if (!is_classes_wow6432node( root )) + return STATUS_OBJECT_NAME_NOT_FOUND; + + attr.RootDirectory = open_classes_root(); + status = NtOpenKeyEx( (HANDLE *)subkey, access_64, &attr, options ); + + if (!status) + NtClose( attr.RootDirectory ); + else + *subkey = attr.RootDirectory; + } + if (status == STATUS_PREDEFINED_HANDLE) { *subkey = get_perflib_key( *subkey ); @@ -193,7 +271,7 @@ static NTSTATUS open_subkey( HKEY *subkey, HKEY root, UNICODE_STRING *name, DWOR if (is_wow64_key && !is_wow6432node( name )) { HKEY wow6432node = open_wow6432node( *subkey ); - if (wow6432node) + if (wow6432node != *subkey) { NtClose( *subkey ); *subkey = wow6432node; @@ -207,16 +285,36 @@ static NTSTATUS open_subkey( HKEY *subkey, HKEY root, UNICODE_STRING *name, DWOR /* wrapper for NtOpenKeyEx that handles Wow6432 nodes but also returns a key on error */ static NTSTATUS unsafe_open_key( HKEY *retkey, HKEY root, UNICODE_STRING *name, DWORD options, ACCESS_MASK access ) { + BOOL is_wow64_key = (access & KEY_WOW64_32KEY) || (is_wow64 && !(access & KEY_WOW64_64KEY)); HKEY subkey = 0, subkey_root = root; + BOOL was_wow6432node = TRUE; NTSTATUS status = 0;
+ if (root && is_wow64 && !(access & KEY_WOW64_64KEY) && !is_wow6432node( name )) + { + subkey_root = open_wow6432node( root ); + if (!is_classes_wow6432node( subkey_root ) && subkey_root != root) + { + NtClose( subkey_root ); + subkey_root = root; + } + } + while (!status && (name->Length || !subkey)) { + was_wow6432node = is_wow6432node( name ); status = open_subkey( &subkey, subkey_root, name, options, access ); if (subkey && subkey_root && subkey_root != root) NtClose( subkey_root ); if (subkey) subkey_root = subkey; }
+ /* Return the shared parent if we didn't explicitly look for the Wow6432Node */ + if (!status && !was_wow6432node && is_wow64_key && is_classes_wow6432node( subkey_root )) + { + if (subkey_root && subkey_root != root) NtClose( subkey_root ); + subkey_root = open_classes_root(); + } + *retkey = subkey_root;
return status;