xref: /reactos/base/setup/lib/utils/regutil.c (revision cc439606)
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
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 = wcslen(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 = 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
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
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
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
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
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
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