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