xref: /reactos/base/setup/lib/setuplib.c (revision 69931a4a)
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