This reimplements all logic from the server in kernelbase, which is needed because Nt*Key ignores KEY_WOW64_32KEY. Unfortunately this requires a lot of server calls.
The full branch can be found here
https://gitlab.winehq.org/sbaars/wine/-/tree/shared-classes-new
The old approach that doesn't duplicate code to kernelbase can be found here
https://gitlab.winehq.org/sbaars/wine/-/tree/shared-classes-old
I ran make_requests by the way, not sure if we should still leave that out with the gitlab workflow.
-- v4: kernelbase: Recursively obtain the Wow6432Node parent. kernelbase: Add support for shared registry keys. kernelbase: Factor out the common parts of open_key() and create_key().
From: Sven Baars sbaars@codeweavers.com
--- dlls/kernelbase/registry.c | 134 ++++++++++++++++++++++--------------- 1 file changed, 79 insertions(+), 55 deletions(-)
diff --git a/dlls/kernelbase/registry.c b/dlls/kernelbase/registry.c index 91462d80e06..135ff48bf7e 100644 --- a/dlls/kernelbase/registry.c +++ b/dlls/kernelbase/registry.c @@ -100,7 +100,7 @@ static inline BOOL is_version_nt(void)
static BOOL is_wow6432node( const UNICODE_STRING *name ) { - return (name->Length == 11 * sizeof(WCHAR) && !wcsnicmp( name->Buffer, L"Wow6432Node", 11 )); + return (name->Length >= 11 * sizeof(WCHAR) && !wcsnicmp( name->Buffer, L"Wow6432Node", 11 )); }
/* open the Wow6432Node subkey of the specified key */ @@ -214,19 +214,77 @@ static NTSTATUS create_key( HKEY *retkey, ACCESS_MASK access, OBJECT_ATTRIBUTES return status; }
-/* wrapper for NtOpenKeyEx to handle Wow6432 nodes */ -static NTSTATUS open_key( HKEY *retkey, DWORD options, ACCESS_MASK access, OBJECT_ATTRIBUTES *attr ) +static NTSTATUS open_subkey( HKEY *subkey, HKEY root, UNICODE_STRING *name, DWORD options, ACCESS_MASK access ) { - NTSTATUS status; - BOOL force_wow32 = is_win64 && (access & KEY_WOW64_32KEY); - HANDLE subkey, root = attr->RootDirectory; - WCHAR *buffer = attr->ObjectName->Buffer; - DWORD pos = 0, i = 0, len = attr->ObjectName->Length / sizeof(WCHAR); + DWORD i = 0, len = name->Length / sizeof(WCHAR); + WCHAR *buffer = name->Buffer; + OBJECT_ATTRIBUTES attr; UNICODE_STRING str; + NTSTATUS status = 0; + + attr.Length = sizeof(attr); + attr.RootDirectory = (HANDLE)root; + attr.ObjectName = &str; + attr.Attributes = 0; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + + if (!root && len > 10 && !wcsnicmp( buffer, L"\Registry\", 10 )) i += 10; + if (i < len && buffer[i] == '\') return STATUS_OBJECT_PATH_INVALID; + while (i < len && buffer[i] != '\') i++; + + str.Buffer = name->Buffer; + str.Length = i * sizeof(WCHAR); + + if (i == len) + { + if (options & REG_OPTION_OPEN_LINK) attr.Attributes |= OBJ_OPENLINK; + status = NtOpenKeyEx( (HANDLE *)subkey, access, &attr, options ); + } + else + { + if (!(options & REG_OPTION_OPEN_LINK)) attr.Attributes &= ~OBJ_OPENLINK; + status = NtOpenKeyEx( (HANDLE *)subkey, access, &attr, options & ~REG_OPTION_OPEN_LINK ); + } + + if (status == STATUS_PREDEFINED_HANDLE) + { + *subkey = get_perflib_key( *subkey ); + status = STATUS_SUCCESS; + } + + if (!status) + { + while (i < len && buffer[i] == '\') i++; + + name->Buffer += i; + name->Length -= i * sizeof(WCHAR); + + if (!is_wow6432node( name )) + { + HKEY wow6432node = open_wow6432node( *subkey ); + if (wow6432node) + { + NtClose( *subkey ); + *subkey = wow6432node; + } + } + } + + return status; +} + + +/* wrapper for NtOpenKeyEx to handle Wow6432 nodes */ +static NTSTATUS open_key( HKEY *retkey, HKEY root, DWORD options, ACCESS_MASK access, OBJECT_ATTRIBUTES *attr ) +{ + HKEY subkey, subkey_root = root; + UNICODE_STRING name; + NTSTATUS status = 0;
*retkey = NULL;
- if (!force_wow32) + if (!(is_win64 && (access & KEY_WOW64_32KEY))) { if (options & REG_OPTION_OPEN_LINK) attr->Attributes |= OBJ_OPENLINK; status = NtOpenKeyEx( (HANDLE *)retkey, access, attr, options ); @@ -238,53 +296,19 @@ static NTSTATUS open_key( HKEY *retkey, DWORD options, ACCESS_MASK access, OBJEC return status; }
- if (len && buffer[0] == '\') return STATUS_OBJECT_PATH_INVALID; - while (i < len && buffer[i] != '\') i++; - attr->ObjectName = &str; + name.Buffer = attr->ObjectName->Buffer; + name.Length = attr->ObjectName->Length;
- for (;;) - { - str.Buffer = buffer + pos; - str.Length = (i - pos) * sizeof(WCHAR); - if (force_wow32 && pos) - { - if (is_wow6432node( &str )) force_wow32 = FALSE; - else if ((subkey = open_wow6432node( attr->RootDirectory ))) - { - if (attr->RootDirectory != root) NtClose( attr->RootDirectory ); - attr->RootDirectory = subkey; - force_wow32 = FALSE; - } - } - if (i == len) - { - if (options & REG_OPTION_OPEN_LINK) attr->Attributes |= OBJ_OPENLINK; - status = NtOpenKeyEx( &subkey, access, attr, options ); - } - else - { - if (!(options & REG_OPTION_OPEN_LINK)) attr->Attributes &= ~OBJ_OPENLINK; - status = NtOpenKeyEx( &subkey, access, attr, options & ~REG_OPTION_OPEN_LINK ); - } - if (attr->RootDirectory != root) NtClose( attr->RootDirectory ); - if (status) return status; - attr->RootDirectory = subkey; - if (i == len) break; - while (i < len && buffer[i] == '\') i++; - pos = i; - while (i < len && buffer[i] != '\') i++; - } - if (force_wow32 && (subkey = open_wow6432node( attr->RootDirectory ))) - { - if (attr->RootDirectory != root) NtClose( attr->RootDirectory ); - attr->RootDirectory = subkey; - } - if (status == STATUS_PREDEFINED_HANDLE) + while (!status && name.Length) { - attr->RootDirectory = get_perflib_key( attr->RootDirectory ); - status = STATUS_SUCCESS; + status = open_subkey( &subkey, subkey_root, &name, options, access ); + if (subkey_root && subkey_root != root) NtClose( subkey_root ); + subkey_root = subkey; } - *retkey = attr->RootDirectory; + + if (!status) + *retkey = subkey_root; + return status; }
@@ -538,7 +562,7 @@ LSTATUS WINAPI DECLSPEC_HOTPATCH RegOpenKeyExW( HKEY hkey, LPCWSTR name, DWORD o attr.SecurityDescriptor = NULL; attr.SecurityQualityOfService = NULL; RtlInitUnicodeString( &nameW, name ); - return RtlNtStatusToDosError( open_key( retkey, options, access, &attr ) ); + return RtlNtStatusToDosError( open_key( retkey, hkey, options, access, &attr ) ); }
@@ -596,7 +620,7 @@ LSTATUS WINAPI DECLSPEC_HOTPATCH RegOpenKeyExA( HKEY hkey, LPCSTR name, DWORD op if (!(status = RtlAnsiStringToUnicodeString( &NtCurrentTeb()->StaticUnicodeString, &nameA, FALSE ))) { - status = open_key( retkey, options, access, &attr ); + status = open_key( retkey, hkey, options, access, &attr ); } return RtlNtStatusToDosError( status ); }
From: Sven Baars sbaars@codeweavers.com
--- dlls/kernelbase/registry.c | 179 +++++++++++++++++++++---------------- 1 file changed, 101 insertions(+), 78 deletions(-)
diff --git a/dlls/kernelbase/registry.c b/dlls/kernelbase/registry.c index 135ff48bf7e..f490c849c17 100644 --- a/dlls/kernelbase/registry.c +++ b/dlls/kernelbase/registry.c @@ -140,82 +140,10 @@ static HKEY get_perflib_key( HANDLE key ) return key; }
-/* wrapper for NtCreateKey that creates the key recursively if necessary */ -static NTSTATUS create_key( HKEY *retkey, ACCESS_MASK access, OBJECT_ATTRIBUTES *attr, - const UNICODE_STRING *class, ULONG options, PULONG dispos ) -{ - BOOL force_wow32 = is_win64 && (access & KEY_WOW64_32KEY); - NTSTATUS status = STATUS_OBJECT_NAME_NOT_FOUND; - HANDLE subkey, root = attr->RootDirectory; - - if (!force_wow32) status = NtCreateKey( &subkey, access, attr, 0, class, options, dispos ); - - if (status == STATUS_OBJECT_NAME_NOT_FOUND) - { - WCHAR *buffer = attr->ObjectName->Buffer; - DWORD attrs, pos = 0, i = 0, len = attr->ObjectName->Length / sizeof(WCHAR); - UNICODE_STRING str; - - /* don't try to create registry root */ - if (!attr->RootDirectory && len > 10 && !wcsnicmp( buffer, L"\Registry\", 10 )) i += 10; - - while (i < len && buffer[i] != '\') i++; - if (i == len && !force_wow32) return status; - - attrs = attr->Attributes; - attr->ObjectName = &str; - - for (;;) - { - str.Buffer = buffer + pos; - str.Length = (i - pos) * sizeof(WCHAR); - if (force_wow32 && pos) - { - if (is_wow6432node( &str )) force_wow32 = FALSE; - else if ((subkey = open_wow6432node( attr->RootDirectory ))) - { - if (attr->RootDirectory != root) NtClose( attr->RootDirectory ); - attr->RootDirectory = subkey; - force_wow32 = FALSE; - } - } - if (i == len) - { - attr->Attributes = attrs; - status = NtCreateKey( &subkey, access, attr, 0, class, options, dispos ); - } - else - { - attr->Attributes = attrs & ~OBJ_OPENLINK; - status = NtCreateKey( &subkey, access, attr, 0, class, - options & ~REG_OPTION_CREATE_LINK, dispos ); - } - if (attr->RootDirectory != root) NtClose( attr->RootDirectory ); - if (!NT_SUCCESS(status)) return status; - if (i == len) break; - attr->RootDirectory = subkey; - while (i < len && buffer[i] == '\') i++; - pos = i; - while (i < len && buffer[i] != '\') i++; - } - } - attr->RootDirectory = subkey; - if (force_wow32 && (subkey = open_wow6432node( attr->RootDirectory ))) - { - if (attr->RootDirectory != root) NtClose( attr->RootDirectory ); - attr->RootDirectory = subkey; - } - if (status == STATUS_PREDEFINED_HANDLE) - { - attr->RootDirectory = get_perflib_key( attr->RootDirectory ); - status = STATUS_SUCCESS; - } - *retkey = attr->RootDirectory; - return status; -} - static NTSTATUS open_subkey( HKEY *subkey, HKEY root, UNICODE_STRING *name, DWORD options, ACCESS_MASK access ) { + BOOL is_wow64_key = (access & KEY_WOW64_32KEY) || (is_wow64 && !(access & KEY_WOW64_64KEY)); + ACCESS_MASK access_64 = (access & ~KEY_WOW64_32KEY) | KEY_WOW64_64KEY; DWORD i = 0, len = name->Length / sizeof(WCHAR); WCHAR *buffer = name->Buffer; OBJECT_ATTRIBUTES attr; @@ -239,14 +167,14 @@ static NTSTATUS open_subkey( HKEY *subkey, HKEY root, UNICODE_STRING *name, DWOR if (i == len) { if (options & REG_OPTION_OPEN_LINK) attr.Attributes |= OBJ_OPENLINK; - status = NtOpenKeyEx( (HANDLE *)subkey, access, &attr, options ); } else { if (!(options & REG_OPTION_OPEN_LINK)) attr.Attributes &= ~OBJ_OPENLINK; - status = NtOpenKeyEx( (HANDLE *)subkey, access, &attr, options & ~REG_OPTION_OPEN_LINK ); + options &= ~REG_OPTION_OPEN_LINK; }
+ status = NtOpenKeyEx( (HANDLE *)subkey, access_64, &attr, options ); if (status == STATUS_PREDEFINED_HANDLE) { *subkey = get_perflib_key( *subkey ); @@ -260,7 +188,7 @@ static NTSTATUS open_subkey( HKEY *subkey, HKEY root, UNICODE_STRING *name, DWOR name->Buffer += i; name->Length -= i * sizeof(WCHAR);
- if (!is_wow6432node( name )) + if (is_wow64_key && !is_wow6432node( name )) { HKEY wow6432node = open_wow6432node( *subkey ); if (wow6432node) @@ -274,7 +202,6 @@ static NTSTATUS open_subkey( HKEY *subkey, HKEY root, UNICODE_STRING *name, DWOR return status; }
- /* wrapper for NtOpenKeyEx to handle Wow6432 nodes */ static NTSTATUS open_key( HKEY *retkey, HKEY root, DWORD options, ACCESS_MASK access, OBJECT_ATTRIBUTES *attr ) { @@ -312,6 +239,102 @@ static NTSTATUS open_key( HKEY *retkey, HKEY root, DWORD options, ACCESS_MASK ac return status; }
+static NTSTATUS create_subkey( HKEY *subkey, HKEY root, UNICODE_STRING *name, DWORD options, ACCESS_MASK access, + const UNICODE_STRING *class, PULONG dispos ) +{ + DWORD i = 0, len = name->Length / sizeof(WCHAR); + WCHAR *buffer = name->Buffer; + OBJECT_ATTRIBUTES attr; + UNICODE_STRING str; + NTSTATUS status; + + attr.Length = sizeof(attr); + attr.RootDirectory = (HANDLE)root; + attr.ObjectName = &str; + attr.Attributes = 0; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + + if (i < len && buffer[i] == '\') return STATUS_OBJECT_PATH_INVALID; + while (i < len && buffer[i] != '\') i++; + + str.Buffer = name->Buffer; + str.Length = i * sizeof(WCHAR); + + if (i == len) + { + if (options & REG_OPTION_OPEN_LINK) attr.Attributes |= OBJ_OPENLINK; + } + else + { + options &= ~REG_OPTION_CREATE_LINK; + } + + status = NtCreateKey( (HANDLE *)subkey, access, &attr, 0, class, options, dispos ); + if (status == STATUS_PREDEFINED_HANDLE) + { + *subkey = get_perflib_key( *subkey ); + status = STATUS_SUCCESS; + } + + if (!status) + { + while (i < len && buffer[i] == '\') i++; + + name->Buffer += i; + name->Length -= i * sizeof(WCHAR); + } + + return status; +} + +/* wrapper for NtCreateKey that creates the key recursively if necessary */ +static NTSTATUS create_key( HKEY *retkey, ACCESS_MASK access, OBJECT_ATTRIBUTES *attr, + const UNICODE_STRING *class, ULONG options, PULONG dispos ) +{ + HKEY subkey, root = attr->RootDirectory, subkey_root = root; + NTSTATUS status = STATUS_OBJECT_NAME_NOT_FOUND; + UNICODE_STRING name; + + *retkey = NULL; + + name.Buffer = attr->ObjectName->Buffer; + name.Length = attr->ObjectName->Length; + + while (name.Length) + { + status = open_subkey( &subkey, subkey_root, &name, options & REG_OPTION_OPEN_LINK, access ); + if (status) break; + if (subkey_root && subkey_root != root) NtClose( subkey_root ); + subkey_root = subkey; + } + + if (!status && (options & REG_OPTION_CREATE_LINK)) + { + NtClose( subkey_root ); + status = STATUS_OBJECT_NAME_COLLISION; + } + + if (!status) + if (dispos) *dispos = REG_OPENED_EXISTING_KEY; + + if (status == STATUS_OBJECT_NAME_NOT_FOUND) + { + status = STATUS_SUCCESS; + while (!status && name.Length) + { + status = create_subkey( &subkey, subkey_root, &name, options, access, class, dispos ); + if (subkey_root && subkey_root != root) NtClose( subkey_root ); + subkey_root = subkey; + } + } + + if (!status) + *retkey = subkey_root; + + return status; +} + /* create one of the HKEY_* special root keys */ static HKEY create_special_root_hkey( HKEY hkey, DWORD access ) {
From: Sven Baars sbaars@codeweavers.com
--- dlls/kernelbase/registry.c | 46 ++++++++++++++++++++++------------ dlls/ntdll/tests/reg.c | 39 ++++++++++++++++++++++++++++ dlls/ntdll/unix/registry.c | 18 ++++++++++++- include/wine/server_protocol.h | 4 ++- server/protocol.def | 1 + server/registry.c | 23 +++++++++++++++++ server/request.h | 9 ++++--- server/trace.c | 1 + 8 files changed, 119 insertions(+), 22 deletions(-)
diff --git a/dlls/kernelbase/registry.c b/dlls/kernelbase/registry.c index f490c849c17..d7f69ed687c 100644 --- a/dlls/kernelbase/registry.c +++ b/dlls/kernelbase/registry.c @@ -150,6 +150,8 @@ static NTSTATUS open_subkey( HKEY *subkey, HKEY root, UNICODE_STRING *name, DWOR UNICODE_STRING str; NTSTATUS status = 0;
+ *subkey = 0; + attr.Length = sizeof(attr); attr.RootDirectory = (HANDLE)root; attr.ObjectName = &str; @@ -202,12 +204,30 @@ static NTSTATUS open_subkey( HKEY *subkey, HKEY root, UNICODE_STRING *name, DWOR return status; }
+/* 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 ) +{ + HKEY subkey = 0, subkey_root = root; + NTSTATUS status = 0; + + while (!status && (name->Length || !subkey)) + { + status = open_subkey( &subkey, subkey_root, name, options, access ); + if (subkey && subkey_root && subkey_root != root) NtClose( subkey_root ); + if (subkey) subkey_root = subkey; + } + + *retkey = subkey_root; + + return status; +} + /* wrapper for NtOpenKeyEx to handle Wow6432 nodes */ static NTSTATUS open_key( HKEY *retkey, HKEY root, DWORD options, ACCESS_MASK access, OBJECT_ATTRIBUTES *attr ) { - HKEY subkey, subkey_root = root; UNICODE_STRING name; NTSTATUS status = 0; + HKEY subkey;
*retkey = NULL;
@@ -226,15 +246,11 @@ static NTSTATUS open_key( HKEY *retkey, HKEY root, DWORD options, ACCESS_MASK ac name.Buffer = attr->ObjectName->Buffer; name.Length = attr->ObjectName->Length;
- while (!status && name.Length) - { - status = open_subkey( &subkey, subkey_root, &name, options, access ); - if (subkey_root && subkey_root != root) NtClose( subkey_root ); - subkey_root = subkey; - } - + status = unsafe_open_key( &subkey, root, &name, options, access ); if (!status) - *retkey = subkey_root; + *retkey = subkey; + else if (subkey && subkey != root) + NtClose( subkey );
return status; } @@ -293,21 +309,19 @@ static NTSTATUS create_key( HKEY *retkey, ACCESS_MASK access, OBJECT_ATTRIBUTES const UNICODE_STRING *class, ULONG options, PULONG dispos ) { HKEY subkey, root = attr->RootDirectory, subkey_root = root; - NTSTATUS status = STATUS_OBJECT_NAME_NOT_FOUND; UNICODE_STRING name; + NTSTATUS status;
*retkey = NULL;
name.Buffer = attr->ObjectName->Buffer; name.Length = attr->ObjectName->Length;
- while (name.Length) - { - status = open_subkey( &subkey, subkey_root, &name, options & REG_OPTION_OPEN_LINK, access ); - if (status) break; - if (subkey_root && subkey_root != root) NtClose( subkey_root ); + status = unsafe_open_key( &subkey, subkey_root, &name, options & REG_OPTION_OPEN_LINK, access ); + if (!status || status == STATUS_OBJECT_NAME_NOT_FOUND) subkey_root = subkey; - } + else if (subkey && subkey != root) + NtClose( subkey );
if (!status && (options & REG_OPTION_CREATE_LINK)) { diff --git a/dlls/ntdll/tests/reg.c b/dlls/ntdll/tests/reg.c index 978d50dbd0d..ca5613f02e5 100644 --- a/dlls/ntdll/tests/reg.c +++ b/dlls/ntdll/tests/reg.c @@ -115,6 +115,12 @@ typedef enum _KEY_VALUE_INFORMATION_CLASS {
#endif
+typedef struct _KEY_FLAGS_INFORMATION{ + ULONG Unknown1; + ULONG Unknown2; + ULONG WowShare; +} KEY_FLAGS_INFORMATION, *PKEY_FLAGS_INFORMATION; + static BOOLEAN (WINAPI * pRtlCreateUnicodeStringFromAsciiz)(PUNICODE_STRING, LPCSTR); static void (WINAPI * pRtlInitUnicodeString)(PUNICODE_STRING,PCWSTR); static NTSTATUS (WINAPI * pRtlFreeUnicodeString)(PUNICODE_STRING); @@ -2135,6 +2141,7 @@ static void test_NtQueryKey(void) OBJECT_ATTRIBUTES attr; ULONG length, len; KEY_NAME_INFORMATION *info = NULL; + KEY_FLAGS_INFORMATION flags_info; KEY_CACHED_INFORMATION cached_info; UNICODE_STRING str; DWORD dw; @@ -2242,6 +2249,38 @@ static void test_NtQueryKey(void)
pNtClose(subkey2); pNtClose(subkey); + + status = pNtQueryKey(key, KeyFlagsInformation, &flags_info, sizeof(flags_info), &len); + ok(status == STATUS_SUCCESS, "NtQueryKey Failed: 0x%08lx\n", status); + ok(len == sizeof(flags_info), "got unexpected length %ld\n", len); + ok(flags_info.WowShare == 0, "flags_info.WowShare = %lu\n", flags_info.WowShare); + + pNtClose(key); + + pRtlCreateUnicodeStringFromAsciiz(&str, "\REGISTRY\Machine\Software\Classes"); + attr.RootDirectory = 0; + status = pNtOpenKey(&key, KEY_READ, &attr); + ok(status == STATUS_SUCCESS, "NtOpenKey Failed: 0x%08lx\n", status); + pRtlFreeUnicodeString(&str); + + status = pNtQueryKey(key, KeyFlagsInformation, &flags_info, sizeof(flags_info), &len); + ok(status == STATUS_SUCCESS, "NtQueryKey Failed: 0x%08lx\n", status); + ok(len == sizeof(flags_info), "got unexpected length %ld\n", len); + ok(flags_info.WowShare == 10, "flags_info.WowShare = %lu\n", flags_info.WowShare); + + pNtClose(key); + + pRtlCreateUnicodeStringFromAsciiz(&str, "\REGISTRY\Machine\Software\Classes\Interface"); + attr.RootDirectory = 0; + status = pNtOpenKey(&key, KEY_READ, &attr); + ok(status == STATUS_SUCCESS, "NtOpenKey Failed: 0x%08lx\n", status); + pRtlFreeUnicodeString(&str); + + status = pNtQueryKey(key, KeyFlagsInformation, &flags_info, sizeof(flags_info), &len); + ok(status == STATUS_SUCCESS, "NtQueryKey Failed: 0x%08lx\n", status); + ok(len == sizeof(flags_info), "got unexpected length %ld\n", len); + ok(flags_info.WowShare == 10, "flags_info.WowShare = %lu\n", flags_info.WowShare); + pNtClose(key); }
diff --git a/dlls/ntdll/unix/registry.c b/dlls/ntdll/unix/registry.c index d183474b53f..272782ef7a7 100644 --- a/dlls/ntdll/unix/registry.c +++ b/dlls/ntdll/unix/registry.c @@ -232,7 +232,6 @@ NTSTATUS WINAPI NtRenameKey( HANDLE key, UNICODE_STRING *name ) return ret; }
- /****************************************************************************** * enumerate_key * @@ -242,6 +241,12 @@ static NTSTATUS enumerate_key( HANDLE handle, int index, KEY_INFORMATION_CLASS i void *info, DWORD length, DWORD *result_len )
{ + typedef struct { + ULONG Unknown1; + ULONG Unknown2; + ULONG WowShare; + } KEY_FLAGS_INFORMATION; + unsigned int ret; void *data_ptr; size_t fixed_size; @@ -253,6 +258,7 @@ static NTSTATUS enumerate_key( HANDLE handle, int index, KEY_INFORMATION_CLASS i case KeyNodeInformation: data_ptr = ((KEY_NODE_INFORMATION *)info)->Name; break; case KeyNameInformation: data_ptr = ((KEY_NAME_INFORMATION *)info)->Name; break; case KeyCachedInformation: data_ptr = ((KEY_CACHED_INFORMATION *)info)+1; break; + case KeyFlagsInformation: data_ptr = ((KEY_FLAGS_INFORMATION *)info)+1; break; default: FIXME( "Information class %d not implemented\n", info_class ); return STATUS_INVALID_PARAMETER; @@ -339,6 +345,16 @@ static NTSTATUS enumerate_key( HANDLE handle, int index, KEY_INFORMATION_CLASS i break; }
+ case KeyFlagsInformation: + { + KEY_FLAGS_INFORMATION keyinfo; + keyinfo.Unknown1 = 0; + keyinfo.Unknown2 = 0; + keyinfo.WowShare = reply->wowshare; + memcpy( info, &keyinfo, min( length, fixed_size ) ); + break; + } + default: break; } diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index 472c0ea709d..fbdaf965a06 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -2301,6 +2301,8 @@ struct enum_key_reply int values; int max_value; int max_data; + int wowshare; + char __pad_36[4]; timeout_t modif; data_size_t total; data_size_t namelen; @@ -6356,7 +6358,7 @@ union generic_reply
/* ### protocol_version begin ### */
-#define SERVER_PROTOCOL_VERSION 758 +#define SERVER_PROTOCOL_VERSION 759
/* ### protocol_version end ### */
diff --git a/server/protocol.def b/server/protocol.def index 8c2fbeb4afe..0dba49f75d3 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1791,6 +1791,7 @@ struct process_info int values; /* number of values */ int max_value; /* longest value name */ int max_data; /* longest value data */ + int wowshare; /* wowshare flags */ timeout_t modif; /* last modification time */ data_size_t total; /* total length needed for full name and class */ data_size_t namelen; /* length of key name in bytes */ diff --git a/server/registry.c b/server/registry.c index 03e5f20cae5..895a962d3a7 100644 --- a/server/registry.c +++ b/server/registry.c @@ -900,6 +900,17 @@ static struct key *create_key_recursive( struct key *root, const struct unicode_ return parent; }
+static int has_wowshare_parent( struct key *key ) +{ + if (!key) + return 0; + + if (key->wow6432node) + return (key->wow6432node->flags & KEY_WOWSHARE) ? 1 : 0; + + return has_wowshare_parent( get_parent( key ) ); +} + /* query information about a key or a subkey */ static void enum_key( struct key *key, int index, int info_class, struct enum_key_reply *reply ) { @@ -942,6 +953,7 @@ static void enum_key( struct key *key, int index, int info_class, struct enum_ke reply->max_class = 0; reply->max_value = 0; reply->max_data = 0; + reply->wowshare = 0; break; case KeyFullInformation: case KeyCachedInformation: @@ -959,11 +971,22 @@ static void enum_key( struct key *key, int index, int info_class, struct enum_ke reply->max_class = max_class; reply->max_value = max_value; reply->max_data = max_data; + reply->wowshare = 0; reply->namelen = namelen; if (info_class == KeyCachedInformation) classlen = 0; /* don't return any data, only its size */ namelen = 0; /* don't return name */ break; + case KeyFlagsInformation: + namelen = 0; + classlen = 0; + reply->max_subkey = 0; + reply->max_class = 0; + reply->max_value = 0; + reply->max_data = 0; + reply->wowshare = has_wowshare_parent(key) ? 10 : 0; + reply->namelen = 0; + break; default: set_error( STATUS_INVALID_PARAMETER ); return; diff --git a/server/request.h b/server/request.h index 089af79e199..befd1cf93f1 100644 --- a/server/request.h +++ b/server/request.h @@ -1209,10 +1209,11 @@ C_ASSERT( FIELD_OFFSET(struct enum_key_reply, max_class) == 16 ); C_ASSERT( FIELD_OFFSET(struct enum_key_reply, values) == 20 ); C_ASSERT( FIELD_OFFSET(struct enum_key_reply, max_value) == 24 ); C_ASSERT( FIELD_OFFSET(struct enum_key_reply, max_data) == 28 ); -C_ASSERT( FIELD_OFFSET(struct enum_key_reply, modif) == 32 ); -C_ASSERT( FIELD_OFFSET(struct enum_key_reply, total) == 40 ); -C_ASSERT( FIELD_OFFSET(struct enum_key_reply, namelen) == 44 ); -C_ASSERT( sizeof(struct enum_key_reply) == 48 ); +C_ASSERT( FIELD_OFFSET(struct enum_key_reply, wowshare) == 32 ); +C_ASSERT( FIELD_OFFSET(struct enum_key_reply, modif) == 40 ); +C_ASSERT( FIELD_OFFSET(struct enum_key_reply, total) == 48 ); +C_ASSERT( FIELD_OFFSET(struct enum_key_reply, namelen) == 52 ); +C_ASSERT( sizeof(struct enum_key_reply) == 56 ); C_ASSERT( FIELD_OFFSET(struct set_key_value_request, hkey) == 12 ); C_ASSERT( FIELD_OFFSET(struct set_key_value_request, type) == 16 ); C_ASSERT( FIELD_OFFSET(struct set_key_value_request, namelen) == 20 ); diff --git a/server/trace.c b/server/trace.c index a0076d5449b..301f1a0ff35 100644 --- a/server/trace.c +++ b/server/trace.c @@ -2360,6 +2360,7 @@ static void dump_enum_key_reply( const struct enum_key_reply *req ) fprintf( stderr, ", values=%d", req->values ); fprintf( stderr, ", max_value=%d", req->max_value ); fprintf( stderr, ", max_data=%d", req->max_data ); + fprintf( stderr, ", wowshare=%d", req->wowshare ); dump_timeout( ", modif=", &req->modif ); fprintf( stderr, ", total=%u", req->total ); fprintf( stderr, ", namelen=%u", req->namelen );
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;
From: Sven Baars sbaars@codeweavers.com
--- dlls/advapi32/tests/registry.c | 17 ++++-------- dlls/kernelbase/registry.c | 49 +++++++++++++++++++++++++++++++++- 2 files changed, 53 insertions(+), 13 deletions(-)
diff --git a/dlls/advapi32/tests/registry.c b/dlls/advapi32/tests/registry.c index ecc71ec8db8..fe84c0ef417 100644 --- a/dlls/advapi32/tests/registry.c +++ b/dlls/advapi32/tests/registry.c @@ -2615,12 +2615,9 @@ static void test_redirection(void) err = RegCreateKeyExA( HKEY_LOCAL_MACHINE, "Software", 0, NULL, 0, KEY_ALL_ACCESS, NULL, &key, NULL ); ok( err == ERROR_SUCCESS, "RegCreateKeyExA failed: %lu\n", err ); - check_key_value( key, "Wine\Winetest", 0, ptr_size ); check_key_value( key, "Wine\Winetest", KEY_WOW64_64KEY, ptr_size ); - dw = get_key_value( key, "Wine\Winetest", KEY_WOW64_32KEY ); - todo_wine_if (ptr_size == 64) ok( dw == 32, "wrong value %lu\n", dw ); - + check_key_value( key, "Wine\Winetest", KEY_WOW64_32KEY, 32 ); check_key_value( key, "Wow6432Node\Wine\Winetest", 0, ptr_size == 32 ? 0 : 32 ); check_key_value( key, "Wow6432Node\Wine\Winetest", KEY_WOW64_64KEY, ptr_size == 32 ? 0 : 32 ); check_key_value( key, "Wow6432Node\Wine\Winetest", KEY_WOW64_32KEY, ptr_size == 32 ? 0 : 32 ); @@ -2632,8 +2629,7 @@ static void test_redirection(void) dw = get_key_value( key, "Wine\Winetest", 0 ); ok( dw == 64 || broken(dw == 32) /* win7 */, "wrong value %lu\n", dw ); check_key_value( key, "Wine\Winetest", KEY_WOW64_64KEY, 64 ); - dw = get_key_value( key, "Wine\Winetest", KEY_WOW64_32KEY ); - todo_wine_if (ptr_size == 64) ok( dw == 32, "wrong value %lu\n", dw ); + check_key_value( key, "Wine\Winetest", KEY_WOW64_32KEY, 32 ); check_key_value( key, "Wow6432Node\Wine\Winetest", 0, 32 ); check_key_value( key, "Wow6432Node\Wine\Winetest", KEY_WOW64_64KEY, 32 ); check_key_value( key, "Wow6432Node\Wine\Winetest", KEY_WOW64_32KEY, 32 ); @@ -2710,9 +2706,7 @@ static void test_redirection(void) ok( err == ERROR_SUCCESS, "RegCreateKeyExA failed: %lu\n", err ); check_key_value( key, "Winetest", 0, ptr_size ); check_key_value( key, "Winetest", KEY_WOW64_64KEY, ptr_size ); - dw = get_key_value( key, "Winetest", KEY_WOW64_32KEY ); - todo_wine_if (ptr_size != 32) - ok( dw == 32, "wrong value %lu\n", dw ); + check_key_value( key, "Winetest", KEY_WOW64_32KEY, 32 ); RegCloseKey( key );
err = RegCreateKeyExA( HKEY_LOCAL_MACHINE, "Software\Wine", 0, NULL, 0, @@ -2720,8 +2714,7 @@ static void test_redirection(void) ok( err == ERROR_SUCCESS, "RegCreateKeyExA failed: %lu\n", err ); check_key_value( key, "Winetest", 0, 64 ); check_key_value( key, "Winetest", KEY_WOW64_64KEY, 64 ); - dw = get_key_value( key, "Winetest", KEY_WOW64_32KEY ); - todo_wine_if (ptr_size == 64) ok( dw == 32, "wrong value %lu\n", dw ); + check_key_value( key, "Winetest", KEY_WOW64_32KEY, 32 ); RegCloseKey( key );
err = RegCreateKeyExA( HKEY_LOCAL_MACHINE, "Software\Wine", 0, NULL, 0, @@ -2778,7 +2771,7 @@ static void test_redirection(void)
/* verify subkey is not present in native mode */ err = RegOpenKeyExA(native, "AWineTest", 0, KEY_ALL_ACCESS, &key); - todo_wine_if(ptr_size == 64) ok(err == ERROR_FILE_NOT_FOUND, "got %li\n", err); + 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 bd2d665c705..ce20fcc926a 100644 --- a/dlls/kernelbase/registry.c +++ b/dlls/kernelbase/registry.c @@ -282,6 +282,51 @@ static NTSTATUS open_subkey( HKEY *subkey, HKEY root, UNICODE_STRING *name, DWOR return status; }
+static NTSTATUS open_wow6432node_parent( HKEY *retkey, HKEY root, DWORD options, ACCESS_MASK access ) +{ + char buffer[256], *buf_ptr = buffer; + KEY_NAME_INFORMATION *info = (KEY_NAME_INFORMATION *)buffer; + DWORD len = sizeof(buffer); + UNICODE_STRING name; + NTSTATUS status; + + /* Obtain the name of the root key */ + status = NtQueryKey( root, KeyNameInformation, info, len, &len ); + if (status && status != STATUS_BUFFER_OVERFLOW) return status; + + /* 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 STATUS_NO_MEMORY; + info = (KEY_NAME_INFORMATION *)buf_ptr; + status = NtQueryKey( root, KeyNameInformation, info, len, &len ); + } + + if (status) + { + if (buf_ptr != buffer) heap_free( buf_ptr ); + return status; + } + + name.Buffer = info->Name; + name.Length = info->NameLength; + root = 0; + + /* Obtain the parent Wow6432Node if it exists */ + while (!status && name.Length) + { + status = open_subkey( retkey, root, &name, options & ~REG_OPTION_OPEN_LINK, access ); + if (root) NtClose( root ); + root = *retkey; + } + + if (buf_ptr != buffer) heap_free( buf_ptr ); + + return status; +} + /* 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 ) { @@ -290,7 +335,9 @@ static NTSTATUS unsafe_open_key( HKEY *retkey, HKEY root, UNICODE_STRING *name, BOOL was_wow6432node = TRUE; NTSTATUS status = 0;
- if (root && is_wow64 && !(access & KEY_WOW64_64KEY) && !is_wow6432node( name )) + if (root && (access & KEY_WOW64_32KEY) && !is_wow6432node( name )) + status = open_wow6432node_parent( &subkey_root, root, options, access ); + else 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)
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=126785
Your paranoid android.
=== debian11 (32 bit report) ===
ntdll: reg.c:2269: Test failed: flags_info.WowShare = 0 reg.c:2282: Test failed: flags_info.WowShare = 0
=== debian11 (32 bit ar:MA report) ===
ntdll: reg.c:2269: Test failed: flags_info.WowShare = 0 reg.c:2282: Test failed: flags_info.WowShare = 0
=== debian11 (32 bit de report) ===
ntdll: reg.c:2269: Test failed: flags_info.WowShare = 0 reg.c:2282: Test failed: flags_info.WowShare = 0
=== debian11 (32 bit fr report) ===
ntdll: reg.c:2269: Test failed: flags_info.WowShare = 0 reg.c:2282: Test failed: flags_info.WowShare = 0
=== debian11 (32 bit he:IL report) ===
ntdll: reg.c:2269: Test failed: flags_info.WowShare = 0 reg.c:2282: Test failed: flags_info.WowShare = 0
=== debian11 (32 bit hi:IN report) ===
ntdll: reg.c:2269: Test failed: flags_info.WowShare = 0 reg.c:2282: Test failed: flags_info.WowShare = 0
=== debian11 (32 bit ja:JP report) ===
ntdll: reg.c:2269: Test failed: flags_info.WowShare = 0 reg.c:2282: Test failed: flags_info.WowShare = 0
=== debian11 (32 bit zh:CN report) ===
ntdll: reg.c:2269: Test failed: flags_info.WowShare = 0 reg.c:2282: Test failed: flags_info.WowShare = 0
I pushed a new version that doesn't use KeyFlagsInformation but instead just checks for HKCR because KeyFlagsInformation apparently didn't do what I was expecting. Maybe the flag is for marking sharing between the machine and user instead.