[ADVAPI32]
[reactos.git] / reactos / dll / win32 / advapi32 / reg / hkcr.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS system libraries
4 * FILE: lib/advapi32/reg/hkcr.c
5 * PURPOSE: Registry functions - HKEY_CLASSES_ROOT abstraction
6 * PROGRAMMER: Jerôme Gardou (jerome.gardou@reactos.org)
7 */
8
9 #include <advapi32.h>
10
11 #include <ndk/cmfuncs.h>
12 #include <pseh/pseh2.h>
13
14 #include "reg.h"
15
16 WINE_DEFAULT_DEBUG_CHANNEL(reg);
17
18 static const UNICODE_STRING HKLM_ClassesPath = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\Software\\Classes");
19
20 static
21 LONG
22 GetKeyName(HKEY hKey, PUNICODE_STRING KeyName)
23 {
24 UNICODE_STRING InfoName;
25 PKEY_NAME_INFORMATION NameInformation;
26 ULONG InfoLength;
27 NTSTATUS Status;
28
29 /* Get info length */
30 InfoLength = 0;
31 Status = NtQueryKey(hKey, KeyNameInformation, NULL, 0, &InfoLength);
32 if (Status != STATUS_BUFFER_TOO_SMALL)
33 {
34 ERR("NtQueryKey returned unexpected Status: 0x%08x\n", Status);
35 return RtlNtStatusToDosError(Status);
36 }
37
38 /* Get it for real */
39 NameInformation = RtlAllocateHeap(RtlGetProcessHeap(), 0, InfoLength);
40 if (NameInformation == NULL)
41 {
42 ERR("Failed to allocate %lu bytes", InfoLength);
43 return ERROR_NOT_ENOUGH_MEMORY;
44 }
45
46 Status = NtQueryKey(hKey, KeyNameInformation, NameInformation, InfoLength, &InfoLength);
47 if (!NT_SUCCESS(Status))
48 {
49 ERR("NtQueryKey failed: 0x%08x\n", Status);
50 return RtlNtStatusToDosError(Status);
51 }
52
53 /* Make it a proper UNICODE_STRING */
54 InfoName.Length = NameInformation->NameLength;
55 InfoName.MaximumLength = NameInformation->NameLength;
56 InfoName.Buffer = NameInformation->Name;
57
58 Status = RtlDuplicateUnicodeString(RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE, &InfoName, KeyName);
59 if (!NT_SUCCESS(Status))
60 {
61 ERR("RtlDuplicateUnicodeString failed: 0x%08x\n", Status);
62 return RtlNtStatusToDosError(Status);
63 }
64
65 RtlFreeHeap(RtlGetProcessHeap(), 0, NameInformation);
66
67 return ERROR_SUCCESS;
68 }
69
70 static
71 LONG
72 GetKeySam(
73 _In_ HKEY hKey,
74 _Out_ REGSAM* RegSam)
75 {
76 NTSTATUS Status;
77 OBJECT_BASIC_INFORMATION ObjectInfo;
78
79 Status = NtQueryObject(hKey, ObjectBasicInformation, &ObjectInfo, sizeof(ObjectInfo), NULL);
80 if (!NT_SUCCESS(Status))
81 {
82 ERR("NtQueryObject failed, Status %x08x\n", Status);
83 return RtlNtStatusToDosError(Status);
84 }
85
86 *RegSam = ObjectInfo.GrantedAccess;
87 return ERROR_SUCCESS;
88 }
89
90 /*
91 * Gets a HKLM key from an HKCU key.
92 */
93 static
94 LONG
95 GetFallbackHKCRKey(
96 _In_ HKEY hKey,
97 _Out_ HKEY* MachineKey)
98 {
99 UNICODE_STRING KeyName;
100 LPWSTR SubKeyName;
101 LONG ErrorCode;
102 REGSAM SamDesired;
103
104 /* Get the key name */
105 ErrorCode = GetKeyName(hKey, &KeyName);
106 if (ErrorCode != ERROR_SUCCESS)
107 return ErrorCode;
108
109 /* See if we really need a conversion */
110 if (RtlPrefixUnicodeString(&HKLM_ClassesPath, &KeyName, TRUE))
111 {
112 RtlFreeUnicodeString(&KeyName);
113 *MachineKey = hKey;
114 return ERROR_SUCCESS;
115 }
116
117 SubKeyName = KeyName.Buffer + 15; /* 15 == wcslen(L"\\Registry\\User\\") */
118 /* Skip the user token */
119 while (*SubKeyName++ != L'\\')
120 {
121 if (!*SubKeyName)
122 {
123 ERR("Key name %S is invalid!\n", KeyName.Buffer);
124 return ERROR_INTERNAL_ERROR;
125 }
126 }
127
128 /* Use the same access mask than the original key */
129 ErrorCode = GetKeySam(hKey, &SamDesired);
130 if (ErrorCode != ERROR_SUCCESS)
131 {
132 RtlFreeUnicodeString(&KeyName);
133 return ErrorCode;
134 }
135
136 /* Open the key. */
137 ErrorCode = RegOpenKeyExW(
138 HKEY_LOCAL_MACHINE,
139 SubKeyName,
140 0,
141 SamDesired,
142 MachineKey);
143
144 RtlFreeUnicodeString(&KeyName);
145
146 return ErrorCode;
147 }
148
149 /* Get the HKCU key (if it exists) from an HKCR key */
150 static
151 LONG
152 GetPreferredHKCRKey(
153 _In_ HKEY hKey,
154 _Out_ HKEY* PreferredKey)
155 {
156 UNICODE_STRING KeyName;
157 LPWSTR SubKeyName;
158 LONG ErrorCode;
159 REGSAM SamDesired;
160
161 /* Get the key name */
162 ErrorCode = GetKeyName(hKey, &KeyName);
163 if (ErrorCode != ERROR_SUCCESS)
164 return ErrorCode;
165
166 /* See if we really need a conversion */
167 if (!RtlPrefixUnicodeString(&HKLM_ClassesPath, &KeyName, TRUE))
168 {
169 RtlFreeUnicodeString(&KeyName);
170 *PreferredKey = hKey;
171 return ERROR_SUCCESS;
172 }
173
174 /* 18 == wcslen(L"\\Registry\\Machine\\") */
175 SubKeyName = KeyName.Buffer + 18;
176
177 /* Use the same access mask than the original key */
178 ErrorCode = GetKeySam(hKey, &SamDesired);
179 if (ErrorCode != ERROR_SUCCESS)
180 {
181 RtlFreeUnicodeString(&KeyName);
182 return ErrorCode;
183 }
184
185 /* Open the key. */
186 ErrorCode = RegOpenKeyExW(
187 HKEY_CURRENT_USER,
188 SubKeyName,
189 0,
190 SamDesired,
191 PreferredKey);
192
193 RtlFreeUnicodeString(&KeyName);
194
195 return ErrorCode;
196 }
197
198 /* Same as RegOpenKeyExW, but for HKEY_CLASSES_ROOT subkeys */
199 LONG
200 WINAPI
201 OpenHKCRKey(
202 _In_ HKEY hKey,
203 _In_ LPCWSTR lpSubKey,
204 _In_ DWORD ulOptions,
205 _In_ REGSAM samDesired,
206 _In_ PHKEY phkResult)
207 {
208 HKEY QueriedKey;
209 LONG ErrorCode;
210
211 ASSERT(IsHKCRKey(hKey));
212
213 /* Remove the HKCR flag while we're working */
214 hKey = (HKEY)(((ULONG_PTR)hKey) & ~0x2);
215
216 ErrorCode = GetPreferredHKCRKey(hKey, &QueriedKey);
217
218 if (ErrorCode == ERROR_FILE_NOT_FOUND)
219 {
220 /* The key doesn't exist on HKCU side, no chance for a subkey */
221 ErrorCode = RegOpenKeyExW(hKey, lpSubKey, ulOptions, samDesired, phkResult);
222 if (ErrorCode == ERROR_SUCCESS)
223 MakeHKCRKey(phkResult);
224 return ErrorCode;
225 }
226
227 if (ErrorCode != ERROR_SUCCESS)
228 {
229 /* Somehow we failed for another reason (maybe deleted key or whatever) */
230 return ErrorCode;
231 }
232
233 /* Try on the HKCU side */
234 ErrorCode = RegOpenKeyExW(QueriedKey, lpSubKey, ulOptions, samDesired, phkResult);
235 if (ErrorCode == ERROR_SUCCESS)
236 MakeHKCRKey(phkResult);
237
238 /* Close it if we must */
239 if (QueriedKey != hKey)
240 {
241 /* The original key is on the machine view */
242 RegCloseKey(QueriedKey);
243 }
244
245 /* Anything else than ERROR_FILE_NOT_FOUND means that we found it, even if it is with failures. */
246 if (ErrorCode != ERROR_FILE_NOT_FOUND)
247 return ErrorCode;
248
249 /* If we're here, we must open from HKLM key. */
250 ErrorCode = GetFallbackHKCRKey(hKey, &QueriedKey);
251 if (ErrorCode != ERROR_SUCCESS)
252 {
253 /* Maybe the key doesn't exist in the HKLM view */
254 return ErrorCode;
255 }
256
257 ErrorCode = RegOpenKeyExW(QueriedKey, lpSubKey, ulOptions, samDesired, phkResult);
258 if (ErrorCode == ERROR_SUCCESS)
259 MakeHKCRKey(phkResult);
260
261 /* Close it if we must */
262 if (QueriedKey != hKey)
263 {
264 /* The original key is on the machine view */
265 RegCloseKey(QueriedKey);
266 }
267
268 return ErrorCode;
269 }