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