[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 RtlFreeHeap(RtlGetProcessHeap(), 0, NameInformation);
50 ERR("NtQueryKey failed: 0x%08x\n", Status);
51 return RtlNtStatusToDosError(Status);
52 }
53
54 /* Make it a proper UNICODE_STRING */
55 InfoName.Length = NameInformation->NameLength;
56 InfoName.MaximumLength = NameInformation->NameLength;
57 InfoName.Buffer = NameInformation->Name;
58
59 Status = RtlDuplicateUnicodeString(RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE, &InfoName, KeyName);
60 if (!NT_SUCCESS(Status))
61 {
62 RtlFreeHeap(RtlGetProcessHeap(), 0, NameInformation);
63 ERR("RtlDuplicateUnicodeString failed: 0x%08x\n", Status);
64 return RtlNtStatusToDosError(Status);
65 }
66
67 RtlFreeHeap(RtlGetProcessHeap(), 0, NameInformation);
68
69 return ERROR_SUCCESS;
70 }
71
72 static
73 LONG
74 GetKeySam(
75 _In_ HKEY hKey,
76 _Out_ REGSAM* RegSam)
77 {
78 NTSTATUS Status;
79 OBJECT_BASIC_INFORMATION ObjectInfo;
80
81 Status = NtQueryObject(hKey, ObjectBasicInformation, &ObjectInfo, sizeof(ObjectInfo), NULL);
82 if (!NT_SUCCESS(Status))
83 {
84 ERR("NtQueryObject failed, Status %x08x\n", Status);
85 return RtlNtStatusToDosError(Status);
86 }
87
88 *RegSam = ObjectInfo.GrantedAccess;
89 return ERROR_SUCCESS;
90 }
91
92 /*
93 * Gets a HKLM key from an HKCU key.
94 */
95 static
96 LONG
97 GetFallbackHKCRKey(
98 _In_ HKEY hKey,
99 _Out_ HKEY* MachineKey)
100 {
101 UNICODE_STRING KeyName;
102 LPWSTR SubKeyName;
103 LONG ErrorCode;
104 REGSAM SamDesired;
105
106 /* Get the key name */
107 ErrorCode = GetKeyName(hKey, &KeyName);
108 if (ErrorCode != ERROR_SUCCESS)
109 return ErrorCode;
110
111 /* See if we really need a conversion */
112 if (RtlPrefixUnicodeString(&HKLM_ClassesPath, &KeyName, TRUE))
113 {
114 RtlFreeUnicodeString(&KeyName);
115 *MachineKey = hKey;
116 return ERROR_SUCCESS;
117 }
118
119 SubKeyName = KeyName.Buffer + 15; /* 15 == wcslen(L"\\Registry\\User\\") */
120 /* Skip the user token */
121 while (*SubKeyName++ != L'\\')
122 {
123 if (!*SubKeyName)
124 {
125 ERR("Key name %S is invalid!\n", KeyName.Buffer);
126 return ERROR_INTERNAL_ERROR;
127 }
128 }
129
130 /* Use the same access mask than the original key */
131 ErrorCode = GetKeySam(hKey, &SamDesired);
132 if (ErrorCode != ERROR_SUCCESS)
133 {
134 RtlFreeUnicodeString(&KeyName);
135 return ErrorCode;
136 }
137
138 /* Open the key. */
139 ErrorCode = RegOpenKeyExW(
140 HKEY_LOCAL_MACHINE,
141 SubKeyName,
142 0,
143 SamDesired,
144 MachineKey);
145
146 RtlFreeUnicodeString(&KeyName);
147
148 return ErrorCode;
149 }
150
151 /* Get the HKCU key (if it exists) from an HKCR key */
152 static
153 LONG
154 GetPreferredHKCRKey(
155 _In_ HKEY hKey,
156 _Out_ HKEY* PreferredKey)
157 {
158 UNICODE_STRING KeyName;
159 LPWSTR SubKeyName;
160 LONG ErrorCode;
161 REGSAM SamDesired;
162
163 /* Get the key name */
164 ErrorCode = GetKeyName(hKey, &KeyName);
165 if (ErrorCode != ERROR_SUCCESS)
166 return ErrorCode;
167
168 /* See if we really need a conversion */
169 if (!RtlPrefixUnicodeString(&HKLM_ClassesPath, &KeyName, TRUE))
170 {
171 RtlFreeUnicodeString(&KeyName);
172 *PreferredKey = hKey;
173 return ERROR_SUCCESS;
174 }
175
176 /* 18 == wcslen(L"\\Registry\\Machine\\") */
177 SubKeyName = KeyName.Buffer + 18;
178
179 /* Use the same access mask than the original key */
180 ErrorCode = GetKeySam(hKey, &SamDesired);
181 if (ErrorCode != ERROR_SUCCESS)
182 {
183 RtlFreeUnicodeString(&KeyName);
184 return ErrorCode;
185 }
186
187 /* Open the key. */
188 ErrorCode = RegOpenKeyExW(
189 HKEY_CURRENT_USER,
190 SubKeyName,
191 0,
192 SamDesired,
193 PreferredKey);
194
195 RtlFreeUnicodeString(&KeyName);
196
197 return ErrorCode;
198 }
199
200 /* Same as RegOpenKeyExW, but for HKEY_CLASSES_ROOT subkeys */
201 LONG
202 WINAPI
203 OpenHKCRKey(
204 _In_ HKEY hKey,
205 _In_ LPCWSTR lpSubKey,
206 _In_ DWORD ulOptions,
207 _In_ REGSAM samDesired,
208 _In_ PHKEY phkResult)
209 {
210 HKEY QueriedKey;
211 LONG ErrorCode;
212
213 ASSERT(IsHKCRKey(hKey));
214
215 /* Remove the HKCR flag while we're working */
216 hKey = (HKEY)(((ULONG_PTR)hKey) & ~0x2);
217
218 ErrorCode = GetPreferredHKCRKey(hKey, &QueriedKey);
219
220 if (ErrorCode == ERROR_FILE_NOT_FOUND)
221 {
222 /* The key doesn't exist on HKCU side, no chance for a subkey */
223 ErrorCode = RegOpenKeyExW(hKey, lpSubKey, ulOptions, samDesired, phkResult);
224 if (ErrorCode == ERROR_SUCCESS)
225 MakeHKCRKey(phkResult);
226 return ErrorCode;
227 }
228
229 if (ErrorCode != ERROR_SUCCESS)
230 {
231 /* Somehow we failed for another reason (maybe deleted key or whatever) */
232 return ErrorCode;
233 }
234
235 /* Try on the HKCU side */
236 ErrorCode = RegOpenKeyExW(QueriedKey, lpSubKey, ulOptions, samDesired, phkResult);
237 if (ErrorCode == ERROR_SUCCESS)
238 MakeHKCRKey(phkResult);
239
240 /* Close it if we must */
241 if (QueriedKey != hKey)
242 {
243 RegCloseKey(QueriedKey);
244 }
245
246 /* Anything else than ERROR_FILE_NOT_FOUND means that we found it, even if it is with failures. */
247 if (ErrorCode != ERROR_FILE_NOT_FOUND)
248 return ErrorCode;
249
250 /* If we're here, we must open from HKLM key. */
251 ErrorCode = GetFallbackHKCRKey(hKey, &QueriedKey);
252 if (ErrorCode != ERROR_SUCCESS)
253 {
254 /* Maybe the key doesn't exist in the HKLM view */
255 return ErrorCode;
256 }
257
258 ErrorCode = RegOpenKeyExW(QueriedKey, lpSubKey, ulOptions, samDesired, phkResult);
259 if (ErrorCode == ERROR_SUCCESS)
260 MakeHKCRKey(phkResult);
261
262 /* Close it if we must */
263 if (QueriedKey != hKey)
264 {
265 RegCloseKey(QueriedKey);
266 }
267
268 return ErrorCode;
269 }
270
271 LONG
272 WINAPI
273 DeleteHKCRKey(
274 _In_ HKEY hKey,
275 _In_ LPCWSTR lpSubKey,
276 _In_ REGSAM RegSam,
277 _In_ DWORD Reserved)
278 {
279 HKEY QueriedKey;
280 LONG ErrorCode;
281
282 ASSERT(IsHKCRKey(hKey));
283
284 /* Remove the HKCR flag while we're working */
285 hKey = (HKEY)(((ULONG_PTR)hKey) & ~0x2);
286
287 ErrorCode = GetPreferredHKCRKey(hKey, &QueriedKey);
288
289 if (ErrorCode == ERROR_FILE_NOT_FOUND)
290 {
291 /* The key doesn't exist on HKCU side, no chance for a subkey */
292 return RegDeleteKeyExW(hKey, lpSubKey, RegSam, Reserved);
293 }
294
295 if (ErrorCode != ERROR_SUCCESS)
296 {
297 /* Somehow we failed for another reason (maybe deleted key or whatever) */
298 return ErrorCode;
299 }
300
301 ErrorCode = RegDeleteKeyExW(QueriedKey, lpSubKey, RegSam, Reserved);
302
303 /* Close it if we must */
304 if (QueriedKey != hKey)
305 {
306 /* The original key is on the machine view */
307 RegCloseKey(QueriedKey);
308 }
309
310 /* Anything else than ERROR_FILE_NOT_FOUND means that we found it, even if it is with failures. */
311 if (ErrorCode != ERROR_FILE_NOT_FOUND)
312 return ErrorCode;
313
314 /* If we're here, we must open from HKLM key. */
315 ErrorCode = GetFallbackHKCRKey(hKey, &QueriedKey);
316 if (ErrorCode != ERROR_SUCCESS)
317 {
318 /* Maybe the key doesn't exist in the HKLM view */
319 return ErrorCode;
320 }
321
322 ErrorCode = RegDeleteKeyExW(QueriedKey, lpSubKey, RegSam, Reserved);
323
324 /* Close it if we must */
325 if (QueriedKey != hKey)
326 {
327 RegCloseKey(QueriedKey);
328 }
329
330 return ErrorCode;
331 }