1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Setup Library
4 * FILE: base/setup/lib/regutil.c
5 * PURPOSE: Registry utility functions
6 * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr)
7 */
8
9 /* INCLUDES *****************************************************************/
10
11 #include "precomp.h"
12 #include "filesup.h"
13
14 #include "regutil.h"
15
16 #define NDEBUG
17 #include <debug.h>
18
19 /* GLOBALS ******************************************************************/
20
21 static UNICODE_STRING SymbolicLinkValueName =
22 RTL_CONSTANT_STRING(L"SymbolicLinkValue");
23
24 /* FUNCTIONS ****************************************************************/
25
26 /*
27 * This function is similar to the one in dlls/win32/advapi32/reg/reg.c
28 * TODO: I should review both of them very carefully, because they may need
29 * some adjustments in their NtCreateKey calls, especially for CreateOptions
30 * stuff etc...
31 */
32 NTSTATUS
CreateNestedKey(PHANDLE KeyHandle,ACCESS_MASK DesiredAccess,POBJECT_ATTRIBUTES ObjectAttributes,ULONG CreateOptions)33 CreateNestedKey(PHANDLE KeyHandle,
34 ACCESS_MASK DesiredAccess,
35 POBJECT_ATTRIBUTES ObjectAttributes,
36 ULONG CreateOptions)
37 {
38 OBJECT_ATTRIBUTES LocalObjectAttributes;
39 UNICODE_STRING LocalKeyName;
40 ULONG Disposition;
41 NTSTATUS Status;
42 USHORT FullNameLength;
43 PWCHAR Ptr;
44 HANDLE LocalKeyHandle;
45
46 Status = NtCreateKey(KeyHandle,
47 KEY_ALL_ACCESS,
48 ObjectAttributes,
49 0,
50 NULL,
51 CreateOptions,
52 &Disposition);
53 DPRINT("NtCreateKey(%wZ) called (Status %lx)\n", ObjectAttributes->ObjectName, Status);
54 if (Status != STATUS_OBJECT_NAME_NOT_FOUND)
55 {
56 if (!NT_SUCCESS(Status))
57 DPRINT1("CreateNestedKey: NtCreateKey(%wZ) failed (Status %lx)\n", ObjectAttributes->ObjectName, Status);
58
59 return Status;
60 }
61
62 /* Copy object attributes */
63 RtlCopyMemory(&LocalObjectAttributes,
64 ObjectAttributes,
65 sizeof(OBJECT_ATTRIBUTES));
66 RtlCreateUnicodeString(&LocalKeyName,
67 ObjectAttributes->ObjectName->Buffer);
68 LocalObjectAttributes.ObjectName = &LocalKeyName;
69 FullNameLength = LocalKeyName.Length;
70
71 /* Remove the last part of the key name and try to create the key again. */
72 while (Status == STATUS_OBJECT_NAME_NOT_FOUND)
73 {
74 Ptr = wcsrchr(LocalKeyName.Buffer, '\\');
75 if (Ptr == NULL || Ptr == LocalKeyName.Buffer)
76 {
77 Status = STATUS_UNSUCCESSFUL;
78 break;
79 }
80 *Ptr = (WCHAR)0;
81 LocalKeyName.Length = (Ptr - LocalKeyName.Buffer) * sizeof(WCHAR);
82
83 Status = NtCreateKey(&LocalKeyHandle,
84 KEY_CREATE_SUB_KEY,
85 &LocalObjectAttributes,
86 0,
87 NULL,
88 REG_OPTION_NON_VOLATILE, // FIXME ?
89 &Disposition);
90 DPRINT("NtCreateKey(%wZ) called (Status %lx)\n", &LocalKeyName, Status);
91 if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND)
92 DPRINT1("CreateNestedKey: NtCreateKey(%wZ) failed (Status %lx)\n", LocalObjectAttributes.ObjectName, Status);
93 }
94
95 if (!NT_SUCCESS(Status))
96 {
97 RtlFreeUnicodeString(&LocalKeyName);
98 return Status;
99 }
100
101 /* Add removed parts of the key name and create them too. */
102 while (TRUE)
103 {
104 if (LocalKeyName.Length == FullNameLength)
105 {
106 Status = STATUS_SUCCESS;
107 *KeyHandle = LocalKeyHandle;
108 break;
109 }
110 NtClose(LocalKeyHandle);
111
112 LocalKeyName.Buffer[LocalKeyName.Length / sizeof(WCHAR)] = L'\\';
113 LocalKeyName.Length = (USHORT)wcslen(LocalKeyName.Buffer) * sizeof(WCHAR);
114
115 Status = NtCreateKey(&LocalKeyHandle,
116 KEY_ALL_ACCESS,
117 &LocalObjectAttributes,
118 0,
119 NULL,
120 CreateOptions,
121 &Disposition);
122 DPRINT("NtCreateKey(%wZ) called (Status %lx)\n", &LocalKeyName, Status);
123 if (!NT_SUCCESS(Status))
124 {
125 DPRINT1("CreateNestedKey: NtCreateKey(%wZ) failed (Status %lx)\n", LocalObjectAttributes.ObjectName, Status);
126 break;
127 }
128 }
129
130 RtlFreeUnicodeString(&LocalKeyName);
131
132 return Status;
133 }
134
135
136 /*
137 * Should be called under SE_BACKUP_PRIVILEGE privilege
138 */
139 NTSTATUS
CreateRegistryFile(IN PUNICODE_STRING NtSystemRoot,IN PCWSTR RegistryKey,IN BOOLEAN IsHiveNew,IN HANDLE ProtoKeyHandle)140 CreateRegistryFile(
141 IN PUNICODE_STRING NtSystemRoot,
142 IN PCWSTR RegistryKey,
143 IN BOOLEAN IsHiveNew,
144 IN HANDLE ProtoKeyHandle
145 /*
146 IN PUCHAR Descriptor,
147 IN ULONG DescriptorLength
148 */
149 )
150 {
151 /* '.old' is for old valid hives, while '.brk' is for old broken hives */
152 static PCWSTR Extensions[] = {L"old", L"brk"};
153
154 NTSTATUS Status;
155 HANDLE FileHandle;
156 UNICODE_STRING FileName;
157 OBJECT_ATTRIBUTES ObjectAttributes;
158 IO_STATUS_BLOCK IoStatusBlock;
159 PCWSTR Extension;
160 WCHAR PathBuffer[MAX_PATH];
161 WCHAR PathBuffer2[MAX_PATH];
162
163 CombinePaths(PathBuffer, ARRAYSIZE(PathBuffer), 3,
164 NtSystemRoot->Buffer, L"System32\\config", RegistryKey);
165
166 Extension = Extensions[IsHiveNew ? 0 : 1];
167
168 //
169 // FIXME: The best, actually, would be to rename (move) the existing
170 // System32\config\RegistryKey file to System32\config\RegistryKey.old,
171 // and if it already existed some System32\config\RegistryKey.old, we should
172 // first rename this one into System32\config\RegistryKey_N.old before
173 // performing the original rename.
174 //
175
176 /* Check whether the registry hive file already existed, and if so, rename it */
177 if (DoesFileExist(NULL, PathBuffer))
178 {
179 // UINT i;
180
181 DPRINT1("Registry hive '%S' already exists, rename it\n", PathBuffer);
182
183 // i = 1;
184 /* Try first by just appending the '.old' extension */
185 RtlStringCchPrintfW(PathBuffer2, ARRAYSIZE(PathBuffer2),
186 L"%s.%s", PathBuffer, Extension);
187 #if 0
188 while (DoesFileExist(NULL, PathBuffer2))
189 {
190 /* An old file already exists, increments its index, but not too much */
191 if (i <= 0xFFFF)
192 {
193 /* Append '_N.old' extension */
194 RtlStringCchPrintfW(PathBuffer2, ARRAYSIZE(PathBuffer2),
195 L"%s_%lu.%s", PathBuffer, i, Extension);
196 ++i;
197 }
198 else
199 {
200 /*
201 * Too many old files exist, we will rename the file
202 * using the name of the oldest one.
203 */
204 RtlStringCchPrintfW(PathBuffer2, ARRAYSIZE(PathBuffer2),
205 L"%s.%s", PathBuffer, Extension);
206 break;
207 }
208 }
209 #endif
210
211 /* Now rename the file (force the move) */
212 Status = SetupMoveFile(PathBuffer, PathBuffer2, MOVEFILE_REPLACE_EXISTING);
213 }
214
215 /* Create the file */
216 RtlInitUnicodeString(&FileName, PathBuffer);
217 InitializeObjectAttributes(&ObjectAttributes,
218 &FileName,
219 OBJ_CASE_INSENSITIVE,
220 NULL, // Could have been NtSystemRoot, etc...
221 NULL); // Descriptor
222
223 Status = NtCreateFile(&FileHandle,
224 FILE_GENERIC_WRITE,
225 &ObjectAttributes,
226 &IoStatusBlock,
227 NULL,
228 FILE_ATTRIBUTE_NORMAL,
229 0,
230 FILE_OVERWRITE_IF,
231 FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE,
232 NULL,
233 0);
234 if (!NT_SUCCESS(Status))
235 {
236 DPRINT1("NtCreateFile(%wZ) failed, Status 0x%08lx\n", &FileName, Status);
237 return Status;
238 }
239
240 /* Save the selected hive into the file */
241 Status = NtSaveKeyEx(ProtoKeyHandle, FileHandle, REG_LATEST_FORMAT);
242 if (!NT_SUCCESS(Status))
243 {
244 DPRINT1("NtSaveKeyEx(%wZ) failed, Status 0x%08lx\n", &FileName, Status);
245 }
246
247 /* Close the file and return */
248 NtClose(FileHandle);
249 return Status;
250 }
251
252 /* Adapted from ntoskrnl/config/cmsysini.c:CmpLinkKeyToHive() */
253 NTSTATUS
CreateSymLinkKey(IN HANDLE RootKey OPTIONAL,IN PCWSTR LinkKeyName,IN PCWSTR TargetKeyName)254 CreateSymLinkKey(
255 IN HANDLE RootKey OPTIONAL,
256 IN PCWSTR LinkKeyName,
257 IN PCWSTR TargetKeyName)
258 {
259 NTSTATUS Status;
260 OBJECT_ATTRIBUTES ObjectAttributes;
261 UNICODE_STRING KeyName;
262 HANDLE LinkKeyHandle;
263 ULONG Disposition;
264
265 /* Initialize the object attributes */
266 RtlInitUnicodeString(&KeyName, LinkKeyName);
267 InitializeObjectAttributes(&ObjectAttributes,
268 &KeyName,
269 OBJ_CASE_INSENSITIVE,
270 RootKey,
271 NULL);
272
273 /* Create the link key */
274 Status = NtCreateKey(&LinkKeyHandle,
275 KEY_SET_VALUE | KEY_CREATE_LINK,
276 &ObjectAttributes,
277 0,
278 NULL,
279 REG_OPTION_VOLATILE | REG_OPTION_CREATE_LINK,
280 &Disposition);
281 if (!NT_SUCCESS(Status))
282 {
283 DPRINT1("CreateSymLinkKey: couldn't create '%S', Status = 0x%08lx\n",
284 LinkKeyName, Status);
285 return Status;
286 }
287
288 /* Check if the new key was actually created */
289 if (Disposition != REG_CREATED_NEW_KEY)
290 {
291 DPRINT1("CreateSymLinkKey: %S already exists!\n", LinkKeyName);
292 NtClose(LinkKeyHandle);
293 return STATUS_OBJECT_NAME_EXISTS; // STATUS_OBJECT_NAME_COLLISION;
294 }
295
296 /* Set the target key name as link target */
297 RtlInitUnicodeString(&KeyName, TargetKeyName);
298 Status = NtSetValueKey(LinkKeyHandle,
299 &SymbolicLinkValueName,
300 0,
301 REG_LINK,
302 KeyName.Buffer,
303 KeyName.Length);
304
305 /* Close the link key handle */
306 NtClose(LinkKeyHandle);
307
308 if (!NT_SUCCESS(Status))
309 {
310 DPRINT1("CreateSymLinkKey: couldn't create symbolic link '%S' for '%S', Status = 0x%08lx\n",
311 LinkKeyName, TargetKeyName, Status);
312 }
313
314 return Status;
315 }
316
317 NTSTATUS
DeleteSymLinkKey(IN HANDLE RootKey OPTIONAL,IN PCWSTR LinkKeyName)318 DeleteSymLinkKey(
319 IN HANDLE RootKey OPTIONAL,
320 IN PCWSTR LinkKeyName)
321 {
322 NTSTATUS Status;
323 OBJECT_ATTRIBUTES ObjectAttributes;
324 UNICODE_STRING KeyName;
325 HANDLE LinkKeyHandle;
326 // ULONG Disposition;
327
328 /* Initialize the object attributes */
329 RtlInitUnicodeString(&KeyName, LinkKeyName);
330 InitializeObjectAttributes(&ObjectAttributes,
331 &KeyName,
332 /* Open the symlink key itself if it exists, and not its target */
333 OBJ_CASE_INSENSITIVE | OBJ_OPENIF | OBJ_OPENLINK,
334 RootKey,
335 NULL);
336
337 /*
338 * Note: We could use here NtOpenKey() but it does not allow to pass
339 * opening options. NtOpenKeyEx() could do it but is Windows 7+.
340 * So we use the good old NtCreateKey() that can open the key.
341 */
342 #if 0
343 Status = NtCreateKey(&LinkKeyHandle,
344 DELETE | KEY_SET_VALUE | KEY_CREATE_LINK,
345 &ObjectAttributes,
346 0,
347 NULL,
348 /*REG_OPTION_VOLATILE |*/ REG_OPTION_OPEN_LINK,
349 &Disposition);
350 #else
351 Status = NtOpenKey(&LinkKeyHandle,
352 DELETE | KEY_SET_VALUE | KEY_CREATE_LINK,
353 &ObjectAttributes);
354 #endif
355 if (!NT_SUCCESS(Status))
356 {
357 DPRINT1("NtOpenKey(%wZ) failed (Status %lx)\n", &KeyName, Status);
358 return Status;
359 }
360
361 /*
362 * Delete the special "SymbolicLinkValue" value.
363 * This is technically not needed since we are going to remove
364 * the key anyways, but it is good practice to do it.
365 */
366 Status = NtDeleteValueKey(LinkKeyHandle, &SymbolicLinkValueName);
367 if (!NT_SUCCESS(Status))
368 {
369 DPRINT1("NtDeleteValueKey(%wZ) failed (Status %lx)\n", &KeyName, Status);
370 NtClose(LinkKeyHandle);
371 return Status;
372 }
373
374 /* Finally delete the key itself and close the link key handle */
375 Status = NtDeleteKey(LinkKeyHandle);
376 NtClose(LinkKeyHandle);
377
378 if (!NT_SUCCESS(Status))
379 {
380 DPRINT1("DeleteSymLinkKey: couldn't delete symbolic link '%S', Status = 0x%08lx\n",
381 LinkKeyName, Status);
382 }
383
384 return Status;
385 }
386
387 /*
388 * Should be called under SE_RESTORE_PRIVILEGE privilege
389 */
390 NTSTATUS
ConnectRegistry(IN HANDLE RootKey OPTIONAL,IN PCWSTR RegMountPoint,IN PUNICODE_STRING NtSystemRoot,IN PCWSTR RegistryKey)391 ConnectRegistry(
392 IN HANDLE RootKey OPTIONAL,
393 IN PCWSTR RegMountPoint,
394 // IN HANDLE RootDirectory OPTIONAL,
395 IN PUNICODE_STRING NtSystemRoot,
396 IN PCWSTR RegistryKey
397 /*
398 IN PUCHAR Descriptor,
399 IN ULONG DescriptorLength
400 */
401 )
402 {
403 UNICODE_STRING KeyName, FileName;
404 OBJECT_ATTRIBUTES KeyObjectAttributes;
405 OBJECT_ATTRIBUTES FileObjectAttributes;
406 WCHAR PathBuffer[MAX_PATH];
407
408 RtlInitUnicodeString(&KeyName, RegMountPoint);
409 InitializeObjectAttributes(&KeyObjectAttributes,
410 &KeyName,
411 OBJ_CASE_INSENSITIVE,
412 RootKey,
413 NULL); // Descriptor
414
415 CombinePaths(PathBuffer, ARRAYSIZE(PathBuffer), 3,
416 NtSystemRoot->Buffer, L"System32\\config", RegistryKey);
417 RtlInitUnicodeString(&FileName, PathBuffer);
418 InitializeObjectAttributes(&FileObjectAttributes,
419 &FileName,
420 OBJ_CASE_INSENSITIVE,
421 NULL, // RootDirectory,
422 NULL);
423
424 /* Mount the registry hive in the registry namespace */
425 return NtLoadKey(&KeyObjectAttributes, &FileObjectAttributes);
426 }
427
428 /*
429 * Should be called under SE_RESTORE_PRIVILEGE privilege
430 */
431 NTSTATUS
DisconnectRegistry(IN HANDLE RootKey OPTIONAL,IN PCWSTR RegMountPoint,IN ULONG Flags)432 DisconnectRegistry(
433 IN HANDLE RootKey OPTIONAL,
434 IN PCWSTR RegMountPoint,
435 IN ULONG Flags)
436 {
437 UNICODE_STRING KeyName;
438 OBJECT_ATTRIBUTES ObjectAttributes;
439
440 RtlInitUnicodeString(&KeyName, RegMountPoint);
441 InitializeObjectAttributes(&ObjectAttributes,
442 &KeyName,
443 OBJ_CASE_INSENSITIVE,
444 RootKey,
445 NULL);
446
447 // NOTE: NtUnloadKey == NtUnloadKey2 with Flags == 0.
448 return NtUnloadKey2(&ObjectAttributes, Flags);
449 }
450
451 /*
452 * Should be called under SE_RESTORE_PRIVILEGE privilege
453 */
454 NTSTATUS
VerifyRegistryHive(IN PUNICODE_STRING NtSystemRoot,IN PCWSTR RegistryKey)455 VerifyRegistryHive(
456 // IN HANDLE RootKey OPTIONAL,
457 // // IN HANDLE RootDirectory OPTIONAL,
458 IN PUNICODE_STRING NtSystemRoot,
459 IN PCWSTR RegistryKey /* ,
460 IN PCWSTR RegMountPoint */)
461 {
462 NTSTATUS Status;
463
464 /* Try to mount the specified registry hive */
465 Status = ConnectRegistry(NULL,
466 L"\\Registry\\Machine\\USetup_VerifyHive",
467 NtSystemRoot,
468 RegistryKey
469 /* NULL, 0 */);
470 if (!NT_SUCCESS(Status))
471 {
472 DPRINT1("ConnectRegistry(%S) failed, Status 0x%08lx\n", RegistryKey, Status);
473 }
474
475 DPRINT1("VerifyRegistryHive: ConnectRegistry(%S) returns Status 0x%08lx\n", RegistryKey, Status);
476
477 //
478 // TODO: Check the Status error codes: STATUS_SUCCESS, STATUS_REGISTRY_RECOVERED,
479 // STATUS_REGISTRY_HIVE_RECOVERED, STATUS_REGISTRY_CORRUPT, STATUS_REGISTRY_IO_FAILED,
480 // STATUS_NOT_REGISTRY_FILE, STATUS_CANNOT_LOAD_REGISTRY_FILE ;
481 //(STATUS_HIVE_UNLOADED) ; STATUS_SYSTEM_HIVE_TOO_LARGE
482 //
483
484 if (Status == STATUS_REGISTRY_HIVE_RECOVERED) // NT_SUCCESS is still FALSE in this case!
485 DPRINT1("VerifyRegistryHive: Registry hive %S was recovered but some data may be lost (Status 0x%08lx)\n", RegistryKey, Status);
486
487 if (!NT_SUCCESS(Status))
488 {
489 DPRINT1("VerifyRegistryHive: Registry hive %S is corrupted (Status 0x%08lx)\n", RegistryKey, Status);
490 return Status;
491 }
492
493 if (Status == STATUS_REGISTRY_RECOVERED)
494 DPRINT1("VerifyRegistryHive: Registry hive %S succeeded recovered (Status 0x%08lx)\n", RegistryKey, Status);
495
496 /* Unmount the hive */
497 Status = DisconnectRegistry(NULL,
498 L"\\Registry\\Machine\\USetup_VerifyHive",
499 0);
500 if (!NT_SUCCESS(Status))
501 {
502 DPRINT1("DisconnectRegistry(%S) failed, Status 0x%08lx\n", RegistryKey, Status);
503 }
504
505 return Status;
506 }
507
508 /* EOF */
509