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.
-- v2: kernelbase: Recursively obtain the Wow6432Node parent. kernelbase: Add support for shared registry keys. ntdll: Add a KeyFlagsInformation query. kernelbase: Factor out the common parts of open_key() and create_key(). kernelbase: Reimplement create_key() using open_subkey(). kernelbase: Factor out opening a subkey.
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 +++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 16 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)) {
From: Sven Baars sbaars@codeweavers.com
--- dlls/ntdll/tests/reg.c | 52 ++++++++++++++++++++++++++++++++++ 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 + 7 files changed, 102 insertions(+), 6 deletions(-)
diff --git a/dlls/ntdll/tests/reg.c b/dlls/ntdll/tests/reg.c index 978d50dbd0d..49f53e3f04b 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,51 @@ 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); + + pRtlCreateUnicodeStringFromAsciiz(&str, "\REGISTRY\Machine\Software\Classes\Wow6432Node"); + 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 | 97 +++++++++++++++++++++++++++++++++- 2 files changed, 96 insertions(+), 3 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..e6865e75e8a 100644 --- a/dlls/kernelbase/registry.c +++ b/dlls/kernelbase/registry.c @@ -117,7 +117,7 @@ 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; }
@@ -140,6 +140,67 @@ static HKEY get_perflib_key( HANDLE key ) return key; }
+static BOOL is_shared( HKEY key ) +{ + NTSTATUS status; + DWORD len; + struct { + ULONG Unknown1; + ULONG Unknown2; + ULONG WowShare; + } flags_info; + + status = NtQueryKey( key, KeyFlagsInformation, &flags_info, sizeof(flags_info), &len ); + return (!status && flags_info.WowShare); +} + +static NTSTATUS open_subkey( HKEY *subkey, HKEY root, UNICODE_STRING *name, DWORD options, ACCESS_MASK access ); + +static NTSTATUS open_shared_parent( HKEY *subkey, HKEY root, 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; + KEY_NAME_INFORMATION *info = NULL; + UNICODE_STRING str; + NTSTATUS status; + DWORD len; + + if (is_wow64_key && is_shared( root )) + { + /* Obtain the name of the root key */ + status = NtQueryKey( root, KeyNameInformation, info, 0, &len ); + if (status != STATUS_BUFFER_TOO_SMALL) return status; + + if (!(info = heap_alloc( len ))) + return STATUS_NO_MEMORY; + + /* Open the shared parent only if the key ends in a Wow6432Node */ + status = NtQueryKey( root, KeyNameInformation, info, len, &len ); + if (!status && info->NameLength / sizeof(WCHAR) >= 11) + { + str.Buffer = info->Name + info->NameLength / sizeof(WCHAR) - 11; + str.Length = 11 * sizeof(WCHAR); + if (is_wow6432node( &str )) + { + root = 0; + str.Buffer = info->Name; + str.Length = info->NameLength - 11 * sizeof(WCHAR); + while (!status && str.Length) + { + status = open_subkey( subkey, root, &str, options & ~REG_OPTION_OPEN_LINK, access_64 ); + if (root) NtClose( root ); + root = *subkey; + } + heap_free( info ); + return status; + } + } + heap_free( info ); + } + + return STATUS_OBJECT_NAME_NOT_FOUND; +} + 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)); @@ -177,6 +238,23 @@ 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) + { + HKEY shared_root; + + /* Try to open the shared parent if we can't find the key in the Wow6432Node */ + if (open_shared_parent( &shared_root, root, options, access )) + return STATUS_OBJECT_NAME_NOT_FOUND; + + attr.RootDirectory = (HANDLE)shared_root; + status = NtOpenKeyEx( (HANDLE *)subkey, access_64, &attr, options ); + + if (!status) + NtClose( shared_root ); + else + *subkey = shared_root; + } + 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; @@ -208,15 +286,30 @@ static NTSTATUS open_subkey( HKEY *subkey, HKEY root, UNICODE_STRING *name, DWOR static NTSTATUS unsafe_open_key( HKEY *retkey, HKEY root, UNICODE_STRING *name, DWORD options, ACCESS_MASK access ) { HKEY subkey = 0, subkey_root = root; + BOOL was_wow6432node = TRUE; NTSTATUS status = 0;
+ if (root && is_wow64 && !(access & KEY_WOW64_64KEY) && !is_wow6432node( name ) && is_shared( root )) + subkey_root = open_wow6432node( 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) + { + if (!open_shared_parent( &subkey, subkey_root, options, access )) + { + if (subkey_root && subkey_root != root) NtClose( subkey_root ); + subkey_root = subkey; + } + } + *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 e6865e75e8a..4151fe05bb7 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; + UNICODE_STRING name; + NTSTATUS status; + DWORD len; + + /* Obtain the name of the root key */ + status = NtQueryKey( root, KeyNameInformation, buffer, sizeof(buffer), &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, buf_ptr, 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 ) { @@ -289,7 +334,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 ) && is_shared( root )) + 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 ) && is_shared( root )) subkey_root = open_wow6432node( root );
while (!status && (name->Length || !subkey))
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 full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=126619
Your paranoid android.
=== w7u_2qxl (32 bit report) ===
ntdll: reg.c:2289: Test failed: NtOpenKey Failed: 0xc0000034 reg.c:2293: Test failed: NtQueryKey Failed: 0xc0000008
=== w7u_adm (32 bit report) ===
ntdll: reg.c:2289: Test failed: NtOpenKey Failed: 0xc0000034 reg.c:2293: Test failed: NtQueryKey Failed: 0xc0000008
=== w7u_el (32 bit report) ===
ntdll: reg.c:2289: Test failed: NtOpenKey Failed: 0xc0000034 reg.c:2293: Test failed: NtQueryKey Failed: 0xc0000008
=== debian11 (32 bit report) ===
ntdll: reg.c:2269: Test failed: flags_info.WowShare = 0 reg.c:2282: Test failed: flags_info.WowShare = 0 reg.c:2289: Test failed: NtOpenKey Failed: 0xc0000034 reg.c:2293: Test failed: NtQueryKey Failed: 0xc0000008 reg.c:2295: 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 reg.c:2289: Test failed: NtOpenKey Failed: 0xc0000034 reg.c:2293: Test failed: NtQueryKey Failed: 0xc0000008 reg.c:2295: 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 reg.c:2289: Test failed: NtOpenKey Failed: 0xc0000034 reg.c:2293: Test failed: NtQueryKey Failed: 0xc0000008 reg.c:2295: 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 reg.c:2289: Test failed: NtOpenKey Failed: 0xc0000034 reg.c:2293: Test failed: NtQueryKey Failed: 0xc0000008 reg.c:2295: 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 reg.c:2289: Test failed: NtOpenKey Failed: 0xc0000034 reg.c:2293: Test failed: NtQueryKey Failed: 0xc0000008 reg.c:2295: 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 reg.c:2289: Test failed: NtOpenKey Failed: 0xc0000034 reg.c:2293: Test failed: NtQueryKey Failed: 0xc0000008 reg.c:2295: 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 reg.c:2289: Test failed: NtOpenKey Failed: 0xc0000034 reg.c:2293: Test failed: NtQueryKey Failed: 0xc0000008 reg.c:2295: 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 reg.c:2289: Test failed: NtOpenKey Failed: 0xc0000034 reg.c:2293: Test failed: NtQueryKey Failed: 0xc0000008 reg.c:2295: Test failed: flags_info.WowShare = 0