1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS Setup Library 4 * FILE: base/setup/lib/setuplib.c 5 * PURPOSE: Setup Library - Main initialization helpers 6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net) 7 * Hermes Belusca-Maito (hermes.belusca@sfr.fr) 8 */ 9 10 /* INCLUDES *****************************************************************/ 11 12 #include "precomp.h" 13 #include "filesup.h" 14 #include "infsupp.h" 15 #include "inicache.h" 16 17 #include "setuplib.h" 18 19 #define NDEBUG 20 #include <debug.h> 21 22 23 /* GLOBALS ******************************************************************/ 24 25 /* FUNCTIONS ****************************************************************/ 26 27 VOID 28 CheckUnattendedSetup( 29 IN OUT PUSETUP_DATA pSetupData) 30 { 31 INFCONTEXT Context; 32 HINF UnattendInf; 33 UINT ErrorLine; 34 INT IntValue; 35 PCWSTR Value; 36 WCHAR UnattendInfPath[MAX_PATH]; 37 38 CombinePaths(UnattendInfPath, ARRAYSIZE(UnattendInfPath), 2, 39 pSetupData->SourcePath.Buffer, L"unattend.inf"); 40 41 DPRINT("UnattendInf path: '%S'\n", UnattendInfPath); 42 43 if (DoesFileExist(NULL, UnattendInfPath) == FALSE) 44 { 45 DPRINT("Does not exist: %S\n", UnattendInfPath); 46 return; 47 } 48 49 /* Load 'unattend.inf' from installation media */ 50 UnattendInf = SpInfOpenInfFile(UnattendInfPath, 51 NULL, 52 INF_STYLE_OLDNT, 53 pSetupData->LanguageId, 54 &ErrorLine); 55 if (UnattendInf == INVALID_HANDLE_VALUE) 56 { 57 DPRINT("SpInfOpenInfFile() failed\n"); 58 return; 59 } 60 61 /* Open 'Unattend' section */ 62 if (!SpInfFindFirstLine(UnattendInf, L"Unattend", L"Signature", &Context)) 63 { 64 DPRINT("SpInfFindFirstLine() failed for section 'Unattend'\n"); 65 goto Quit; 66 } 67 68 /* Get pointer 'Signature' key */ 69 if (!INF_GetData(&Context, NULL, &Value)) 70 { 71 DPRINT("INF_GetData() failed for key 'Signature'\n"); 72 goto Quit; 73 } 74 75 /* Check 'Signature' string */ 76 if (_wcsicmp(Value, L"$ReactOS$") != 0) 77 { 78 DPRINT("Signature not $ReactOS$\n"); 79 INF_FreeData(Value); 80 goto Quit; 81 } 82 83 INF_FreeData(Value); 84 85 /* Check if Unattend setup is enabled */ 86 if (!SpInfFindFirstLine(UnattendInf, L"Unattend", L"UnattendSetupEnabled", &Context)) 87 { 88 DPRINT("Can't find key 'UnattendSetupEnabled'\n"); 89 goto Quit; 90 } 91 92 if (!INF_GetData(&Context, NULL, &Value)) 93 { 94 DPRINT("Can't read key 'UnattendSetupEnabled'\n"); 95 goto Quit; 96 } 97 98 if (_wcsicmp(Value, L"yes") != 0) 99 { 100 DPRINT("Unattend setup is disabled by 'UnattendSetupEnabled' key!\n"); 101 INF_FreeData(Value); 102 goto Quit; 103 } 104 105 INF_FreeData(Value); 106 107 /* Search for 'DestinationDiskNumber' in the 'Unattend' section */ 108 if (!SpInfFindFirstLine(UnattendInf, L"Unattend", L"DestinationDiskNumber", &Context)) 109 { 110 DPRINT("SpInfFindFirstLine() failed for key 'DestinationDiskNumber'\n"); 111 goto Quit; 112 } 113 114 if (!SpInfGetIntField(&Context, 1, &IntValue)) 115 { 116 DPRINT("SpInfGetIntField() failed for key 'DestinationDiskNumber'\n"); 117 goto Quit; 118 } 119 120 pSetupData->DestinationDiskNumber = (LONG)IntValue; 121 122 /* Search for 'DestinationPartitionNumber' in the 'Unattend' section */ 123 if (!SpInfFindFirstLine(UnattendInf, L"Unattend", L"DestinationPartitionNumber", &Context)) 124 { 125 DPRINT("SpInfFindFirstLine() failed for key 'DestinationPartitionNumber'\n"); 126 goto Quit; 127 } 128 129 if (!SpInfGetIntField(&Context, 1, &IntValue)) 130 { 131 DPRINT("SpInfGetIntField() failed for key 'DestinationPartitionNumber'\n"); 132 goto Quit; 133 } 134 135 pSetupData->DestinationPartitionNumber = (LONG)IntValue; 136 137 /* Search for 'InstallationDirectory' in the 'Unattend' section (optional) */ 138 if (SpInfFindFirstLine(UnattendInf, L"Unattend", L"InstallationDirectory", &Context)) 139 { 140 /* Get pointer 'InstallationDirectory' key */ 141 if (!INF_GetData(&Context, NULL, &Value)) 142 { 143 DPRINT("INF_GetData() failed for key 'InstallationDirectory'\n"); 144 goto Quit; 145 } 146 147 RtlStringCchCopyW(pSetupData->InstallationDirectory, 148 ARRAYSIZE(pSetupData->InstallationDirectory), 149 Value); 150 151 INF_FreeData(Value); 152 } 153 154 IsUnattendedSetup = TRUE; 155 DPRINT("Running unattended setup\n"); 156 157 /* Search for 'MBRInstallType' in the 'Unattend' section */ 158 pSetupData->MBRInstallType = -1; 159 if (SpInfFindFirstLine(UnattendInf, L"Unattend", L"MBRInstallType", &Context)) 160 { 161 if (SpInfGetIntField(&Context, 1, &IntValue)) 162 { 163 pSetupData->MBRInstallType = IntValue; 164 } 165 } 166 167 /* Search for 'FormatPartition' in the 'Unattend' section */ 168 pSetupData->FormatPartition = 0; 169 if (SpInfFindFirstLine(UnattendInf, L"Unattend", L"FormatPartition", &Context)) 170 { 171 if (SpInfGetIntField(&Context, 1, &IntValue)) 172 { 173 pSetupData->FormatPartition = IntValue; 174 } 175 } 176 177 pSetupData->AutoPartition = 0; 178 if (SpInfFindFirstLine(UnattendInf, L"Unattend", L"AutoPartition", &Context)) 179 { 180 if (SpInfGetIntField(&Context, 1, &IntValue)) 181 { 182 pSetupData->AutoPartition = IntValue; 183 } 184 } 185 186 /* Search for LocaleID in the 'Unattend' section */ 187 if (SpInfFindFirstLine(UnattendInf, L"Unattend", L"LocaleID", &Context)) 188 { 189 if (INF_GetData(&Context, NULL, &Value)) 190 { 191 LONG Id = wcstol(Value, NULL, 16); 192 RtlStringCchPrintfW(pSetupData->LocaleID, 193 ARRAYSIZE(pSetupData->LocaleID), 194 L"%08lx", Id); 195 INF_FreeData(Value); 196 } 197 } 198 199 /* Search for FsType in the 'Unattend' section */ 200 pSetupData->FsType = 0; 201 if (SpInfFindFirstLine(UnattendInf, L"Unattend", L"FsType", &Context)) 202 { 203 if (SpInfGetIntField(&Context, 1, &IntValue)) 204 { 205 pSetupData->FsType = IntValue; 206 } 207 } 208 209 Quit: 210 SpInfCloseInfFile(UnattendInf); 211 } 212 213 VOID 214 InstallSetupInfFile( 215 IN OUT PUSETUP_DATA pSetupData) 216 { 217 NTSTATUS Status; 218 PINICACHE IniCache; 219 220 #if 0 // HACK FIXME! 221 PINICACHE UnattendCache; 222 PINICACHEITERATOR Iterator; 223 #else 224 // WCHAR CrLf[] = {L'\r', L'\n'}; 225 CHAR CrLf[] = {'\r', '\n'}; 226 HANDLE FileHandle, UnattendFileHandle, SectionHandle; 227 FILE_STANDARD_INFORMATION FileInfo; 228 ULONG FileSize; 229 PVOID ViewBase; 230 UNICODE_STRING FileName; 231 OBJECT_ATTRIBUTES ObjectAttributes; 232 IO_STATUS_BLOCK IoStatusBlock; 233 #endif 234 235 PINI_SECTION IniSection; 236 WCHAR PathBuffer[MAX_PATH]; 237 WCHAR UnattendInfPath[MAX_PATH]; 238 239 /* Create a $winnt$.inf file with default entries */ 240 IniCache = IniCacheCreate(); 241 if (!IniCache) 242 return; 243 244 IniSection = IniAddSection(IniCache, L"SetupParams"); 245 if (IniSection) 246 { 247 /* Key "skipmissingfiles" */ 248 // RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer), 249 // L"\"%s\"", L"WinNt5.2"); 250 // IniAddKey(IniSection, L"Version", PathBuffer); 251 } 252 253 IniSection = IniAddSection(IniCache, L"Data"); 254 if (IniSection) 255 { 256 RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer), 257 L"\"%s\"", IsUnattendedSetup ? L"yes" : L"no"); 258 IniAddKey(IniSection, L"UnattendedInstall", PathBuffer); 259 260 // "floppylessbootpath" (yes/no) 261 262 RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer), 263 L"\"%s\"", L"winnt"); 264 IniAddKey(IniSection, L"ProductType", PathBuffer); 265 266 RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer), 267 L"\"%s\\\"", pSetupData->SourceRootPath.Buffer); 268 IniAddKey(IniSection, L"SourcePath", PathBuffer); 269 270 // "floppyless" ("0") 271 } 272 273 #if 0 274 275 /* TODO: Append the standard unattend.inf file */ 276 CombinePaths(UnattendInfPath, ARRAYSIZE(UnattendInfPath), 2, 277 pSetupData->SourcePath.Buffer, L"unattend.inf"); 278 if (DoesFileExist(NULL, UnattendInfPath) == FALSE) 279 { 280 DPRINT("Does not exist: %S\n", UnattendInfPath); 281 goto Quit; 282 } 283 284 Status = IniCacheLoad(&UnattendCache, UnattendInfPath, FALSE); 285 if (!NT_SUCCESS(Status)) 286 { 287 DPRINT1("Cannot load %S as an INI file!\n", UnattendInfPath); 288 goto Quit; 289 } 290 291 IniCacheDestroy(UnattendCache); 292 293 Quit: 294 CombinePaths(PathBuffer, ARRAYSIZE(PathBuffer), 2, 295 pSetupData->DestinationPath.Buffer, L"System32\\$winnt$.inf"); 296 IniCacheSave(IniCache, PathBuffer); 297 IniCacheDestroy(IniCache); 298 299 #else 300 301 CombinePaths(PathBuffer, ARRAYSIZE(PathBuffer), 2, 302 pSetupData->DestinationPath.Buffer, L"System32\\$winnt$.inf"); 303 IniCacheSave(IniCache, PathBuffer); 304 IniCacheDestroy(IniCache); 305 306 /* TODO: Append the standard unattend.inf file */ 307 CombinePaths(UnattendInfPath, ARRAYSIZE(UnattendInfPath), 2, 308 pSetupData->SourcePath.Buffer, L"unattend.inf"); 309 if (DoesFileExist(NULL, UnattendInfPath) == FALSE) 310 { 311 DPRINT("Does not exist: %S\n", UnattendInfPath); 312 return; 313 } 314 315 RtlInitUnicodeString(&FileName, PathBuffer); 316 InitializeObjectAttributes(&ObjectAttributes, 317 &FileName, 318 OBJ_CASE_INSENSITIVE | OBJ_OPENIF, 319 NULL, 320 NULL); 321 Status = NtOpenFile(&FileHandle, 322 FILE_APPEND_DATA | SYNCHRONIZE, 323 &ObjectAttributes, 324 &IoStatusBlock, 325 FILE_SHARE_READ, 326 FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE); 327 if (!NT_SUCCESS(Status)) 328 { 329 DPRINT1("Cannot load %S as an INI file!\n", PathBuffer); 330 return; 331 } 332 333 /* Query the file size */ 334 Status = NtQueryInformationFile(FileHandle, 335 &IoStatusBlock, 336 &FileInfo, 337 sizeof(FileInfo), 338 FileStandardInformation); 339 if (!NT_SUCCESS(Status)) 340 { 341 DPRINT("NtQueryInformationFile() failed (Status %lx)\n", Status); 342 FileInfo.EndOfFile.QuadPart = 0ULL; 343 } 344 345 Status = OpenAndMapFile(NULL, 346 UnattendInfPath, 347 &UnattendFileHandle, 348 &FileSize, 349 &SectionHandle, 350 &ViewBase, 351 FALSE); 352 if (!NT_SUCCESS(Status)) 353 { 354 DPRINT1("Cannot load %S !\n", UnattendInfPath); 355 NtClose(FileHandle); 356 return; 357 } 358 359 /* Write to the INI file */ 360 361 /* "\r\n" */ 362 Status = NtWriteFile(FileHandle, 363 NULL, 364 NULL, 365 NULL, 366 &IoStatusBlock, 367 (PVOID)CrLf, 368 sizeof(CrLf), 369 &FileInfo.EndOfFile, 370 NULL); 371 372 Status = NtWriteFile(FileHandle, 373 NULL, 374 NULL, 375 NULL, 376 &IoStatusBlock, 377 ViewBase, 378 FileSize, 379 NULL, 380 NULL); 381 if (!NT_SUCCESS(Status)) 382 { 383 DPRINT("NtWriteFile() failed (Status %lx)\n", Status); 384 } 385 386 /* Finally, unmap and close the file */ 387 UnMapAndCloseFile(UnattendFileHandle, SectionHandle, ViewBase); 388 389 NtClose(FileHandle); 390 #endif 391 } 392 393 NTSTATUS 394 GetSourcePaths( 395 OUT PUNICODE_STRING SourcePath, 396 OUT PUNICODE_STRING SourceRootPath, 397 OUT PUNICODE_STRING SourceRootDir) 398 { 399 NTSTATUS Status; 400 HANDLE LinkHandle; 401 OBJECT_ATTRIBUTES ObjectAttributes; 402 UCHAR ImageFileBuffer[sizeof(UNICODE_STRING) + MAX_PATH * sizeof(WCHAR)]; 403 PUNICODE_STRING InstallSourcePath = (PUNICODE_STRING)&ImageFileBuffer; 404 WCHAR SystemRootBuffer[MAX_PATH] = L""; 405 UNICODE_STRING SystemRootPath = RTL_CONSTANT_STRING(L"\\SystemRoot"); 406 ULONG BufferSize; 407 PWCHAR Ptr; 408 409 // FIXME: commented out to allow installation from USB 410 #if 0 411 /* Determine the installation source path via the full path of the installer */ 412 RtlInitEmptyUnicodeString(InstallSourcePath, 413 (PWSTR)((ULONG_PTR)ImageFileBuffer + sizeof(UNICODE_STRING)), 414 sizeof(ImageFileBuffer) - sizeof(UNICODE_STRING) 415 /* Reserve space for a NULL terminator */ - sizeof(UNICODE_NULL)); 416 BufferSize = sizeof(ImageFileBuffer); 417 Status = NtQueryInformationProcess(NtCurrentProcess(), 418 ProcessImageFileName, 419 InstallSourcePath, 420 BufferSize, 421 NULL); 422 // STATUS_INFO_LENGTH_MISMATCH or STATUS_BUFFER_TOO_SMALL ? 423 if (!NT_SUCCESS(Status)) 424 return Status; 425 426 /* Manually NULL-terminate */ 427 InstallSourcePath->Buffer[InstallSourcePath->Length / sizeof(WCHAR)] = UNICODE_NULL; 428 429 /* Strip the trailing file name */ 430 Ptr = wcsrchr(InstallSourcePath->Buffer, OBJ_NAME_PATH_SEPARATOR); 431 if (Ptr) 432 *Ptr = UNICODE_NULL; 433 InstallSourcePath->Length = wcslen(InstallSourcePath->Buffer) * sizeof(WCHAR); 434 #endif 435 436 /* 437 * Now resolve the full path to \SystemRoot. In case it prefixes 438 * the installation source path determined from the full path of 439 * the installer, we use instead the resolved \SystemRoot as the 440 * installation source path. 441 * Otherwise, we use instead the path from the full installer path. 442 */ 443 444 InitializeObjectAttributes(&ObjectAttributes, 445 &SystemRootPath, 446 OBJ_CASE_INSENSITIVE, 447 NULL, 448 NULL); 449 450 Status = NtOpenSymbolicLinkObject(&LinkHandle, 451 SYMBOLIC_LINK_QUERY, 452 &ObjectAttributes); 453 if (!NT_SUCCESS(Status)) 454 { 455 /* 456 * We failed at opening the \SystemRoot link (usually due to wrong 457 * access rights). Do not consider this as a fatal error, but use 458 * instead the image file path as the installation source path. 459 */ 460 DPRINT1("NtOpenSymbolicLinkObject(%wZ) failed with Status 0x%08lx\n", 461 &SystemRootPath, Status); 462 goto InitPaths; 463 } 464 465 RtlInitEmptyUnicodeString(&SystemRootPath, 466 SystemRootBuffer, 467 sizeof(SystemRootBuffer)); 468 469 /* Resolve the link and close its handle */ 470 Status = NtQuerySymbolicLinkObject(LinkHandle, 471 &SystemRootPath, 472 &BufferSize); 473 NtClose(LinkHandle); 474 475 if (!NT_SUCCESS(Status)) 476 return Status; // Unexpected error 477 478 /* Check whether the resolved \SystemRoot is a prefix of the image file path */ 479 // FIXME: commented out to allow installation from USB 480 // if (RtlPrefixUnicodeString(&SystemRootPath, InstallSourcePath, TRUE)) 481 { 482 /* Yes it is, so we use instead SystemRoot as the installation source path */ 483 InstallSourcePath = &SystemRootPath; 484 } 485 486 487 InitPaths: 488 /* 489 * Retrieve the different source path components 490 */ 491 RtlCreateUnicodeString(SourcePath, InstallSourcePath->Buffer); 492 493 /* Strip trailing directory */ 494 Ptr = wcsrchr(InstallSourcePath->Buffer, OBJ_NAME_PATH_SEPARATOR); 495 if (Ptr) 496 { 497 RtlCreateUnicodeString(SourceRootDir, Ptr); 498 *Ptr = UNICODE_NULL; 499 } 500 else 501 { 502 RtlCreateUnicodeString(SourceRootDir, L""); 503 } 504 505 RtlCreateUnicodeString(SourceRootPath, InstallSourcePath->Buffer); 506 507 return STATUS_SUCCESS; 508 } 509 510 ERROR_NUMBER 511 LoadSetupInf( 512 IN OUT PUSETUP_DATA pSetupData) 513 { 514 INFCONTEXT Context; 515 UINT ErrorLine; 516 INT IntValue; 517 PCWSTR Value; 518 WCHAR FileNameBuffer[MAX_PATH]; 519 520 CombinePaths(FileNameBuffer, ARRAYSIZE(FileNameBuffer), 2, 521 pSetupData->SourcePath.Buffer, L"txtsetup.sif"); 522 523 DPRINT("SetupInf path: '%S'\n", FileNameBuffer); 524 525 pSetupData->SetupInf = 526 SpInfOpenInfFile(FileNameBuffer, 527 NULL, 528 INF_STYLE_WIN4, 529 pSetupData->LanguageId, 530 &ErrorLine); 531 if (pSetupData->SetupInf == INVALID_HANDLE_VALUE) 532 return ERROR_LOAD_TXTSETUPSIF; 533 534 /* Open 'Version' section */ 535 if (!SpInfFindFirstLine(pSetupData->SetupInf, L"Version", L"Signature", &Context)) 536 return ERROR_CORRUPT_TXTSETUPSIF; 537 538 /* Get pointer 'Signature' key */ 539 if (!INF_GetData(&Context, NULL, &Value)) 540 return ERROR_CORRUPT_TXTSETUPSIF; 541 542 /* Check 'Signature' string */ 543 if (_wcsicmp(Value, L"$ReactOS$") != 0 && 544 _wcsicmp(Value, L"$Windows NT$") != 0) 545 { 546 INF_FreeData(Value); 547 return ERROR_SIGNATURE_TXTSETUPSIF; 548 } 549 550 INF_FreeData(Value); 551 552 /* Open 'DiskSpaceRequirements' section */ 553 if (!SpInfFindFirstLine(pSetupData->SetupInf, L"DiskSpaceRequirements", L"FreeSysPartDiskSpace", &Context)) 554 return ERROR_CORRUPT_TXTSETUPSIF; 555 556 pSetupData->RequiredPartitionDiskSpace = ~0; 557 558 /* Get the 'FreeSysPartDiskSpace' value */ 559 if (!SpInfGetIntField(&Context, 1, &IntValue)) 560 return ERROR_CORRUPT_TXTSETUPSIF; 561 562 pSetupData->RequiredPartitionDiskSpace = (ULONG)IntValue; 563 564 // 565 // Support "SetupSourceDevice" and "SetupSourcePath" in txtsetup.sif 566 // See CORE-9023 567 // Support for that should also be added in setupldr. 568 // 569 570 /* Update the Setup Source paths */ 571 if (SpInfFindFirstLine(pSetupData->SetupInf, L"SetupData", L"SetupSourceDevice", &Context)) 572 { 573 /* 574 * Get optional pointer 'SetupSourceDevice' key, its presence 575 * will dictate whether we also need 'SetupSourcePath'. 576 */ 577 if (INF_GetData(&Context, NULL, &Value)) 578 { 579 /* Free the old source root path string and create the new one */ 580 RtlFreeUnicodeString(&pSetupData->SourceRootPath); 581 RtlCreateUnicodeString(&pSetupData->SourceRootPath, Value); 582 INF_FreeData(Value); 583 584 if (!SpInfFindFirstLine(pSetupData->SetupInf, L"SetupData", L"SetupSourcePath", &Context)) 585 { 586 /* The 'SetupSourcePath' value is mandatory! */ 587 return ERROR_CORRUPT_TXTSETUPSIF; 588 } 589 590 /* Get pointer 'SetupSourcePath' key */ 591 if (!INF_GetData(&Context, NULL, &Value)) 592 { 593 /* The 'SetupSourcePath' value is mandatory! */ 594 return ERROR_CORRUPT_TXTSETUPSIF; 595 } 596 597 /* Free the old source path string and create the new one */ 598 RtlFreeUnicodeString(&pSetupData->SourceRootDir); 599 RtlCreateUnicodeString(&pSetupData->SourceRootDir, Value); 600 INF_FreeData(Value); 601 } 602 } 603 604 /* Search for 'DefaultPath' in the 'SetupData' section */ 605 pSetupData->InstallationDirectory[0] = 0; 606 if (SpInfFindFirstLine(pSetupData->SetupInf, L"SetupData", L"DefaultPath", &Context)) 607 { 608 /* Get pointer 'DefaultPath' key */ 609 if (!INF_GetData(&Context, NULL, &Value)) 610 return ERROR_CORRUPT_TXTSETUPSIF; 611 612 RtlStringCchCopyW(pSetupData->InstallationDirectory, 613 ARRAYSIZE(pSetupData->InstallationDirectory), 614 Value); 615 616 INF_FreeData(Value); 617 } 618 619 return ERROR_SUCCESS; 620 } 621 622 /** 623 * @brief Find or set the active system partition. 624 **/ 625 BOOLEAN 626 InitSystemPartition( 627 /**/_In_ PPARTLIST PartitionList, /* HACK HACK! */ 628 /**/_In_ PPARTENTRY InstallPartition, /* HACK HACK! */ 629 /**/_Out_ PPARTENTRY* pSystemPartition, /* HACK HACK! */ 630 _In_opt_ PFSVOL_CALLBACK FsVolCallback, 631 _In_opt_ PVOID Context) 632 { 633 FSVOL_OP Result; 634 PPARTENTRY SystemPartition; 635 PPARTENTRY OldActivePart; 636 637 /* 638 * If we install on a fixed disk, try to find a supported system 639 * partition on the system. Otherwise if we install on a removable disk 640 * use the install partition as the system partition. 641 */ 642 if (InstallPartition->DiskEntry->MediaType == FixedMedia) 643 { 644 SystemPartition = FindSupportedSystemPartition(PartitionList, 645 FALSE, 646 InstallPartition->DiskEntry, 647 InstallPartition); 648 /* Use the original system partition as the old active partition hint */ 649 OldActivePart = PartitionList->SystemPartition; 650 651 if ( SystemPartition && PartitionList->SystemPartition && 652 (SystemPartition != PartitionList->SystemPartition) ) 653 { 654 DPRINT1("We are using a different system partition!!\n"); 655 656 Result = FsVolCallback(Context, 657 ChangeSystemPartition, 658 (ULONG_PTR)SystemPartition, 659 0); 660 if (Result != FSVOL_DOIT) 661 return FALSE; 662 } 663 } 664 else // if (InstallPartition->DiskEntry->MediaType == RemovableMedia) 665 { 666 SystemPartition = InstallPartition; 667 /* Don't specify any old active partition hint */ 668 OldActivePart = NULL; 669 } 670 671 if (!SystemPartition) 672 { 673 FsVolCallback(Context, 674 FSVOLNOTIFY_PARTITIONERROR, 675 ERROR_SYSTEM_PARTITION_NOT_FOUND, 676 0); 677 return FALSE; 678 } 679 680 *pSystemPartition = SystemPartition; 681 682 /* 683 * If the system partition can be created in some 684 * non-partitioned space, create it now. 685 */ 686 if (!SystemPartition->IsPartitioned) 687 { 688 /* Automatically create the partition; it will be 689 * formatted later with default parameters */ 690 // FIXME: Don't use the whole empty space, but a minimal size 691 // specified from the TXTSETUP.SIF or unattended setup. 692 CreatePartition(PartitionList, 693 SystemPartition, 694 0ULL, 695 0); 696 ASSERT(SystemPartition->IsPartitioned); 697 } 698 699 /* Set it as such */ 700 if (!SetActivePartition(PartitionList, SystemPartition, OldActivePart)) 701 { 702 DPRINT1("SetActivePartition(0x%p) failed?!\n", SystemPartition); 703 ASSERT(FALSE); 704 } 705 706 /* 707 * In all cases, whether or not we are going to perform a formatting, 708 * we must perform a filesystem check of the system partition. 709 */ 710 SystemPartition->NeedsCheck = TRUE; 711 712 return TRUE; 713 } 714 715 NTSTATUS 716 InitDestinationPaths( 717 IN OUT PUSETUP_DATA pSetupData, 718 IN PCWSTR InstallationDir, 719 IN PPARTENTRY PartEntry) // FIXME: HACK! 720 { 721 NTSTATUS Status; 722 PDISKENTRY DiskEntry = PartEntry->DiskEntry; 723 WCHAR PathBuffer[MAX_PATH]; 724 725 ASSERT(PartEntry->IsPartitioned && PartEntry->PartitionNumber != 0); 726 727 /* Create 'pSetupData->DestinationRootPath' string */ 728 RtlFreeUnicodeString(&pSetupData->DestinationRootPath); 729 Status = RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer), 730 L"\\Device\\Harddisk%lu\\Partition%lu\\", 731 DiskEntry->DiskNumber, 732 PartEntry->PartitionNumber); 733 734 if (!NT_SUCCESS(Status)) 735 { 736 DPRINT1("RtlStringCchPrintfW() failed with status 0x%08lx\n", Status); 737 return Status; 738 } 739 740 Status = RtlCreateUnicodeString(&pSetupData->DestinationRootPath, PathBuffer) ? STATUS_SUCCESS : STATUS_NO_MEMORY; 741 742 if (!NT_SUCCESS(Status)) 743 { 744 DPRINT1("RtlCreateUnicodeString() failed with status 0x%08lx\n", Status); 745 return Status; 746 } 747 748 DPRINT("DestinationRootPath: %wZ\n", &pSetupData->DestinationRootPath); 749 750 // FIXME! Which variable to choose? 751 if (!InstallationDir) 752 InstallationDir = pSetupData->InstallationDirectory; 753 754 /** Equivalent of 'NTOS_INSTALLATION::SystemArcPath' **/ 755 /* Create 'pSetupData->DestinationArcPath' */ 756 RtlFreeUnicodeString(&pSetupData->DestinationArcPath); 757 758 if (DiskEntry->MediaType == FixedMedia) 759 { 760 if (DiskEntry->BiosFound) 761 { 762 #if 1 763 Status = RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer), 764 L"multi(0)disk(0)rdisk(%lu)partition(%lu)\\", 765 DiskEntry->HwFixedDiskNumber, 766 PartEntry->OnDiskPartitionNumber); 767 #else 768 Status = RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer), 769 L"multi(%lu)disk(%lu)rdisk(%lu)partition(%lu)\\", 770 DiskEntry->HwAdapterNumber, 771 DiskEntry->HwControllerNumber, 772 DiskEntry->HwFixedDiskNumber, 773 PartEntry->OnDiskPartitionNumber); 774 #endif 775 DPRINT1("Fixed disk found by BIOS, using MULTI ARC path '%S'\n", PathBuffer); 776 } 777 else 778 { 779 Status = RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer), 780 L"scsi(%u)disk(%u)rdisk(%u)partition(%lu)\\", 781 DiskEntry->Port, 782 DiskEntry->Bus, 783 DiskEntry->Id, 784 PartEntry->OnDiskPartitionNumber); 785 DPRINT1("Fixed disk not found by BIOS, using SCSI ARC path '%S'\n", PathBuffer); 786 } 787 } 788 else // if (DiskEntry->MediaType == RemovableMedia) 789 { 790 #if 1 791 Status = RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer), 792 L"multi(0)disk(0)rdisk(%lu)partition(%lu)\\", 793 0, 1); 794 DPRINT1("Removable disk, using MULTI ARC path '%S'\n", PathBuffer); 795 #else 796 Status = RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer), 797 L"signature(%08x)disk(%u)rdisk(%u)partition(%lu)\\", 798 DiskEntry->LayoutBuffer->Signature, 799 DiskEntry->Bus, 800 DiskEntry->Id, 801 PartEntry->OnDiskPartitionNumber); 802 DPRINT1("Removable disk, using SIGNATURE ARC path '%S'\n", PathBuffer); 803 #endif 804 } 805 806 if (!NT_SUCCESS(Status)) 807 { 808 DPRINT1("RtlStringCchPrintfW() failed with status 0x%08lx\n", Status); 809 RtlFreeUnicodeString(&pSetupData->DestinationRootPath); 810 return Status; 811 } 812 813 Status = ConcatPaths(PathBuffer, ARRAYSIZE(PathBuffer), 1, InstallationDir); 814 815 if (!NT_SUCCESS(Status)) 816 { 817 DPRINT1("ConcatPaths() failed with status 0x%08lx\n", Status); 818 RtlFreeUnicodeString(&pSetupData->DestinationRootPath); 819 return Status; 820 } 821 822 Status = RtlCreateUnicodeString(&pSetupData->DestinationArcPath, PathBuffer) ? STATUS_SUCCESS : STATUS_NO_MEMORY; 823 824 if (!NT_SUCCESS(Status)) 825 { 826 DPRINT1("RtlCreateUnicodeString() failed with status 0x%08lx\n", Status); 827 RtlFreeUnicodeString(&pSetupData->DestinationRootPath); 828 return Status; 829 } 830 831 /** Equivalent of 'NTOS_INSTALLATION::SystemNtPath' **/ 832 /* Create 'pSetupData->DestinationPath' string */ 833 RtlFreeUnicodeString(&pSetupData->DestinationPath); 834 Status = CombinePaths(PathBuffer, ARRAYSIZE(PathBuffer), 2, 835 pSetupData->DestinationRootPath.Buffer, InstallationDir); 836 837 if (!NT_SUCCESS(Status)) 838 { 839 DPRINT1("CombinePaths() failed with status 0x%08lx\n", Status); 840 RtlFreeUnicodeString(&pSetupData->DestinationArcPath); 841 RtlFreeUnicodeString(&pSetupData->DestinationRootPath); 842 return Status; 843 } 844 845 Status = RtlCreateUnicodeString(&pSetupData->DestinationPath, PathBuffer) ? STATUS_SUCCESS : STATUS_NO_MEMORY; 846 847 if (!NT_SUCCESS(Status)) 848 { 849 DPRINT1("RtlCreateUnicodeString() failed with status 0x%08lx\n", Status); 850 RtlFreeUnicodeString(&pSetupData->DestinationArcPath); 851 RtlFreeUnicodeString(&pSetupData->DestinationRootPath); 852 return Status; 853 } 854 855 /** Equivalent of 'NTOS_INSTALLATION::PathComponent' **/ 856 // FIXME: This is only temporary!! Must be removed later! 857 Status = RtlCreateUnicodeString(&pSetupData->InstallPath, InstallationDir) ? STATUS_SUCCESS : STATUS_NO_MEMORY; 858 859 if (!NT_SUCCESS(Status)) 860 { 861 DPRINT1("RtlCreateUnicodeString() failed with status 0x%08lx\n", Status); 862 RtlFreeUnicodeString(&pSetupData->DestinationPath); 863 RtlFreeUnicodeString(&pSetupData->DestinationArcPath); 864 RtlFreeUnicodeString(&pSetupData->DestinationRootPath); 865 return Status; 866 } 867 868 return STATUS_SUCCESS; 869 } 870 871 // NTSTATUS 872 ERROR_NUMBER 873 InitializeSetup( 874 IN OUT PUSETUP_DATA pSetupData, 875 IN ULONG InitPhase) 876 { 877 if (InitPhase == 0) 878 { 879 RtlZeroMemory(pSetupData, sizeof(*pSetupData)); 880 881 /* Initialize error handling */ 882 pSetupData->LastErrorNumber = ERROR_SUCCESS; 883 pSetupData->ErrorRoutine = NULL; 884 885 /* Initialize global unicode strings */ 886 RtlInitUnicodeString(&pSetupData->SourcePath, NULL); 887 RtlInitUnicodeString(&pSetupData->SourceRootPath, NULL); 888 RtlInitUnicodeString(&pSetupData->SourceRootDir, NULL); 889 RtlInitUnicodeString(&pSetupData->DestinationArcPath, NULL); 890 RtlInitUnicodeString(&pSetupData->DestinationPath, NULL); 891 RtlInitUnicodeString(&pSetupData->DestinationRootPath, NULL); 892 RtlInitUnicodeString(&pSetupData->SystemRootPath, NULL); 893 894 // FIXME: This is only temporary!! Must be removed later! 895 /***/RtlInitUnicodeString(&pSetupData->InstallPath, NULL);/***/ 896 897 // 898 // TODO: Load and start SetupDD, and ask it for the information 899 // 900 901 return ERROR_SUCCESS; 902 } 903 else 904 if (InitPhase == 1) 905 { 906 ERROR_NUMBER Error; 907 NTSTATUS Status; 908 909 /* Get the source path and source root path */ 910 // 911 // NOTE: Sometimes the source path may not be in SystemRoot !! 912 // (and this is the case when using the 1st-stage GUI setup!) 913 // 914 Status = GetSourcePaths(&pSetupData->SourcePath, 915 &pSetupData->SourceRootPath, 916 &pSetupData->SourceRootDir); 917 if (!NT_SUCCESS(Status)) 918 { 919 DPRINT1("GetSourcePaths() failed (Status 0x%08lx)\n", Status); 920 return ERROR_NO_SOURCE_DRIVE; 921 } 922 /* 923 * Example of output: 924 * SourcePath: '\Device\CdRom0\I386' 925 * SourceRootPath: '\Device\CdRom0' 926 * SourceRootDir: '\I386' 927 */ 928 DPRINT1("SourcePath (1): '%wZ'\n", &pSetupData->SourcePath); 929 DPRINT1("SourceRootPath (1): '%wZ'\n", &pSetupData->SourceRootPath); 930 DPRINT1("SourceRootDir (1): '%wZ'\n", &pSetupData->SourceRootDir); 931 932 /* Load 'txtsetup.sif' from the installation media */ 933 Error = LoadSetupInf(pSetupData); 934 if (Error != ERROR_SUCCESS) 935 { 936 DPRINT1("LoadSetupInf() failed (Error 0x%lx)\n", Error); 937 return Error; 938 } 939 DPRINT1("SourcePath (2): '%wZ'\n", &pSetupData->SourcePath); 940 DPRINT1("SourceRootPath (2): '%wZ'\n", &pSetupData->SourceRootPath); 941 DPRINT1("SourceRootDir (2): '%wZ'\n", &pSetupData->SourceRootDir); 942 943 return ERROR_SUCCESS; 944 } 945 946 return ERROR_SUCCESS; 947 } 948 949 VOID 950 FinishSetup( 951 IN OUT PUSETUP_DATA pSetupData) 952 { 953 /* Destroy the computer settings list */ 954 if (pSetupData->ComputerList != NULL) 955 { 956 DestroyGenericList(pSetupData->ComputerList, TRUE); 957 pSetupData->ComputerList = NULL; 958 } 959 960 /* Destroy the display settings list */ 961 if (pSetupData->DisplayList != NULL) 962 { 963 DestroyGenericList(pSetupData->DisplayList, TRUE); 964 pSetupData->DisplayList = NULL; 965 } 966 967 /* Destroy the keyboard settings list */ 968 if (pSetupData->KeyboardList != NULL) 969 { 970 DestroyGenericList(pSetupData->KeyboardList, TRUE); 971 pSetupData->KeyboardList = NULL; 972 } 973 974 /* Destroy the keyboard layout list */ 975 if (pSetupData->LayoutList != NULL) 976 { 977 DestroyGenericList(pSetupData->LayoutList, TRUE); 978 pSetupData->LayoutList = NULL; 979 } 980 981 /* Destroy the languages list */ 982 if (pSetupData->LanguageList != NULL) 983 { 984 DestroyGenericList(pSetupData->LanguageList, FALSE); 985 pSetupData->LanguageList = NULL; 986 } 987 988 /* Close the Setup INF */ 989 SpInfCloseInfFile(pSetupData->SetupInf); 990 } 991 992 /* 993 * SIDEEFFECTS 994 * Calls RegInitializeRegistry 995 * Calls ImportRegistryFile 996 * Calls SetDefaultPagefile 997 * Calls SetMountedDeviceValues 998 */ 999 ERROR_NUMBER 1000 UpdateRegistry( 1001 IN OUT PUSETUP_DATA pSetupData, 1002 /**/IN BOOLEAN RepairUpdateFlag, /* HACK HACK! */ 1003 /**/IN PPARTLIST PartitionList, /* HACK HACK! */ 1004 /**/IN WCHAR DestinationDriveLetter, /* HACK HACK! */ 1005 /**/IN PCWSTR SelectedLanguageId, /* HACK HACK! */ 1006 IN PREGISTRY_STATUS_ROUTINE StatusRoutine OPTIONAL, 1007 IN PFONTSUBSTSETTINGS SubstSettings OPTIONAL) 1008 { 1009 ERROR_NUMBER ErrorNumber; 1010 NTSTATUS Status; 1011 INFCONTEXT InfContext; 1012 PCWSTR Action; 1013 PCWSTR File; 1014 PCWSTR Section; 1015 BOOLEAN Success; 1016 BOOLEAN ShouldRepairRegistry = FALSE; 1017 BOOLEAN Delete; 1018 1019 if (RepairUpdateFlag) 1020 { 1021 DPRINT1("TODO: Updating / repairing the registry is not completely implemented yet!\n"); 1022 1023 /* Verify the registry hives and check whether we need to update or repair any of them */ 1024 Status = VerifyRegistryHives(&pSetupData->DestinationPath, &ShouldRepairRegistry); 1025 if (!NT_SUCCESS(Status)) 1026 { 1027 DPRINT1("VerifyRegistryHives failed, Status 0x%08lx\n", Status); 1028 ShouldRepairRegistry = FALSE; 1029 } 1030 if (!ShouldRepairRegistry) 1031 DPRINT1("No need to repair the registry\n"); 1032 } 1033 1034 DoUpdate: 1035 ErrorNumber = ERROR_SUCCESS; 1036 1037 /* Update the registry */ 1038 if (StatusRoutine) StatusRoutine(RegHiveUpdate); 1039 1040 /* Initialize the registry and setup the registry hives */ 1041 Status = RegInitializeRegistry(&pSetupData->DestinationPath); 1042 if (!NT_SUCCESS(Status)) 1043 { 1044 DPRINT1("RegInitializeRegistry() failed\n"); 1045 /********** HACK!!!!!!!!!!! **********/ 1046 if (Status == STATUS_NOT_IMPLEMENTED) 1047 { 1048 /* The hack was called, return its corresponding error */ 1049 return ERROR_INITIALIZE_REGISTRY; 1050 } 1051 else 1052 /*************************************/ 1053 { 1054 /* Something else failed */ 1055 return ERROR_CREATE_HIVE; 1056 } 1057 } 1058 1059 if (!RepairUpdateFlag || ShouldRepairRegistry) 1060 { 1061 /* 1062 * We fully setup the hives, in case we are doing a fresh installation 1063 * (RepairUpdateFlag == FALSE), or in case we are doing an update 1064 * (RepairUpdateFlag == TRUE) BUT we have some registry hives to 1065 * "repair" (aka. recreate: ShouldRepairRegistry == TRUE). 1066 */ 1067 1068 Success = SpInfFindFirstLine(pSetupData->SetupInf, L"HiveInfs.Fresh", NULL, &InfContext); // Windows-compatible 1069 if (!Success) 1070 Success = SpInfFindFirstLine(pSetupData->SetupInf, L"HiveInfs.Install", NULL, &InfContext); // ReactOS-specific 1071 1072 if (!Success) 1073 { 1074 DPRINT1("SpInfFindFirstLine() failed\n"); 1075 ErrorNumber = ERROR_FIND_REGISTRY; 1076 goto Cleanup; 1077 } 1078 } 1079 else // if (RepairUpdateFlag && !ShouldRepairRegistry) 1080 { 1081 /* 1082 * In case we are doing an update (RepairUpdateFlag == TRUE) and 1083 * NO registry hives need a repair (ShouldRepairRegistry == FALSE), 1084 * we only update the hives. 1085 */ 1086 1087 Success = SpInfFindFirstLine(pSetupData->SetupInf, L"HiveInfs.Upgrade", NULL, &InfContext); 1088 if (!Success) 1089 { 1090 /* Nothing to do for update! */ 1091 DPRINT1("No update needed for the registry!\n"); 1092 goto Cleanup; 1093 } 1094 } 1095 1096 do 1097 { 1098 INF_GetDataField(&InfContext, 0, &Action); 1099 INF_GetDataField(&InfContext, 1, &File); 1100 INF_GetDataField(&InfContext, 2, &Section); 1101 1102 DPRINT("Action: %S File: %S Section %S\n", Action, File, Section); 1103 1104 if (Action == NULL) 1105 { 1106 INF_FreeData(Action); 1107 INF_FreeData(File); 1108 INF_FreeData(Section); 1109 break; // Hackfix 1110 } 1111 1112 if (!_wcsicmp(Action, L"AddReg")) 1113 Delete = FALSE; 1114 else if (!_wcsicmp(Action, L"DelReg")) 1115 Delete = TRUE; 1116 else 1117 { 1118 DPRINT1("Unrecognized registry INF action '%S'\n", Action); 1119 INF_FreeData(Action); 1120 INF_FreeData(File); 1121 INF_FreeData(Section); 1122 continue; 1123 } 1124 1125 INF_FreeData(Action); 1126 1127 if (StatusRoutine) StatusRoutine(ImportRegHive, File); 1128 1129 if (!ImportRegistryFile(pSetupData->SourcePath.Buffer, 1130 File, Section, 1131 pSetupData->LanguageId, Delete)) 1132 { 1133 DPRINT1("Importing %S failed\n", File); 1134 INF_FreeData(File); 1135 INF_FreeData(Section); 1136 ErrorNumber = ERROR_IMPORT_HIVE; 1137 goto Cleanup; 1138 } 1139 } while (SpInfFindNextLine(&InfContext, &InfContext)); 1140 1141 if (!RepairUpdateFlag || ShouldRepairRegistry) 1142 { 1143 /* See the explanation for this test above */ 1144 1145 PGENERIC_LIST_ENTRY Entry; 1146 PCWSTR LanguageId; // LocaleID; 1147 1148 Entry = GetCurrentListEntry(pSetupData->DisplayList); 1149 ASSERT(Entry); 1150 pSetupData->DisplayType = ((PGENENTRY)GetListEntryData(Entry))->Id; 1151 ASSERT(pSetupData->DisplayType); 1152 1153 /* Update display registry settings */ 1154 if (StatusRoutine) StatusRoutine(DisplaySettingsUpdate); 1155 if (!ProcessDisplayRegistry(pSetupData->SetupInf, pSetupData->DisplayType)) 1156 { 1157 ErrorNumber = ERROR_UPDATE_DISPLAY_SETTINGS; 1158 goto Cleanup; 1159 } 1160 1161 Entry = GetCurrentListEntry(pSetupData->LanguageList); 1162 ASSERT(Entry); 1163 LanguageId = ((PGENENTRY)GetListEntryData(Entry))->Id; 1164 ASSERT(LanguageId); 1165 1166 /* Set the locale */ 1167 if (StatusRoutine) StatusRoutine(LocaleSettingsUpdate); 1168 if (!ProcessLocaleRegistry(/*pSetupData->*/LanguageId)) 1169 { 1170 ErrorNumber = ERROR_UPDATE_LOCALESETTINGS; 1171 goto Cleanup; 1172 } 1173 1174 /* Add the keyboard layouts for the given language (without user override) */ 1175 if (StatusRoutine) StatusRoutine(KeybLayouts); 1176 if (!AddKeyboardLayouts(SelectedLanguageId)) 1177 { 1178 ErrorNumber = ERROR_ADDING_KBLAYOUTS; 1179 goto Cleanup; 1180 } 1181 1182 if (!IsUnattendedSetup) 1183 { 1184 Entry = GetCurrentListEntry(pSetupData->LayoutList); 1185 ASSERT(Entry); 1186 pSetupData->LayoutId = ((PGENENTRY)GetListEntryData(Entry))->Id; 1187 ASSERT(pSetupData->LayoutId); 1188 1189 /* Update keyboard layout settings with user-overridden values */ 1190 // FIXME: Wouldn't it be better to do it all at once 1191 // with the AddKeyboardLayouts() step? 1192 if (StatusRoutine) StatusRoutine(KeybSettingsUpdate); 1193 if (!ProcessKeyboardLayoutRegistry(pSetupData->LayoutId, SelectedLanguageId)) 1194 { 1195 ErrorNumber = ERROR_UPDATE_KBSETTINGS; 1196 goto Cleanup; 1197 } 1198 } 1199 1200 /* Set GeoID */ 1201 if (!SetGeoID(MUIGetGeoID(SelectedLanguageId))) 1202 { 1203 ErrorNumber = ERROR_UPDATE_GEOID; 1204 goto Cleanup; 1205 } 1206 1207 /* Add codepage information to registry */ 1208 if (StatusRoutine) StatusRoutine(CodePageInfoUpdate); 1209 if (!AddCodePage(SelectedLanguageId)) 1210 { 1211 ErrorNumber = ERROR_ADDING_CODEPAGE; 1212 goto Cleanup; 1213 } 1214 1215 /* Set the default pagefile entry */ 1216 SetDefaultPagefile(DestinationDriveLetter); 1217 1218 /* Update the mounted devices list */ 1219 // FIXME: This should technically be done by mountmgr (if AutoMount is enabled)! 1220 SetMountedDeviceValues(PartitionList); 1221 } 1222 1223 #ifdef __REACTOS__ 1224 if (SubstSettings) 1225 { 1226 /* HACK */ 1227 DoRegistryFontFixup(SubstSettings, wcstoul(SelectedLanguageId, NULL, 16)); 1228 } 1229 #endif 1230 1231 Cleanup: 1232 // 1233 // TODO: Unload all the registry stuff, perform cleanup, 1234 // and copy the created hive files into .sav files. 1235 // 1236 RegCleanupRegistry(&pSetupData->DestinationPath); 1237 1238 /* 1239 * Check whether we were in update/repair mode but we were actually 1240 * repairing the registry hives. If so, we have finished repairing them, 1241 * and we now reset the flag and run the proper registry update. 1242 * Otherwise we have finished the registry update! 1243 */ 1244 if (RepairUpdateFlag && ShouldRepairRegistry) 1245 { 1246 ShouldRepairRegistry = FALSE; 1247 goto DoUpdate; 1248 } 1249 1250 return ErrorNumber; 1251 } 1252 1253 /* EOF */ 1254