From: Paul Gofman pgofman@codeweavers.com
--- server/registry.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/server/registry.c b/server/registry.c index 804cfcc638b..aab18fc6061 100644 --- a/server/registry.c +++ b/server/registry.c @@ -97,7 +97,7 @@ struct key #define KEY_DELETED 0x0002 /* key has been deleted */ #define KEY_DIRTY 0x0004 /* key has been modified */ #define KEY_SYMLINK 0x0008 /* key is a symbolic link */ -#define KEY_WOWSHARE 0x0010 /* key is a Wow64 shared key (used for Software\Classes) */ +#define KEY_WOWREFLECT 0x0010 /* key is a Wow64 shared and reflected key (used for Software\Classes) */ #define KEY_PREDEF 0x0020 /* key is marked as predefined */
#define OBJ_KEY_WOW64 0x100000 /* magic flag added to attributes for WoW64 redirection */ @@ -518,7 +518,7 @@ static struct object *key_lookup_name( struct object *obj, struct unicode_str *n }
key = (struct key *)obj; - if (key && (key->flags & KEY_WOWSHARE) && (attr & OBJ_KEY_WOW64) && !name->str) + if (key && (key->flags & KEY_WOWREFLECT) && (attr & OBJ_KEY_WOW64) && !name->str) { key = get_parent( key ); release_object( obj ); @@ -545,7 +545,7 @@ static struct object *key_lookup_name( struct object *obj, struct unicode_str *n
if (!(found = find_subkey( key, &tmp, &index ))) { - if ((key->flags & KEY_WOWSHARE) && (attr & OBJ_KEY_WOW64)) + if ((key->flags & KEY_WOWREFLECT) && (attr & OBJ_KEY_WOW64)) { /* try in the 64-bit parent */ key = get_parent( key ); @@ -777,7 +777,7 @@ static struct key *grab_wow6432node( struct key *key ) struct key *ret = key->wow6432node;
if (!ret) return key; - if (ret->flags & KEY_WOWSHARE) return key; + if (ret->flags & KEY_WOWREFLECT) return key; grab_object( ret ); release_object( key ); return ret; @@ -823,7 +823,7 @@ static struct key *open_key( struct key *parent, const struct unicode_str *name, if (parent && !(access & KEY_WOW64_64KEY) && !is_wow6432node( name->str, name->len )) { key = get_wow6432node( parent ); - if (key && ((access & KEY_WOW64_32KEY) || (key->flags & KEY_WOWSHARE))) + if (key && ((access & KEY_WOW64_32KEY) || (key->flags & KEY_WOWREFLECT))) parent = key; }
@@ -849,7 +849,7 @@ static struct key *create_key( struct key *parent, const struct unicode_str *nam if (parent && !(access & KEY_WOW64_64KEY) && !is_wow6432node( name->str, name->len )) { key = get_wow6432node( parent ); - if (key && ((access & KEY_WOW64_32KEY) || (key->flags & KEY_WOWSHARE))) + if (key && ((access & KEY_WOW64_32KEY) || (key->flags & KEY_WOWREFLECT))) parent = key; }
@@ -1954,7 +1954,7 @@ void init_registry(void) } if ((key = create_key_recursive( hklm, &name, current_time ))) { - key->flags |= KEY_WOWSHARE; + key->flags |= KEY_WOWREFLECT; release_object( key ); } /* FIXME: handle HKCU too */
From: Paul Gofman pgofman@codeweavers.com
--- server/registry.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-)
diff --git a/server/registry.c b/server/registry.c index aab18fc6061..507793e5ed3 100644 --- a/server/registry.c +++ b/server/registry.c @@ -99,6 +99,7 @@ struct key #define KEY_SYMLINK 0x0008 /* key is a symbolic link */ #define KEY_WOWREFLECT 0x0010 /* key is a Wow64 shared and reflected key (used for Software\Classes) */ #define KEY_PREDEF 0x0020 /* key is marked as predefined */ +#define KEY_WOWSHARE 0x0040 /* key is Wow64 shared */
#define OBJ_KEY_WOW64 0x100000 /* magic flag added to attributes for WoW64 redirection */
@@ -604,7 +605,7 @@ static int key_link_name( struct object *obj, struct object_name *name, struct o for (i = ++parent_key->last_subkey; i > index; i--) parent_key->subkeys[i] = parent_key->subkeys[i - 1]; parent_key->subkeys[index] = (struct key *)grab_object( key ); - if (is_wow6432node( name->name, name->len ) && + if (!(parent_key->flags & KEY_WOWSHARE) && is_wow6432node( name->name, name->len ) && !is_wow6432node( parent_key->obj.name->name, parent_key->obj.name->len )) parent_key->wow6432node = key; name->parent = parent; @@ -1886,13 +1887,14 @@ void init_registry(void) 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\', 'P','e','r','f','l','i','b','\', '0','0','9'}; + static const WCHAR software[] = {'S','o','f','t','w','a','r','e',}; static const struct unicode_str root_name = { REGISTRY, sizeof(REGISTRY) }; static const struct unicode_str HKLM_name = { HKLM, sizeof(HKLM) }; static const struct unicode_str HKU_name = { HKU_default, sizeof(HKU_default) }; static const struct unicode_str perflib_name = { perflib, sizeof(perflib) };
WCHAR *current_user_path; - struct unicode_str current_user_str; + struct unicode_str current_user_str, name; struct key *key, *hklm, *hkcu; unsigned int i; char *p; @@ -1944,8 +1946,6 @@ void init_registry(void) /* set the shared flag on Software\Classes\Wow6432Node for all platforms */ for (i = 1; i < supported_machines_count; i++) { - struct unicode_str name; - switch (supported_machines[i]) { case IMAGE_FILE_MACHINE_I386: name.str = classes_i386; name.len = sizeof(classes_i386); break; @@ -1957,7 +1957,15 @@ void init_registry(void) key->flags |= KEY_WOWREFLECT; release_object( key ); } - /* FIXME: handle HKCU too */ + } + + name.str = software; + name.len = sizeof(software); + if ((key = create_key_recursive( hkcu, &name, current_time ))) + { + key->flags |= KEY_WOWSHARE; + key->wow6432node = NULL; + release_object( key ); }
if ((key = create_key_recursive( hklm, &perflib_name, current_time )))
From: Paul Gofman pgofman@codeweavers.com
--- dlls/kernelbase/registry.c | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-)
diff --git a/dlls/kernelbase/registry.c b/dlls/kernelbase/registry.c index 8471376bf96..24b4bd45d70 100644 --- a/dlls/kernelbase/registry.c +++ b/dlls/kernelbase/registry.c @@ -112,20 +112,15 @@ static BOOL is_classes_root( const UNICODE_STRING *name ) return (len >= classes_root_len - 1 && !wcsnicmp( name->Buffer, classes_root, min( len, classes_root_len ) )); }
-static BOOL is_classes_wow6432node( HKEY key ) +static KEY_NAME_INFORMATION *get_key_name( HKEY key, char *buffer, DWORD len ) { - char buffer[256], *buf_ptr = buffer; + char *buf_ptr = buffer; KEY_NAME_INFORMATION *info = (KEY_NAME_INFORMATION *)buffer; - DWORD len = sizeof(buffer); - UNICODE_STRING name; NTSTATUS status; - 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; + status = NtQueryKey( key, KeyNameInformation, buf_ptr, len, &len ); + if (status && status != STATUS_BUFFER_OVERFLOW) return NULL;
- /* Retry with a dynamically allocated buffer */ while (status == STATUS_BUFFER_OVERFLOW) { if (buf_ptr != buffer) heap_free( buf_ptr ); @@ -133,9 +128,22 @@ static BOOL is_classes_wow6432node( HKEY key ) info = (KEY_NAME_INFORMATION *)buf_ptr; status = NtQueryKey( key, KeyNameInformation, info, len, &len ); } + if (!status) return (KEY_NAME_INFORMATION *)buf_ptr; + if (buf_ptr != buffer) heap_free( buf_ptr ); + return NULL; +} + +static BOOL is_classes_wow6432node( HKEY key ) +{ + KEY_NAME_INFORMATION *info; + char buffer[256]; + UNICODE_STRING name; + BOOL ret = FALSE; + + if (!(info = get_key_name( key, buffer, sizeof(buffer) ))) return FALSE;
/* Check if the key ends in Wow6432Node and if the root is the Classes key*/ - if (!status && info->NameLength / sizeof(WCHAR) >= 11) + if (info->NameLength / sizeof(WCHAR) >= 11) { name.Buffer = info->Name + info->NameLength / sizeof(WCHAR) - 11; name.Length = 11 * sizeof(WCHAR); @@ -146,8 +154,7 @@ static BOOL is_classes_wow6432node( HKEY key ) ret = is_classes_root( &name ); } } - - if (buf_ptr != buffer) heap_free( buf_ptr ); + if ((char *)info != buffer) heap_free( info );
return ret; }
From: Paul Gofman pgofman@codeweavers.com
--- dlls/kernelbase/registry.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+)
diff --git a/dlls/kernelbase/registry.c b/dlls/kernelbase/registry.c index 24b4bd45d70..c261cbad100 100644 --- a/dlls/kernelbase/registry.c +++ b/dlls/kernelbase/registry.c @@ -159,6 +159,37 @@ static BOOL is_classes_wow6432node( HKEY key ) return ret; }
+static BOOL is_wow6432_shared( HANDLE key ) +{ + static const WCHAR users_root[] = L"\Registry\User\"; + const DWORD users_root_len = ARRAY_SIZE( users_root ) - 1; + static const WCHAR software[] = L"\Software"; + const DWORD software_len = ARRAY_SIZE( software ) - 1; + KEY_NAME_INFORMATION *info; + char buffer[256]; + BOOL ret = FALSE; + WCHAR *name; + DWORD len; + + info = get_key_name( key, buffer, sizeof(buffer) ); + len = info->NameLength / sizeof(WCHAR); + if (len <= users_root_len) goto done; + name = info->Name; + if (wcsnicmp( name, users_root, users_root_len )) goto done; + name += users_root_len; + len -= users_root_len; + while (len && *name != '\') + { + ++name; + --len; + } + if (len != software_len) goto done; + ret = !wcsnicmp( name, software, software_len ); +done: + if ((char *)info != buffer) heap_free( info ); + return ret; +} + /* Open the Wow6432Node subkey of the specified key */ static HANDLE open_wow6432node( HANDLE key ) { @@ -173,6 +204,11 @@ static HANDLE open_wow6432node( HANDLE key ) attr.SecurityDescriptor = NULL; attr.SecurityQualityOfService = NULL; if (NtOpenKeyEx( &ret, MAXIMUM_ALLOWED | KEY_WOW64_64KEY, &attr, 0 )) return key; + if (is_wow6432_shared( key )) + { + NtClose( ret ); + return key; + } return ret; }
From: Paul Gofman pgofman@codeweavers.com
--- dlls/advapi32/tests/registry.c | 72 ++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+)
diff --git a/dlls/advapi32/tests/registry.c b/dlls/advapi32/tests/registry.c index 0f77b2fa066..d21bd8fe741 100644 --- a/dlls/advapi32/tests/registry.c +++ b/dlls/advapi32/tests/registry.c @@ -3375,6 +3375,78 @@ static void test_redirection(void)
RegDeleteKeyA( key32, "" ); RegCloseKey( key32 ); + + /* HKCU\Software is shared. */ + err = RegCreateKeyExA( HKEY_CURRENT_USER, "Software\Wow6432Node\tmp", 0, NULL, 0, KEY_ALL_ACCESS | KEY_WOW64_32KEY, NULL, &key, NULL ); + ok( !err, "got %#lx.\n", err ); + RegDeleteKeyA( key, "" ); + RegCloseKey( key ); + + err = RegCreateKeyExA( HKEY_CURRENT_USER, "Software\TestKey", 0, NULL, 0, KEY_ALL_ACCESS | KEY_WOW64_32KEY, NULL, &root64, NULL ); + ok( !err, "got %#lx.\n", err ); + err = RegOpenKeyExA( HKEY_CURRENT_USER, "Software\Wow6432Node\TestKey", 0, KEY_ALL_ACCESS | KEY_WOW64_64KEY, &root32 ); + ok ( err == ERROR_FILE_NOT_FOUND, "got %#lx.\n", err ); + + err = RegCreateKeyExA( HKEY_CURRENT_USER, "Software\Wow6432Node\TestKey", 0, NULL, 0, KEY_ALL_ACCESS | KEY_WOW64_32KEY, NULL, &root32, NULL ); + ok( !err, "got %#lx.\n", err ); + + dw = 1; + err = RegSetKeyValueA( root64, NULL, "val", REG_DWORD, &dw, sizeof(dw) ); + ok( !err, "got %#lx.\n", err ); + + dw = 2; + err = RegSetKeyValueA( root32, NULL, "val", REG_DWORD, &dw, sizeof(dw) ); + ok( !err, "got %#lx.\n", err ); + + err = RegCreateKeyExA( root64, "subkey", 0, NULL, 0, KEY_ALL_ACCESS | KEY_WOW64_64KEY, NULL, &key64, NULL ); + ok( !err, "got %#lx.\n", err ); + dw = 1; + err = RegSetKeyValueA( key64, NULL, "val", REG_DWORD, &dw, sizeof(dw) ); + ok( !err, "got %#lx.\n", err ); + + err = RegCreateKeyExA( root32, "subkey", 0, NULL, 0, KEY_ALL_ACCESS | KEY_WOW64_32KEY, NULL, &key32, NULL ); + ok( !err, "got %#lx.\n", err ); + dw = 2; + err = RegSetKeyValueA( key32, NULL, "val", REG_DWORD, &dw, sizeof(dw) ); + ok( !err, "got %#lx.\n", err ); + + err = RegOpenKeyExA( HKEY_CURRENT_USER, "Software\TestKey", 0, KEY_ALL_ACCESS | KEY_WOW64_64KEY, &key ); + ok( !err, "got %#lx.\n", err ); + len = sizeof(dw); + err = RegQueryValueExA( key, "val", NULL, NULL, (BYTE *)&dw, &len ); + ok( !err, "got %#lx.\n", err ); + ok( dw == 1, "got %lu.\n", dw ); + RegCloseKey( key ); + err = RegOpenKeyExA( HKEY_CURRENT_USER, "Software\TestKey", 0, KEY_ALL_ACCESS | KEY_WOW64_32KEY, &key ); + ok( !err, "got %#lx.\n", err ); + len = sizeof(dw); + err = RegQueryValueExA( key, "val", NULL, NULL, (BYTE *)&dw, &len ); + ok( !err, "got %#lx.\n", err ); + ok( dw == 1, "got %lu.\n", dw ); + RegCloseKey( key ); + err = RegOpenKeyExA( HKEY_CURRENT_USER, "Software\TestKey\subkey", 0, KEY_ALL_ACCESS | KEY_WOW64_64KEY, &key ); + ok( !err, "got %#lx.\n", err ); + len = sizeof(dw); + err = RegQueryValueExA( key, "val", NULL, NULL, (BYTE *)&dw, &len ); + ok( !err, "got %#lx.\n", err ); + ok( dw == 1, "got %lu.\n", dw ); + RegCloseKey( key ); + err = RegOpenKeyExA( HKEY_CURRENT_USER, "Software\TestKey\subkey", 0, KEY_ALL_ACCESS | KEY_WOW64_32KEY, &key ); + ok( !err, "got %#lx.\n", err ); + len = sizeof(dw); + err = RegQueryValueExA( key, "val", NULL, NULL, (BYTE *)&dw, &len ); + ok( !err, "got %#lx.\n", err ); + ok( dw == 1, "got %lu.\n", dw ); + RegCloseKey( key ); + + RegDeleteKeyA( key64, "" ); + RegCloseKey( key64 ); + RegDeleteKeyA( key32, "" ); + RegCloseKey( key32 ); + RegDeleteKeyA( root32, "" ); + RegCloseKey( root32 ); + RegDeleteKeyA( root64, "" ); + RegCloseKey( root64 ); }
static void test_classesroot(void)
The registry key is currently WoW shared in Wine if there is no WOW6432Node subkey. This is not the case on Windows, the key is present from somewhere at least on my install and can be created (as included test suggests). That stops sharing of HKCU/Software in Wine and breaks a lot of things. [1] lists, in particular, HKCU/Software as shared and the test confirms that.
This fixes Ubisoft login under Steam (and some other accompanying failures resulting from weirdly working registry) for some games (like Far Cry 3) which happen to create HKCU/Software/WOW6432Node key during their setup steps.
1. https://learn.microsoft.com/en-us/windows/win32/winprog64/shared-registry-ke...