xref: /reactos/base/setup/lib/setuplib.c (revision 44323e61)
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 Quit:
200     SpInfCloseInfFile(UnattendInf);
201 }
202 
203 VOID
204 InstallSetupInfFile(
205     IN OUT PUSETUP_DATA pSetupData)
206 {
207     NTSTATUS Status;
208     PINICACHE IniCache;
209 
210 #if 0 // HACK FIXME!
211     PINICACHE UnattendCache;
212     PINICACHEITERATOR Iterator;
213 #else
214     // WCHAR CrLf[] = {L'\r', L'\n'};
215     CHAR CrLf[] = {'\r', '\n'};
216     HANDLE FileHandle, UnattendFileHandle, SectionHandle;
217     FILE_STANDARD_INFORMATION FileInfo;
218     ULONG FileSize;
219     PVOID ViewBase;
220     UNICODE_STRING FileName;
221     OBJECT_ATTRIBUTES ObjectAttributes;
222     IO_STATUS_BLOCK IoStatusBlock;
223 #endif
224 
225     PINICACHESECTION IniSection;
226     WCHAR PathBuffer[MAX_PATH];
227     WCHAR UnattendInfPath[MAX_PATH];
228 
229     /* Create a $winnt$.inf file with default entries */
230     IniCache = IniCacheCreate();
231     if (!IniCache)
232         return;
233 
234     IniSection = IniCacheAppendSection(IniCache, L"SetupParams");
235     if (IniSection)
236     {
237         /* Key "skipmissingfiles" */
238         // RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
239                             // L"\"%s\"", L"WinNt5.2");
240         // IniCacheInsertKey(IniSection, NULL, INSERT_LAST,
241                           // L"Version", PathBuffer);
242     }
243 
244     IniSection = IniCacheAppendSection(IniCache, L"Data");
245     if (IniSection)
246     {
247         RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
248                             L"\"%s\"", IsUnattendedSetup ? L"yes" : L"no");
249         IniCacheInsertKey(IniSection, NULL, INSERT_LAST,
250                           L"UnattendedInstall", PathBuffer);
251 
252         // "floppylessbootpath" (yes/no)
253 
254         RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
255                             L"\"%s\"", L"winnt");
256         IniCacheInsertKey(IniSection, NULL, INSERT_LAST,
257                           L"ProductType", PathBuffer);
258 
259         RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
260                             L"\"%s\\\"", pSetupData->SourceRootPath.Buffer);
261         IniCacheInsertKey(IniSection, NULL, INSERT_LAST,
262                           L"SourcePath", PathBuffer);
263 
264         // "floppyless" ("0")
265     }
266 
267 #if 0
268 
269     /* TODO: Append the standard unattend.inf file */
270     CombinePaths(UnattendInfPath, ARRAYSIZE(UnattendInfPath), 2,
271                  pSetupData->SourcePath.Buffer, L"unattend.inf");
272     if (DoesFileExist(NULL, UnattendInfPath) == FALSE)
273     {
274         DPRINT("Does not exist: %S\n", UnattendInfPath);
275         goto Quit;
276     }
277 
278     Status = IniCacheLoad(&UnattendCache, UnattendInfPath, FALSE);
279     if (!NT_SUCCESS(Status))
280     {
281         DPRINT1("Cannot load %S as an INI file!\n", UnattendInfPath);
282         goto Quit;
283     }
284 
285     IniCacheDestroy(UnattendCache);
286 
287 Quit:
288     CombinePaths(PathBuffer, ARRAYSIZE(PathBuffer), 2,
289                  pSetupData->DestinationPath.Buffer, L"System32\\$winnt$.inf");
290     IniCacheSave(IniCache, PathBuffer);
291     IniCacheDestroy(IniCache);
292 
293 #else
294 
295     CombinePaths(PathBuffer, ARRAYSIZE(PathBuffer), 2,
296                  pSetupData->DestinationPath.Buffer, L"System32\\$winnt$.inf");
297     IniCacheSave(IniCache, PathBuffer);
298     IniCacheDestroy(IniCache);
299 
300     /* TODO: Append the standard unattend.inf file */
301     CombinePaths(UnattendInfPath, ARRAYSIZE(UnattendInfPath), 2,
302                  pSetupData->SourcePath.Buffer, L"unattend.inf");
303     if (DoesFileExist(NULL, UnattendInfPath) == FALSE)
304     {
305         DPRINT("Does not exist: %S\n", UnattendInfPath);
306         return;
307     }
308 
309     RtlInitUnicodeString(&FileName, PathBuffer);
310     InitializeObjectAttributes(&ObjectAttributes,
311                                &FileName,
312                                OBJ_CASE_INSENSITIVE | OBJ_OPENIF,
313                                NULL,
314                                NULL);
315     Status = NtOpenFile(&FileHandle,
316                         FILE_APPEND_DATA | SYNCHRONIZE,
317                         &ObjectAttributes,
318                         &IoStatusBlock,
319                         FILE_SHARE_READ,
320                         FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE);
321     if (!NT_SUCCESS(Status))
322     {
323         DPRINT1("Cannot load %S as an INI file!\n", PathBuffer);
324         return;
325     }
326 
327     /* Query the file size */
328     Status = NtQueryInformationFile(FileHandle,
329                                     &IoStatusBlock,
330                                     &FileInfo,
331                                     sizeof(FileInfo),
332                                     FileStandardInformation);
333     if (!NT_SUCCESS(Status))
334     {
335         DPRINT("NtQueryInformationFile() failed (Status %lx)\n", Status);
336         FileInfo.EndOfFile.QuadPart = 0ULL;
337     }
338 
339     Status = OpenAndMapFile(NULL,
340                             UnattendInfPath,
341                             &UnattendFileHandle,
342                             &SectionHandle,
343                             &ViewBase,
344                             &FileSize,
345                             FALSE);
346     if (!NT_SUCCESS(Status))
347     {
348         DPRINT1("Cannot load %S !\n", UnattendInfPath);
349         NtClose(FileHandle);
350         return;
351     }
352 
353     /* Write to the INI file */
354 
355     /* "\r\n" */
356     Status = NtWriteFile(FileHandle,
357                          NULL,
358                          NULL,
359                          NULL,
360                          &IoStatusBlock,
361                          (PVOID)CrLf,
362                          sizeof(CrLf),
363                          &FileInfo.EndOfFile,
364                          NULL);
365 
366     Status = NtWriteFile(FileHandle,
367                          NULL,
368                          NULL,
369                          NULL,
370                          &IoStatusBlock,
371                          ViewBase,
372                          FileSize,
373                          NULL,
374                          NULL);
375     if (!NT_SUCCESS(Status))
376     {
377         DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
378     }
379 
380     /* Finally, unmap and close the file */
381     UnMapAndCloseFile(UnattendFileHandle, SectionHandle, ViewBase);
382 
383     NtClose(FileHandle);
384 #endif
385 }
386 
387 NTSTATUS
388 GetSourcePaths(
389     OUT PUNICODE_STRING SourcePath,
390     OUT PUNICODE_STRING SourceRootPath,
391     OUT PUNICODE_STRING SourceRootDir)
392 {
393     NTSTATUS Status;
394     HANDLE LinkHandle;
395     OBJECT_ATTRIBUTES ObjectAttributes;
396     UCHAR ImageFileBuffer[sizeof(UNICODE_STRING) + MAX_PATH * sizeof(WCHAR)];
397     PUNICODE_STRING InstallSourcePath = (PUNICODE_STRING)&ImageFileBuffer;
398     WCHAR SystemRootBuffer[MAX_PATH] = L"";
399     UNICODE_STRING SystemRootPath = RTL_CONSTANT_STRING(L"\\SystemRoot");
400     ULONG BufferSize;
401     PWCHAR Ptr;
402 
403     /* Determine the installation source path via the full path of the installer */
404     RtlInitEmptyUnicodeString(InstallSourcePath,
405                               (PWSTR)((ULONG_PTR)ImageFileBuffer + sizeof(UNICODE_STRING)),
406                               sizeof(ImageFileBuffer) - sizeof(UNICODE_STRING)
407             /* Reserve space for a NULL terminator */ - sizeof(UNICODE_NULL));
408     BufferSize = sizeof(ImageFileBuffer);
409     Status = NtQueryInformationProcess(NtCurrentProcess(),
410                                        ProcessImageFileName,
411                                        InstallSourcePath,
412                                        BufferSize,
413                                        NULL);
414     // STATUS_INFO_LENGTH_MISMATCH or STATUS_BUFFER_TOO_SMALL ?
415     if (!NT_SUCCESS(Status))
416         return Status;
417 
418     /* Manually NULL-terminate */
419     InstallSourcePath->Buffer[InstallSourcePath->Length / sizeof(WCHAR)] = UNICODE_NULL;
420 
421     /* Strip the trailing file name */
422     Ptr = wcsrchr(InstallSourcePath->Buffer, OBJ_NAME_PATH_SEPARATOR);
423     if (Ptr)
424         *Ptr = UNICODE_NULL;
425     InstallSourcePath->Length = wcslen(InstallSourcePath->Buffer) * sizeof(WCHAR);
426 
427 
428     /*
429      * Now resolve the full path to \SystemRoot. In case it prefixes
430      * the installation source path determined from the full path of
431      * the installer, we use instead the resolved \SystemRoot as the
432      * installation source path.
433      * Otherwise, we use instead the path from the full installer path.
434      */
435 
436     InitializeObjectAttributes(&ObjectAttributes,
437                                &SystemRootPath,
438                                OBJ_CASE_INSENSITIVE,
439                                NULL,
440                                NULL);
441 
442     Status = NtOpenSymbolicLinkObject(&LinkHandle,
443                                       SYMBOLIC_LINK_QUERY,
444                                       &ObjectAttributes);
445     if (!NT_SUCCESS(Status))
446     {
447         /*
448          * We failed at opening the \SystemRoot link (usually due to wrong
449          * access rights). Do not consider this as a fatal error, but use
450          * instead the image file path as the installation source path.
451          */
452         DPRINT1("NtOpenSymbolicLinkObject(%wZ) failed with Status 0x%08lx\n",
453                 &SystemRootPath, Status);
454         goto InitPaths;
455     }
456 
457     RtlInitEmptyUnicodeString(&SystemRootPath,
458                               SystemRootBuffer,
459                               sizeof(SystemRootBuffer));
460 
461     /* Resolve the link and close its handle */
462     Status = NtQuerySymbolicLinkObject(LinkHandle,
463                                        &SystemRootPath,
464                                        &BufferSize);
465     NtClose(LinkHandle);
466 
467     if (!NT_SUCCESS(Status))
468         return Status; // Unexpected error
469 
470     /* Check whether the resolved \SystemRoot is a prefix of the image file path */
471     if (RtlPrefixUnicodeString(&SystemRootPath, InstallSourcePath, TRUE))
472     {
473         /* Yes it is, so we use instead SystemRoot as the installation source path */
474         InstallSourcePath = &SystemRootPath;
475     }
476 
477 
478 InitPaths:
479     /*
480      * Retrieve the different source path components
481      */
482     RtlCreateUnicodeString(SourcePath, InstallSourcePath->Buffer);
483 
484     /* Strip trailing directory */
485     Ptr = wcsrchr(InstallSourcePath->Buffer, OBJ_NAME_PATH_SEPARATOR);
486     if (Ptr)
487     {
488         RtlCreateUnicodeString(SourceRootDir, Ptr);
489         *Ptr = UNICODE_NULL;
490     }
491     else
492     {
493         RtlCreateUnicodeString(SourceRootDir, L"");
494     }
495 
496     RtlCreateUnicodeString(SourceRootPath, InstallSourcePath->Buffer);
497 
498     return STATUS_SUCCESS;
499 }
500 
501 ERROR_NUMBER
502 LoadSetupInf(
503     IN OUT PUSETUP_DATA pSetupData)
504 {
505     INFCONTEXT Context;
506     UINT ErrorLine;
507     INT IntValue;
508     PCWSTR Value;
509     WCHAR FileNameBuffer[MAX_PATH];
510 
511     CombinePaths(FileNameBuffer, ARRAYSIZE(FileNameBuffer), 2,
512                  pSetupData->SourcePath.Buffer, L"txtsetup.sif");
513 
514     DPRINT("SetupInf path: '%S'\n", FileNameBuffer);
515 
516     pSetupData->SetupInf =
517         SpInfOpenInfFile(FileNameBuffer,
518                          NULL,
519                          INF_STYLE_WIN4,
520                          pSetupData->LanguageId,
521                          &ErrorLine);
522     if (pSetupData->SetupInf == INVALID_HANDLE_VALUE)
523         return ERROR_LOAD_TXTSETUPSIF;
524 
525     /* Open 'Version' section */
526     if (!SpInfFindFirstLine(pSetupData->SetupInf, L"Version", L"Signature", &Context))
527         return ERROR_CORRUPT_TXTSETUPSIF;
528 
529     /* Get pointer 'Signature' key */
530     if (!INF_GetData(&Context, NULL, &Value))
531         return ERROR_CORRUPT_TXTSETUPSIF;
532 
533     /* Check 'Signature' string */
534     if (_wcsicmp(Value, L"$ReactOS$") != 0 &&
535         _wcsicmp(Value, L"$Windows NT$") != 0)
536     {
537         INF_FreeData(Value);
538         return ERROR_SIGNATURE_TXTSETUPSIF;
539     }
540 
541     INF_FreeData(Value);
542 
543     /* Open 'DiskSpaceRequirements' section */
544     if (!SpInfFindFirstLine(pSetupData->SetupInf, L"DiskSpaceRequirements", L"FreeSysPartDiskSpace", &Context))
545         return ERROR_CORRUPT_TXTSETUPSIF;
546 
547     pSetupData->RequiredPartitionDiskSpace = ~0;
548 
549     /* Get the 'FreeSysPartDiskSpace' value */
550     if (!SpInfGetIntField(&Context, 1, &IntValue))
551         return ERROR_CORRUPT_TXTSETUPSIF;
552 
553     pSetupData->RequiredPartitionDiskSpace = (ULONG)IntValue;
554 
555     //
556     // Support "SetupSourceDevice" and "SetupSourcePath" in txtsetup.sif
557     // See CORE-9023
558     // Support for that should also be added in setupldr.
559     //
560 
561     /* Update the Setup Source paths */
562     if (SpInfFindFirstLine(pSetupData->SetupInf, L"SetupData", L"SetupSourceDevice", &Context))
563     {
564         /*
565          * Get optional pointer 'SetupSourceDevice' key, its presence
566          * will dictate whether we also need 'SetupSourcePath'.
567          */
568         if (INF_GetData(&Context, NULL, &Value))
569         {
570             /* Free the old source root path string and create the new one */
571             RtlFreeUnicodeString(&pSetupData->SourceRootPath);
572             RtlCreateUnicodeString(&pSetupData->SourceRootPath, Value);
573             INF_FreeData(Value);
574 
575             if (!SpInfFindFirstLine(pSetupData->SetupInf, L"SetupData", L"SetupSourcePath", &Context))
576             {
577                 /* The 'SetupSourcePath' value is mandatory! */
578                 return ERROR_CORRUPT_TXTSETUPSIF;
579             }
580 
581             /* Get pointer 'SetupSourcePath' key */
582             if (!INF_GetData(&Context, NULL, &Value))
583             {
584                 /* The 'SetupSourcePath' value is mandatory! */
585                 return ERROR_CORRUPT_TXTSETUPSIF;
586             }
587 
588             /* Free the old source path string and create the new one */
589             RtlFreeUnicodeString(&pSetupData->SourceRootDir);
590             RtlCreateUnicodeString(&pSetupData->SourceRootDir, Value);
591             INF_FreeData(Value);
592         }
593     }
594 
595     /* Search for 'DefaultPath' in the 'SetupData' section */
596     pSetupData->InstallationDirectory[0] = 0;
597     if (SpInfFindFirstLine(pSetupData->SetupInf, L"SetupData", L"DefaultPath", &Context))
598     {
599         /* Get pointer 'DefaultPath' key */
600         if (!INF_GetData(&Context, NULL, &Value))
601             return ERROR_CORRUPT_TXTSETUPSIF;
602 
603         RtlStringCchCopyW(pSetupData->InstallationDirectory,
604                           ARRAYSIZE(pSetupData->InstallationDirectory),
605                           Value);
606 
607         INF_FreeData(Value);
608     }
609 
610     return ERROR_SUCCESS;
611 }
612 
613 NTSTATUS
614 InitDestinationPaths(
615     IN OUT PUSETUP_DATA pSetupData,
616     IN PCWSTR InstallationDir,
617     IN PDISKENTRY DiskEntry,    // FIXME: HACK!
618     IN PPARTENTRY PartEntry)    // FIXME: HACK!
619 {
620     WCHAR PathBuffer[MAX_PATH];
621 
622     //
623     // TODO: Check return status values of the functions!
624     //
625 
626     /* Create 'pSetupData->DestinationRootPath' string */
627     RtlFreeUnicodeString(&pSetupData->DestinationRootPath);
628     RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
629             L"\\Device\\Harddisk%lu\\Partition%lu\\",
630             DiskEntry->DiskNumber,
631             PartEntry->PartitionNumber);
632     RtlCreateUnicodeString(&pSetupData->DestinationRootPath, PathBuffer);
633     DPRINT("DestinationRootPath: %wZ\n", &pSetupData->DestinationRootPath);
634 
635     // FIXME! Which variable to choose?
636     if (!InstallationDir)
637         InstallationDir = pSetupData->InstallationDirectory;
638 
639 /** Equivalent of 'NTOS_INSTALLATION::SystemArcPath' **/
640     /* Create 'pSetupData->DestinationArcPath' */
641     RtlFreeUnicodeString(&pSetupData->DestinationArcPath);
642     RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
643             L"multi(0)disk(0)rdisk(%lu)partition(%lu)\\",
644             DiskEntry->BiosDiskNumber,
645             PartEntry->PartitionNumber);
646     ConcatPaths(PathBuffer, ARRAYSIZE(PathBuffer), 1, InstallationDir);
647     RtlCreateUnicodeString(&pSetupData->DestinationArcPath, PathBuffer);
648 
649 /** Equivalent of 'NTOS_INSTALLATION::SystemNtPath' **/
650     /* Create 'pSetupData->DestinationPath' string */
651     RtlFreeUnicodeString(&pSetupData->DestinationPath);
652     CombinePaths(PathBuffer, ARRAYSIZE(PathBuffer), 2,
653                  pSetupData->DestinationRootPath.Buffer, InstallationDir);
654     RtlCreateUnicodeString(&pSetupData->DestinationPath, PathBuffer);
655 
656 /** Equivalent of 'NTOS_INSTALLATION::PathComponent' **/
657     // FIXME: This is only temporary!! Must be removed later!
658     /***/RtlCreateUnicodeString(&pSetupData->InstallPath, InstallationDir);/***/
659 
660     return STATUS_SUCCESS;
661 }
662 
663 // NTSTATUS
664 ERROR_NUMBER
665 InitializeSetup(
666     IN OUT PUSETUP_DATA pSetupData,
667     IN ULONG InitPhase)
668 {
669     if (InitPhase == 0)
670     {
671         RtlZeroMemory(pSetupData, sizeof(*pSetupData));
672 
673         // pSetupData->ComputerList = NULL;
674         // pSetupData->DisplayList  = NULL;
675         // pSetupData->KeyboardList = NULL;
676         // pSetupData->LayoutList   = NULL;
677         // pSetupData->LanguageList = NULL;
678 
679         /* Initialize error handling */
680         pSetupData->LastErrorNumber = ERROR_SUCCESS;
681         pSetupData->ErrorRoutine = NULL;
682 
683         /* Initialize global unicode strings */
684         RtlInitUnicodeString(&pSetupData->SourcePath, NULL);
685         RtlInitUnicodeString(&pSetupData->SourceRootPath, NULL);
686         RtlInitUnicodeString(&pSetupData->SourceRootDir, NULL);
687         RtlInitUnicodeString(&pSetupData->DestinationArcPath, NULL);
688         RtlInitUnicodeString(&pSetupData->DestinationPath, NULL);
689         RtlInitUnicodeString(&pSetupData->DestinationRootPath, NULL);
690         RtlInitUnicodeString(&pSetupData->SystemRootPath, NULL);
691 
692         // FIXME: This is only temporary!! Must be removed later!
693         /***/RtlInitUnicodeString(&pSetupData->InstallPath, NULL);/***/
694 
695         //
696         // TODO: Load and start SetupDD, and ask it for the information
697         //
698 
699         return ERROR_SUCCESS;
700     }
701     else
702     if (InitPhase == 1)
703     {
704         ERROR_NUMBER Error;
705         NTSTATUS Status;
706 
707         /* Get the source path and source root path */
708         //
709         // NOTE: Sometimes the source path may not be in SystemRoot !!
710         // (and this is the case when using the 1st-stage GUI setup!)
711         //
712         Status = GetSourcePaths(&pSetupData->SourcePath,
713                                 &pSetupData->SourceRootPath,
714                                 &pSetupData->SourceRootDir);
715         if (!NT_SUCCESS(Status))
716         {
717             DPRINT1("GetSourcePaths() failed (Status 0x%08lx)", Status);
718             return ERROR_NO_SOURCE_DRIVE;
719         }
720         /*
721          * Example of output:
722          *   SourcePath: '\Device\CdRom0\I386'
723          *   SourceRootPath: '\Device\CdRom0'
724          *   SourceRootDir: '\I386'
725          */
726         DPRINT1("SourcePath (1): '%wZ'\n", &pSetupData->SourcePath);
727         DPRINT1("SourceRootPath (1): '%wZ'\n", &pSetupData->SourceRootPath);
728         DPRINT1("SourceRootDir (1): '%wZ'\n", &pSetupData->SourceRootDir);
729 
730         /* Load 'txtsetup.sif' from the installation media */
731         Error = LoadSetupInf(pSetupData);
732         if (Error != ERROR_SUCCESS)
733         {
734             DPRINT1("LoadSetupInf() failed (Error 0x%lx)", Error);
735             return Error;
736         }
737         DPRINT1("SourcePath (2): '%wZ'\n", &pSetupData->SourcePath);
738         DPRINT1("SourceRootPath (2): '%wZ'\n", &pSetupData->SourceRootPath);
739         DPRINT1("SourceRootDir (2): '%wZ'\n", &pSetupData->SourceRootDir);
740 
741         return ERROR_SUCCESS;
742     }
743 
744     return ERROR_SUCCESS;
745 }
746 
747 VOID
748 FinishSetup(
749     IN OUT PUSETUP_DATA pSetupData)
750 {
751     /* Destroy the computer settings list */
752     if (pSetupData->ComputerList != NULL)
753     {
754         DestroyGenericList(pSetupData->ComputerList, TRUE);
755         pSetupData->ComputerList = NULL;
756     }
757 
758     /* Destroy the display settings list */
759     if (pSetupData->DisplayList != NULL)
760     {
761         DestroyGenericList(pSetupData->DisplayList, TRUE);
762         pSetupData->DisplayList = NULL;
763     }
764 
765     /* Destroy the keyboard settings list */
766     if (pSetupData->KeyboardList != NULL)
767     {
768         DestroyGenericList(pSetupData->KeyboardList, TRUE);
769         pSetupData->KeyboardList = NULL;
770     }
771 
772     /* Destroy the keyboard layout list */
773     if (pSetupData->LayoutList != NULL)
774     {
775         DestroyGenericList(pSetupData->LayoutList, TRUE);
776         pSetupData->LayoutList = NULL;
777     }
778 
779     /* Destroy the languages list */
780     if (pSetupData->LanguageList != NULL)
781     {
782         DestroyGenericList(pSetupData->LanguageList, FALSE);
783         pSetupData->LanguageList = NULL;
784     }
785 
786     /* Close the Setup INF */
787     SpInfCloseInfFile(pSetupData->SetupInf);
788 }
789 
790 /*
791  * SIDEEFFECTS
792  *  Calls RegInitializeRegistry
793  *  Calls ImportRegistryFile
794  *  Calls SetDefaultPagefile
795  *  Calls SetMountedDeviceValues
796  */
797 ERROR_NUMBER
798 UpdateRegistry(
799     IN OUT PUSETUP_DATA pSetupData,
800     /**/IN BOOLEAN RepairUpdateFlag,     /* HACK HACK! */
801     /**/IN PPARTLIST PartitionList,      /* HACK HACK! */
802     /**/IN WCHAR DestinationDriveLetter, /* HACK HACK! */
803     /**/IN PCWSTR SelectedLanguageId,    /* HACK HACK! */
804     IN PREGISTRY_STATUS_ROUTINE StatusRoutine OPTIONAL)
805 {
806     ERROR_NUMBER ErrorNumber;
807     NTSTATUS Status;
808     INFCONTEXT InfContext;
809     PCWSTR Action;
810     PCWSTR File;
811     PCWSTR Section;
812     BOOLEAN Success;
813     BOOLEAN ShouldRepairRegistry = FALSE;
814     BOOLEAN Delete;
815 
816     if (RepairUpdateFlag)
817     {
818         DPRINT1("TODO: Updating / repairing the registry is not completely implemented yet!\n");
819 
820         /* Verify the registry hives and check whether we need to update or repair any of them */
821         Status = VerifyRegistryHives(&pSetupData->DestinationPath, &ShouldRepairRegistry);
822         if (!NT_SUCCESS(Status))
823         {
824             DPRINT1("VerifyRegistryHives failed, Status 0x%08lx\n", Status);
825             ShouldRepairRegistry = FALSE;
826         }
827         if (!ShouldRepairRegistry)
828             DPRINT1("No need to repair the registry\n");
829     }
830 
831 DoUpdate:
832     ErrorNumber = ERROR_SUCCESS;
833 
834     /* Update the registry */
835     if (StatusRoutine) StatusRoutine(RegHiveUpdate);
836 
837     /* Initialize the registry and setup the registry hives */
838     Status = RegInitializeRegistry(&pSetupData->DestinationPath);
839     if (!NT_SUCCESS(Status))
840     {
841         DPRINT1("RegInitializeRegistry() failed\n");
842         /********** HACK!!!!!!!!!!! **********/
843         if (Status == STATUS_NOT_IMPLEMENTED)
844         {
845             /* The hack was called, return its corresponding error */
846             return ERROR_INITIALIZE_REGISTRY;
847         }
848         else
849         /*************************************/
850         {
851             /* Something else failed */
852             return ERROR_CREATE_HIVE;
853         }
854     }
855 
856     if (!RepairUpdateFlag || ShouldRepairRegistry)
857     {
858         /*
859          * We fully setup the hives, in case we are doing a fresh installation
860          * (RepairUpdateFlag == FALSE), or in case we are doing an update
861          * (RepairUpdateFlag == TRUE) BUT we have some registry hives to
862          * "repair" (aka. recreate: ShouldRepairRegistry == TRUE).
863          */
864 
865         Success = SpInfFindFirstLine(pSetupData->SetupInf, L"HiveInfs.Fresh", NULL, &InfContext);       // Windows-compatible
866         if (!Success)
867             Success = SpInfFindFirstLine(pSetupData->SetupInf, L"HiveInfs.Install", NULL, &InfContext); // ReactOS-specific
868 
869         if (!Success)
870         {
871             DPRINT1("SpInfFindFirstLine() failed\n");
872             ErrorNumber = ERROR_FIND_REGISTRY;
873             goto Cleanup;
874         }
875     }
876     else // if (RepairUpdateFlag && !ShouldRepairRegistry)
877     {
878         /*
879          * In case we are doing an update (RepairUpdateFlag == TRUE) and
880          * NO registry hives need a repair (ShouldRepairRegistry == FALSE),
881          * we only update the hives.
882          */
883 
884         Success = SpInfFindFirstLine(pSetupData->SetupInf, L"HiveInfs.Upgrade", NULL, &InfContext);
885         if (!Success)
886         {
887             /* Nothing to do for update! */
888             DPRINT1("No update needed for the registry!\n");
889             goto Cleanup;
890         }
891     }
892 
893     do
894     {
895         INF_GetDataField(&InfContext, 0, &Action);
896         INF_GetDataField(&InfContext, 1, &File);
897         INF_GetDataField(&InfContext, 2, &Section);
898 
899         DPRINT("Action: %S  File: %S  Section %S\n", Action, File, Section);
900 
901         if (Action == NULL)
902         {
903             INF_FreeData(Action);
904             INF_FreeData(File);
905             INF_FreeData(Section);
906             break; // Hackfix
907         }
908 
909         if (!_wcsicmp(Action, L"AddReg"))
910             Delete = FALSE;
911         else if (!_wcsicmp(Action, L"DelReg"))
912             Delete = TRUE;
913         else
914         {
915             DPRINT1("Unrecognized registry INF action '%S'\n", Action);
916             INF_FreeData(Action);
917             INF_FreeData(File);
918             INF_FreeData(Section);
919             continue;
920         }
921 
922         INF_FreeData(Action);
923 
924         if (StatusRoutine) StatusRoutine(ImportRegHive, File);
925 
926         if (!ImportRegistryFile(pSetupData->SourcePath.Buffer,
927                                 File, Section,
928                                 pSetupData->LanguageId, Delete))
929         {
930             DPRINT1("Importing %S failed\n", File);
931             INF_FreeData(File);
932             INF_FreeData(Section);
933             ErrorNumber = ERROR_IMPORT_HIVE;
934             goto Cleanup;
935         }
936     } while (SpInfFindNextLine(&InfContext, &InfContext));
937 
938     if (!RepairUpdateFlag || ShouldRepairRegistry)
939     {
940         /* See the explanation for this test above */
941 
942         /* Update display registry settings */
943         if (StatusRoutine) StatusRoutine(DisplaySettingsUpdate);
944         if (!ProcessDisplayRegistry(pSetupData->SetupInf, pSetupData->DisplayList))
945         {
946             ErrorNumber = ERROR_UPDATE_DISPLAY_SETTINGS;
947             goto Cleanup;
948         }
949 
950         /* Set the locale */
951         if (StatusRoutine) StatusRoutine(LocaleSettingsUpdate);
952         if (!ProcessLocaleRegistry(pSetupData->LanguageList))
953         {
954             ErrorNumber = ERROR_UPDATE_LOCALESETTINGS;
955             goto Cleanup;
956         }
957 
958         /* Add keyboard layouts */
959         if (StatusRoutine) StatusRoutine(KeybLayouts);
960         if (!AddKeyboardLayouts(SelectedLanguageId))
961         {
962             ErrorNumber = ERROR_ADDING_KBLAYOUTS;
963             goto Cleanup;
964         }
965 
966         /* Set GeoID */
967         if (!SetGeoID(MUIGetGeoID(SelectedLanguageId)))
968         {
969             ErrorNumber = ERROR_UPDATE_GEOID;
970             goto Cleanup;
971         }
972 
973         if (!IsUnattendedSetup)
974         {
975             /* Update keyboard layout settings */
976             if (StatusRoutine) StatusRoutine(KeybSettingsUpdate);
977             if (!ProcessKeyboardLayoutRegistry(pSetupData->LayoutList, SelectedLanguageId))
978             {
979                 ErrorNumber = ERROR_UPDATE_KBSETTINGS;
980                 goto Cleanup;
981             }
982         }
983 
984         /* Add codepage information to registry */
985         if (StatusRoutine) StatusRoutine(CodePageInfoUpdate);
986         if (!AddCodePage(SelectedLanguageId))
987         {
988             ErrorNumber = ERROR_ADDING_CODEPAGE;
989             goto Cleanup;
990         }
991 
992         /* Set the default pagefile entry */
993         SetDefaultPagefile(DestinationDriveLetter);
994 
995         /* Update the mounted devices list */
996         // FIXME: This should technically be done by mountmgr (if AutoMount is enabled)!
997         SetMountedDeviceValues(PartitionList);
998     }
999 
1000 Cleanup:
1001     //
1002     // TODO: Unload all the registry stuff, perform cleanup,
1003     // and copy the created hive files into .sav files.
1004     //
1005     RegCleanupRegistry(&pSetupData->DestinationPath);
1006 
1007     /*
1008      * Check whether we were in update/repair mode but we were actually
1009      * repairing the registry hives. If so, we have finished repairing them,
1010      * and we now reset the flag and run the proper registry update.
1011      * Otherwise we have finished the registry update!
1012      */
1013     if (RepairUpdateFlag && ShouldRepairRegistry)
1014     {
1015         ShouldRepairRegistry = FALSE;
1016         goto DoUpdate;
1017     }
1018 
1019     return ErrorNumber;
1020 }
1021 
1022 /* EOF */
1023