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