From 4f28264863db577c4af099aab8cafa046e79e41b Mon Sep 17 00:00:00 2001
From: George Stephanos <gaf.stephanos@gmail.com>
Date: Sat, 24 Aug 2013 20:30:05 +0200
Subject: [PATCH 1/2] advapi32: Foundation for HKCR merge

---
 dlls/advapi32/registry.c       | 255 ++++++++++++++++++++++++++++++++++++++++-
 dlls/advapi32/tests/registry.c |   6 +-
 2 files changed, 256 insertions(+), 5 deletions(-)

diff --git a/dlls/advapi32/registry.c b/dlls/advapi32/registry.c
index c01e125..d9135e1 100644
--- a/dlls/advapi32/registry.c
+++ b/dlls/advapi32/registry.c
@@ -65,7 +65,7 @@ static const WCHAR name_DYN_DATA[] =
 
 static const WCHAR * const root_key_names[] =
 {
-    name_CLASSES_ROOT,
+    NULL,         /* HKEY_CLASSES_ROOT is dynamic */
     NULL,         /* HKEY_CURRENT_USER is determined dynamically */
     name_LOCAL_MACHINE,
     name_USERS,
@@ -79,6 +79,34 @@ static const WCHAR * const root_key_names[] =
 static HKEY special_root_keys[NB_SPECIAL_ROOT_KEYS];
 static BOOL hkcu_cache_disabled;
 
+typedef struct {
+    BOOL  init;
+    HKEY  hklm;
+} opened_hkcr_t;
+
+/* ############################### */
+
+static CRITICAL_SECTION hkcr_handles_cs;
+static CRITICAL_SECTION_DEBUG hkcr_handles_cs_debug =
+{
+    0, 0, &hkcr_handles_cs,
+    { &hkcr_handles_cs_debug.ProcessLocksList, &hkcr_handles_cs_debug.ProcessLocksList },
+      0, 0, { (DWORD_PTR)(__FILE__ ": hkcr_handles_cs") }
+};
+static CRITICAL_SECTION hkcr_handles_cs = { &hkcr_handles_cs_debug, -1, 0, 0, 0, 0 };
+
+/* ############################### */
+
+static opened_hkcr_t *hkcr_handles;
+static UINT nb_hkcr_handles;
+
+static UINT *hkcr_handles_free;
+static UINT nb_hkcr_handles_free;
+
+#define SPECIAL_HKEY_MASK 0x80000003
+#define HKCR_FLAG 2
+#define IS_HKCR(hk) (((UINT_PTR)(hk) & SPECIAL_HKEY_MASK) == HKCR_FLAG)
+
 static const int is_win64 = (sizeof(void *) > sizeof(int));
 
 /* check if value type needs string conversion (Ansi<->Unicode) */
@@ -224,13 +252,148 @@ static NTSTATUS open_key( HKEY *retkey, ACCESS_MASK access, OBJECT_ATTRIBUTES *a
 
 }
 
+static opened_hkcr_t *get_opened_hkcr( HKEY hkey )
+{
+    UINT_PTR idx = (UINT_PTR)hkey >> 2;
+    opened_hkcr_t *ret = NULL;
+
+    if (!IS_HKCR(hkey)) return NULL;
+
+    EnterCriticalSection(&hkcr_handles_cs);
+    if (idx < nb_hkcr_handles && hkcr_handles[idx].init) ret = &hkcr_handles[idx];
+    LeaveCriticalSection(&hkcr_handles_cs);
+
+    return ret;
+}
+
+static LSTATUS create_hkcr_struct( HKEY *hkey, opened_hkcr_t **hkcr )
+{
+    UINT_PTR handle, i;
+    LSTATUS ret = ERROR_SUCCESS;
+
+    EnterCriticalSection( &hkcr_handles_cs );
+
+    if (!nb_hkcr_handles_free)
+    {
+        opened_hkcr_t *new_array;
+        UINT *new_array_free;
+
+        if (hkcr_handles)
+        {
+            new_array = HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, hkcr_handles,
+                                     (nb_hkcr_handles * 2) * sizeof(*new_array) );
+            new_array_free = HeapReAlloc( GetProcessHeap(), 0, hkcr_handles_free,
+                                          (nb_hkcr_handles * 2) * sizeof(*new_array_free) );
+        }
+        else
+        {
+            new_array = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 16 * sizeof(*new_array) );
+            new_array_free = HeapAlloc( GetProcessHeap(), 0, 16 * sizeof(*new_array_free) );
+        }
+
+        if (!new_array || !new_array_free)
+        {
+            ret = ERROR_NOT_ENOUGH_MEMORY;
+            goto end;
+        }
+
+        hkcr_handles = new_array;
+        hkcr_handles_free = new_array_free;
+
+        nb_hkcr_handles = !nb_hkcr_handles ? 16 : nb_hkcr_handles * 2;
+
+        for (i = nb_hkcr_handles == 16 ? 0 : nb_hkcr_handles/2; i < nb_hkcr_handles; i++)
+            hkcr_handles_free[nb_hkcr_handles_free++] = i;
+    }
+
+    handle = hkcr_handles_free[--nb_hkcr_handles_free];
+    memset( &hkcr_handles[handle], 0, sizeof(opened_hkcr_t) );
+    hkcr_handles[handle].init = TRUE;
+    *hkcr = &hkcr_handles[handle];
+    *hkey = (HKEY)( handle << 2 | HKCR_FLAG );
+
+end:
+    LeaveCriticalSection( &hkcr_handles_cs );
+    return ret;
+}
+
+static HKEY resolve_hkcr( HKEY hkey )
+{
+    HKEY ret;
+    opened_hkcr_t *hkcr;
+
+    hkcr = get_opened_hkcr(hkey);
+    if (!hkcr) return NULL;
+
+    EnterCriticalSection( &hkcr_handles_cs );
+    ret = hkcr->hklm;
+    LeaveCriticalSection( &hkcr_handles_cs );
+
+    return ret;
+}
+
+static LSTATUS WINAPI create_hkcr( HKEY hkey, CONST WCHAR *name, WCHAR *class,
+                                   DWORD options, REGSAM samDesired,
+                                   SECURITY_ATTRIBUTES *sa, HKEY *retkey, DWORD *dispos )
+{
+    HKEY hklm;
+    opened_hkcr_t *hkcr = NULL;
+    LSTATUS res;
+    *retkey = 0;
+
+    if (hkey != HKEY_LOCAL_MACHINE) hkey = resolve_hkcr( hkey );
+    res = RegCreateKeyExW( hkey, name, 0, class, options, samDesired, sa, &hklm, dispos );
+    if (res) return res;
+
+    if ((res = create_hkcr_struct(retkey, &hkcr)))
+    {
+        RegCloseKey( hklm );
+        return res;
+    }
+
+    EnterCriticalSection( &hkcr_handles_cs );
+    hkcr->hklm = hklm;
+    LeaveCriticalSection( &hkcr_handles_cs );
+
+    return res;
+}
+
+static LSTATUS WINAPI open_hkcr( HKEY hkey, CONST WCHAR *name, REGSAM samDesired, HKEY *retkey )
+{
+    HKEY hklm;
+    opened_hkcr_t *hkcr = NULL;
+    LSTATUS res;
+    *retkey = 0;
+
+    hkey = resolve_hkcr( hkey );
+    res = RegOpenKeyExW( hkey, name, 0, samDesired, &hklm );
+    if (res) return res;
+
+    if ((res = create_hkcr_struct(retkey, &hkcr)))
+    {
+        RegCloseKey( hklm );
+        return res;
+    }
+
+    EnterCriticalSection( &hkcr_handles_cs );
+    hkcr->hklm = hklm;
+    LeaveCriticalSection( &hkcr_handles_cs );
+
+    return res;
+}
+
+
 /* create one of the HKEY_* special root keys */
 static HKEY create_special_root_hkey( HKEY hkey, DWORD access )
 {
     HKEY ret = 0;
     int idx = HandleToUlong(hkey) - HandleToUlong(HKEY_SPECIAL_ROOT_FIRST);
 
-    if (HandleToUlong(hkey) == HandleToUlong(HKEY_CURRENT_USER))
+    if (HandleToUlong(hkey) == HandleToUlong(HKEY_CLASSES_ROOT))
+    {
+        if (create_hkcr( HKEY_LOCAL_MACHINE, name_CLASSES_ROOT + 8, NULL, 0, access, NULL, &hkey, NULL )) return 0;
+    }
+    else if (HandleToUlong(hkey) == HandleToUlong(HKEY_CURRENT_USER))
     {
         if (RtlOpenCurrentUser( access, (HANDLE *)&hkey )) return 0;
         TRACE( "HKEY_CURRENT_USER -> %p\n", hkey );
@@ -258,7 +421,7 @@ static HKEY create_special_root_hkey( HKEY hkey, DWORD access )
     if (!(ret = InterlockedCompareExchangePointer( (void **)&special_root_keys[idx], hkey, 0 )))
         ret = hkey;
     else
-        NtClose( hkey );  /* somebody beat us to it */
+        RegCloseKey( hkey );
     return ret;
 }
 
@@ -276,6 +439,22 @@ static inline HKEY get_special_root_hkey( HKEY hkey )
     return ret;
 }
 
+static LSTATUS close_hkcr( HKEY hkey )
+{
+    opened_hkcr_t *hkcr;
+
+    hkcr = get_opened_hkcr(hkey);
+    if (!hkcr) return ERROR_INVALID_HANDLE;
+
+    EnterCriticalSection( &hkcr_handles_cs );
+    RegCloseKey( hkcr->hklm );
+    hkcr->init = FALSE;
+    hkcr_handles_free[nb_hkcr_handles_free++] = (UINT_PTR)hkey >> 2;
+    LeaveCriticalSection( &hkcr_handles_cs );
+
+    return ERROR_SUCCESS;
+}
+
 
 /******************************************************************************
  * RegOverridePredefKey   [ADVAPI32.@]
@@ -320,6 +499,7 @@ LSTATUS WINAPI RegCreateKeyExW( HKEY hkey, LPCWSTR name, DWORD reserved, LPWSTR
 
     if (reserved) return ERROR_INVALID_PARAMETER;
     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
+    if (IS_HKCR(hkey)) return create_hkcr( hkey, name, class, options, access, sa, retkey, dispos );
 
     attr.Length = sizeof(attr);
     attr.RootDirectory = hkey;
@@ -375,6 +555,20 @@ LSTATUS WINAPI RegCreateKeyExA( HKEY hkey, LPCSTR name, DWORD reserved, LPSTR cl
     }
     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
 
+    if (IS_HKCR(hkey))
+    {
+        MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, name, -1,
+                             NtCurrentTeb()->StaticUnicodeBuffer,
+                             sizeof(NtCurrentTeb()->StaticUnicodeBuffer)/sizeof(WCHAR) );
+        RtlInitAnsiString( &classA, class );
+        if (!(status = RtlAnsiStringToUnicodeString( &classW, &classA, TRUE )))
+        {
+            status = create_hkcr( hkey, NtCurrentTeb()->StaticUnicodeBuffer, classW.Buffer, options, access, sa, retkey, dispos );
+            RtlFreeUnicodeString( &classW );
+        }
+        return status;
+    }
+
     attr.Length = sizeof(attr);
     attr.RootDirectory = hkey;
     attr.ObjectName = &NtCurrentTeb()->StaticUnicodeString;
@@ -450,6 +644,8 @@ LSTATUS WINAPI RegOpenKeyExW( HKEY hkey, LPCWSTR name, DWORD options, REGSAM acc
     if (!retkey) return ERROR_INVALID_PARAMETER;
     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
 
+    if (IS_HKCR(hkey)) return open_hkcr( hkey, name, access, retkey );
+
     attr.Length = sizeof(attr);
     attr.RootDirectory = hkey;
     attr.ObjectName = &nameW;
@@ -497,6 +693,14 @@ LSTATUS WINAPI RegOpenKeyExA( HKEY hkey, LPCSTR name, DWORD options, REGSAM acce
 
     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
 
+    if (IS_HKCR(hkey))
+    {
+        MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, name, -1,
+                             NtCurrentTeb()->StaticUnicodeBuffer,
+                             sizeof(NtCurrentTeb()->StaticUnicodeBuffer)/sizeof(WCHAR) );
+        return open_hkcr( hkey, NtCurrentTeb()->StaticUnicodeBuffer, access, retkey );
+    }
+
     attr.Length = sizeof(attr);
     attr.RootDirectory = hkey;
     attr.ObjectName = &NtCurrentTeb()->StaticUnicodeString;
@@ -623,6 +827,8 @@ LSTATUS WINAPI RegEnumKeyExW( HKEY hkey, DWORD index, LPWSTR name, LPDWORD name_
     if (reserved) return ERROR_INVALID_PARAMETER;
     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
 
+    if (IS_HKCR(hkey) && !(hkey = resolve_hkcr( hkey ))) return ERROR_INVALID_HANDLE;
+
     status = NtEnumerateKey( hkey, index, KeyNodeInformation,
                              buffer, sizeof(buffer), &total_size );
 
@@ -687,6 +893,8 @@ LSTATUS WINAPI RegEnumKeyExA( HKEY hkey, DWORD index, LPSTR name, LPDWORD name_l
     if (reserved) return ERROR_INVALID_PARAMETER;
     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
 
+    if (IS_HKCR(hkey) && !(hkey = resolve_hkcr( hkey ))) return ERROR_INVALID_HANDLE;
+
     status = NtEnumerateKey( hkey, index, KeyNodeInformation,
                              buffer, sizeof(buffer), &total_size );
 
@@ -814,6 +1022,8 @@ LSTATUS WINAPI RegQueryInfoKeyW( HKEY hkey, LPWSTR class, LPDWORD class_len, LPD
     if (class && !class_len && is_version_nt()) return ERROR_INVALID_PARAMETER;
     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
 
+    if (IS_HKCR(hkey) && !(hkey = resolve_hkcr( hkey ))) return ERROR_INVALID_HANDLE;
+
     status = NtQueryKey( hkey, KeyFullInformation, buffer, sizeof(buffer), &total_size );
     if (status && status != STATUS_BUFFER_OVERFLOW) goto done;
 
@@ -1000,6 +1210,8 @@ LSTATUS WINAPI RegQueryInfoKeyA( HKEY hkey, LPSTR class, LPDWORD class_len, LPDW
     if (class && !class_len && is_version_nt()) return ERROR_INVALID_PARAMETER;
     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
 
+    if (IS_HKCR(hkey) && !(hkey = resolve_hkcr( hkey ))) return ERROR_INVALID_HANDLE;
+
     status = NtQueryKey( hkey, KeyFullInformation, buffer, sizeof(buffer), &total_size );
     if (status && status != STATUS_BUFFER_OVERFLOW) goto done;
 
@@ -1062,6 +1274,7 @@ LSTATUS WINAPI RegCloseKey( HKEY hkey )
 {
     if (!hkey) return ERROR_INVALID_HANDLE;
     if (hkey >= (HKEY)0x80000000) return ERROR_SUCCESS;
+    if (IS_HKCR(hkey)) return close_hkcr( hkey );
     return RtlNtStatusToDosError( NtClose( hkey ) );
 }
 
@@ -1078,6 +1291,16 @@ LSTATUS WINAPI RegDeleteKeyExW( HKEY hkey, LPCWSTR name, REGSAM access, DWORD re
 
     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
 
+    if (IS_HKCR(hkey))
+    {
+        if (!(ret = open_hkcr( hkey, name, access | DELETE, &tmp )))
+        {
+            ret = RtlNtStatusToDosError( NtDeleteKey( resolve_hkcr(tmp) ) );
+            close_hkcr( tmp );
+        }
+        return ret;
+    }
+
     access &= KEY_WOW64_64KEY | KEY_WOW64_32KEY;
     if (!(ret = RegOpenKeyExW( hkey, name, 0, access | DELETE, &tmp )))
     {
@@ -1112,6 +1335,19 @@ LSTATUS WINAPI RegDeleteKeyExA( HKEY hkey, LPCSTR name, REGSAM access, DWORD res
 
     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
 
+    if (IS_HKCR(hkey))
+    {
+        MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, name, -1,
+                             NtCurrentTeb()->StaticUnicodeBuffer,
+                             sizeof(NtCurrentTeb()->StaticUnicodeBuffer)/sizeof(WCHAR) );
+        if (!(ret = open_hkcr( hkey, NtCurrentTeb()->StaticUnicodeBuffer, access | DELETE, &tmp )))
+        {
+            ret = RtlNtStatusToDosError( NtDeleteKey( resolve_hkcr(tmp) ) );
+            close_hkcr( tmp );
+        }
+        return ret;
+    }
+
     access &= KEY_WOW64_64KEY | KEY_WOW64_32KEY;
     if (!(ret = RegOpenKeyExA( hkey, name, 0, access | DELETE, &tmp )))
     {
@@ -1193,6 +1429,8 @@ LSTATUS WINAPI RegSetValueExW( HKEY hkey, LPCWSTR name, DWORD reserved,
     }
     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
 
+    if (IS_HKCR(hkey) && !(hkey = resolve_hkcr( hkey ))) return ERROR_INVALID_HANDLE;
+
     RtlInitUnicodeString( &nameW, name );
     return RtlNtStatusToDosError( NtSetValueKey( hkey, &nameW, 0, type, data, count ) );
 }
@@ -1231,6 +1469,8 @@ LSTATUS WINAPI RegSetValueExA( HKEY hkey, LPCSTR name, DWORD reserved, DWORD typ
 
     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
 
+    if (IS_HKCR(hkey) && !(hkey = resolve_hkcr( hkey ))) return ERROR_INVALID_HANDLE;
+
     if (is_string( type )) /* need to convert to Unicode */
     {
         DWORD lenW;
@@ -1336,6 +1576,8 @@ LSTATUS WINAPI RegQueryValueExW( HKEY hkey, LPCWSTR name, LPDWORD reserved, LPDW
     if ((data && !count) || reserved) return ERROR_INVALID_PARAMETER;
     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
 
+    if (IS_HKCR(hkey) && !(hkey = resolve_hkcr( hkey ))) return ERROR_INVALID_HANDLE;
+
     RtlInitUnicodeString( &name_str, name );
 
     if (data) total_size = min( sizeof(buffer), *count + info_size );
@@ -1426,6 +1668,8 @@ LSTATUS WINAPI RegQueryValueExA( HKEY hkey, LPCSTR name, LPDWORD reserved, LPDWO
     if ((data && !count) || reserved) return ERROR_INVALID_PARAMETER;
     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
 
+    if (IS_HKCR(hkey) && !(hkey = resolve_hkcr( hkey ))) return ERROR_INVALID_HANDLE;
+
     if (count) datalen = *count;
     if (!data && count) *count = 0;
 
@@ -1864,6 +2108,8 @@ LSTATUS WINAPI RegEnumValueW( HKEY hkey, DWORD index, LPWSTR value, LPDWORD val_
     if ((data && !count) || reserved) return ERROR_INVALID_PARAMETER;
     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
 
+    if (IS_HKCR(hkey) && !(hkey = resolve_hkcr( hkey ))) return ERROR_INVALID_HANDLE;
+
     total_size = info_size + (MAX_PATH + 1) * sizeof(WCHAR);
     if (data) total_size += *count;
     total_size = min( sizeof(buffer), total_size );
@@ -1949,6 +2195,8 @@ LSTATUS WINAPI RegEnumValueA( HKEY hkey, DWORD index, LPSTR value, LPDWORD val_c
     if ((data && !count) || reserved) return ERROR_INVALID_PARAMETER;
     if (!(hkey = get_special_root_hkey( hkey ))) return ERROR_INVALID_HANDLE;
 
+    if (IS_HKCR(hkey) && !(hkey = resolve_hkcr( hkey ))) return ERROR_INVALID_HANDLE;
+
     total_size = info_size + (MAX_PATH + 1) * sizeof(WCHAR);
     if (data) total_size += *count;
     total_size = min( sizeof(buffer), total_size );
@@ -2468,6 +2716,7 @@ LSTATUS WINAPI RegGetKeySecurity( HKEY hkey, SECURITY_INFORMATION SecurityInform
 LSTATUS WINAPI RegFlushKey( HKEY hkey )
 {
     hkey = get_special_root_hkey( hkey );
+    if (IS_HKCR(hkey) && !(hkey = resolve_hkcr( hkey ))) return ERROR_INVALID_HANDLE;
     if (!hkey) return ERROR_INVALID_HANDLE;
 
     return RtlNtStatusToDosError( NtFlushKey( hkey ) );
diff --git a/dlls/advapi32/tests/registry.c b/dlls/advapi32/tests/registry.c
index 981ac93..027728a 100644
--- a/dlls/advapi32/tests/registry.c
+++ b/dlls/advapi32/tests/registry.c
@@ -31,7 +31,9 @@
 #include "winerror.h"
 #include "aclapi.h"
 
-#define IS_HKCR(hk) ((UINT_PTR)hk > 0 && ((UINT_PTR)hk & 3) == 2)
+#define SPECIAL_HKEY_MASK 0x80000003
+#define HKCR_FLAG 2
+#define IS_HKCR(hk) (((UINT_PTR)(hk) & SPECIAL_HKEY_MASK) == HKCR_FLAG)
 
 static HKEY hkey_main;
 static DWORD GLE;
@@ -2584,7 +2586,7 @@ static void test_classesroot_mask(void)
 
     res = RegOpenKeyA( HKEY_CLASSES_ROOT, "CLSID", &hkey );
     ok(res == ERROR_SUCCESS, "RegOpenKeyA failed: %d\n", res);
-    todo_wine ok(IS_HKCR(hkey) || broken(!IS_HKCR(hkey)) /* WinNT */,
+    ok(IS_HKCR(hkey) || broken(!IS_HKCR(hkey)) /* WinNT */,
                  "hkcr mask not set in %p\n", hkey);
     RegCloseKey( hkey );
 
-- 
1.8.2.3

