xref: /reactos/base/setup/lib/setuplib.c (revision 32d615fc)
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 NTSTATUS
623 InitDestinationPaths(
624     IN OUT PUSETUP_DATA pSetupData,
625     IN PCWSTR InstallationDir,
626     IN PPARTENTRY PartEntry)    // FIXME: HACK!
627 {
628     NTSTATUS Status;
629     PDISKENTRY DiskEntry = PartEntry->DiskEntry;
630     WCHAR PathBuffer[MAX_PATH];
631 
632     ASSERT(PartEntry->IsPartitioned && PartEntry->PartitionNumber != 0);
633 
634     /* Create 'pSetupData->DestinationRootPath' string */
635     RtlFreeUnicodeString(&pSetupData->DestinationRootPath);
636     Status = RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
637                      L"\\Device\\Harddisk%lu\\Partition%lu\\",
638                      DiskEntry->DiskNumber,
639                      PartEntry->PartitionNumber);
640 
641     if (!NT_SUCCESS(Status))
642     {
643         DPRINT1("RtlStringCchPrintfW() failed with status 0x%08lx\n", Status);
644         return Status;
645     }
646 
647     Status = RtlCreateUnicodeString(&pSetupData->DestinationRootPath, PathBuffer) ? STATUS_SUCCESS : STATUS_NO_MEMORY;
648 
649     if (!NT_SUCCESS(Status))
650     {
651         DPRINT1("RtlCreateUnicodeString() failed with status 0x%08lx\n", Status);
652         return Status;
653     }
654 
655     DPRINT("DestinationRootPath: %wZ\n", &pSetupData->DestinationRootPath);
656 
657     // FIXME! Which variable to choose?
658     if (!InstallationDir)
659         InstallationDir = pSetupData->InstallationDirectory;
660 
661 /** Equivalent of 'NTOS_INSTALLATION::SystemArcPath' **/
662     /* Create 'pSetupData->DestinationArcPath' */
663     RtlFreeUnicodeString(&pSetupData->DestinationArcPath);
664 
665     if (DiskEntry->MediaType == FixedMedia)
666     {
667         if (DiskEntry->BiosFound)
668         {
669 #if 1
670             Status = RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
671                              L"multi(0)disk(0)rdisk(%lu)partition(%lu)\\",
672                              DiskEntry->HwFixedDiskNumber,
673                              PartEntry->OnDiskPartitionNumber);
674 #else
675             Status = RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
676                              L"multi(%lu)disk(%lu)rdisk(%lu)partition(%lu)\\",
677                              DiskEntry->HwAdapterNumber,
678                              DiskEntry->HwControllerNumber,
679                              DiskEntry->HwFixedDiskNumber,
680                              PartEntry->OnDiskPartitionNumber);
681 #endif
682             DPRINT1("Fixed disk found by BIOS, using MULTI ARC path '%S'\n", PathBuffer);
683         }
684         else
685         {
686             Status = RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
687                              L"scsi(%u)disk(%u)rdisk(%u)partition(%lu)\\",
688                              DiskEntry->Port,
689                              DiskEntry->Bus,
690                              DiskEntry->Id,
691                              PartEntry->OnDiskPartitionNumber);
692             DPRINT1("Fixed disk not found by BIOS, using SCSI ARC path '%S'\n", PathBuffer);
693         }
694     }
695     else // if (DiskEntry->MediaType == RemovableMedia)
696     {
697 #if 1
698         Status = RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
699                          L"multi(0)disk(0)rdisk(%lu)partition(%lu)\\",
700                          0, 1);
701         DPRINT1("Removable disk, using MULTI ARC path '%S'\n", PathBuffer);
702 #else
703         Status = RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
704                          L"signature(%08x)disk(%u)rdisk(%u)partition(%lu)\\",
705                          DiskEntry->LayoutBuffer->Signature,
706                          DiskEntry->Bus,
707                          DiskEntry->Id,
708                          PartEntry->OnDiskPartitionNumber);
709         DPRINT1("Removable disk, using SIGNATURE ARC path '%S'\n", PathBuffer);
710 #endif
711     }
712 
713     if (!NT_SUCCESS(Status))
714     {
715         DPRINT1("RtlStringCchPrintfW() failed with status 0x%08lx\n", Status);
716         RtlFreeUnicodeString(&pSetupData->DestinationRootPath);
717         return Status;
718     }
719 
720     Status = ConcatPaths(PathBuffer, ARRAYSIZE(PathBuffer), 1, InstallationDir);
721 
722     if (!NT_SUCCESS(Status))
723     {
724         DPRINT1("ConcatPaths() failed with status 0x%08lx\n", Status);
725         RtlFreeUnicodeString(&pSetupData->DestinationRootPath);
726         return Status;
727     }
728 
729     Status = RtlCreateUnicodeString(&pSetupData->DestinationArcPath, PathBuffer) ? STATUS_SUCCESS : STATUS_NO_MEMORY;
730 
731     if (!NT_SUCCESS(Status))
732     {
733         DPRINT1("RtlCreateUnicodeString() failed with status 0x%08lx\n", Status);
734         RtlFreeUnicodeString(&pSetupData->DestinationRootPath);
735         return Status;
736     }
737 
738 /** Equivalent of 'NTOS_INSTALLATION::SystemNtPath' **/
739     /* Create 'pSetupData->DestinationPath' string */
740     RtlFreeUnicodeString(&pSetupData->DestinationPath);
741     Status = CombinePaths(PathBuffer, ARRAYSIZE(PathBuffer), 2,
742                           pSetupData->DestinationRootPath.Buffer, InstallationDir);
743 
744     if (!NT_SUCCESS(Status))
745     {
746         DPRINT1("CombinePaths() failed with status 0x%08lx\n", Status);
747         RtlFreeUnicodeString(&pSetupData->DestinationArcPath);
748         RtlFreeUnicodeString(&pSetupData->DestinationRootPath);
749         return Status;
750     }
751 
752     Status = RtlCreateUnicodeString(&pSetupData->DestinationPath, PathBuffer) ? STATUS_SUCCESS : STATUS_NO_MEMORY;
753 
754     if (!NT_SUCCESS(Status))
755     {
756         DPRINT1("RtlCreateUnicodeString() failed with status 0x%08lx\n", Status);
757         RtlFreeUnicodeString(&pSetupData->DestinationArcPath);
758         RtlFreeUnicodeString(&pSetupData->DestinationRootPath);
759         return Status;
760     }
761 
762 /** Equivalent of 'NTOS_INSTALLATION::PathComponent' **/
763     // FIXME: This is only temporary!! Must be removed later!
764     Status = RtlCreateUnicodeString(&pSetupData->InstallPath, InstallationDir) ? STATUS_SUCCESS : STATUS_NO_MEMORY;
765 
766     if (!NT_SUCCESS(Status))
767     {
768         DPRINT1("RtlCreateUnicodeString() failed with status 0x%08lx\n", Status);
769         RtlFreeUnicodeString(&pSetupData->DestinationPath);
770         RtlFreeUnicodeString(&pSetupData->DestinationArcPath);
771         RtlFreeUnicodeString(&pSetupData->DestinationRootPath);
772         return Status;
773     }
774 
775     return STATUS_SUCCESS;
776 }
777 
778 // NTSTATUS
779 ERROR_NUMBER
780 InitializeSetup(
781     IN OUT PUSETUP_DATA pSetupData,
782     IN ULONG InitPhase)
783 {
784     if (InitPhase == 0)
785     {
786         RtlZeroMemory(pSetupData, sizeof(*pSetupData));
787 
788         /* Initialize error handling */
789         pSetupData->LastErrorNumber = ERROR_SUCCESS;
790         pSetupData->ErrorRoutine = NULL;
791 
792         /* Initialize global unicode strings */
793         RtlInitUnicodeString(&pSetupData->SourcePath, NULL);
794         RtlInitUnicodeString(&pSetupData->SourceRootPath, NULL);
795         RtlInitUnicodeString(&pSetupData->SourceRootDir, NULL);
796         RtlInitUnicodeString(&pSetupData->DestinationArcPath, NULL);
797         RtlInitUnicodeString(&pSetupData->DestinationPath, NULL);
798         RtlInitUnicodeString(&pSetupData->DestinationRootPath, NULL);
799         RtlInitUnicodeString(&pSetupData->SystemRootPath, NULL);
800 
801         // FIXME: This is only temporary!! Must be removed later!
802         /***/RtlInitUnicodeString(&pSetupData->InstallPath, NULL);/***/
803 
804         //
805         // TODO: Load and start SetupDD, and ask it for the information
806         //
807 
808         return ERROR_SUCCESS;
809     }
810     else
811     if (InitPhase == 1)
812     {
813         ERROR_NUMBER Error;
814         NTSTATUS Status;
815 
816         /* Get the source path and source root path */
817         //
818         // NOTE: Sometimes the source path may not be in SystemRoot !!
819         // (and this is the case when using the 1st-stage GUI setup!)
820         //
821         Status = GetSourcePaths(&pSetupData->SourcePath,
822                                 &pSetupData->SourceRootPath,
823                                 &pSetupData->SourceRootDir);
824         if (!NT_SUCCESS(Status))
825         {
826             DPRINT1("GetSourcePaths() failed (Status 0x%08lx)\n", Status);
827             return ERROR_NO_SOURCE_DRIVE;
828         }
829         /*
830          * Example of output:
831          *   SourcePath: '\Device\CdRom0\I386'
832          *   SourceRootPath: '\Device\CdRom0'
833          *   SourceRootDir: '\I386'
834          */
835         DPRINT1("SourcePath (1): '%wZ'\n", &pSetupData->SourcePath);
836         DPRINT1("SourceRootPath (1): '%wZ'\n", &pSetupData->SourceRootPath);
837         DPRINT1("SourceRootDir (1): '%wZ'\n", &pSetupData->SourceRootDir);
838 
839         /* Load 'txtsetup.sif' from the installation media */
840         Error = LoadSetupInf(pSetupData);
841         if (Error != ERROR_SUCCESS)
842         {
843             DPRINT1("LoadSetupInf() failed (Error 0x%lx)\n", Error);
844             return Error;
845         }
846         DPRINT1("SourcePath (2): '%wZ'\n", &pSetupData->SourcePath);
847         DPRINT1("SourceRootPath (2): '%wZ'\n", &pSetupData->SourceRootPath);
848         DPRINT1("SourceRootDir (2): '%wZ'\n", &pSetupData->SourceRootDir);
849 
850         return ERROR_SUCCESS;
851     }
852 
853     return ERROR_SUCCESS;
854 }
855 
856 VOID
857 FinishSetup(
858     IN OUT PUSETUP_DATA pSetupData)
859 {
860     /* Destroy the computer settings list */
861     if (pSetupData->ComputerList != NULL)
862     {
863         DestroyGenericList(pSetupData->ComputerList, TRUE);
864         pSetupData->ComputerList = NULL;
865     }
866 
867     /* Destroy the display settings list */
868     if (pSetupData->DisplayList != NULL)
869     {
870         DestroyGenericList(pSetupData->DisplayList, TRUE);
871         pSetupData->DisplayList = NULL;
872     }
873 
874     /* Destroy the keyboard settings list */
875     if (pSetupData->KeyboardList != NULL)
876     {
877         DestroyGenericList(pSetupData->KeyboardList, TRUE);
878         pSetupData->KeyboardList = NULL;
879     }
880 
881     /* Destroy the keyboard layout list */
882     if (pSetupData->LayoutList != NULL)
883     {
884         DestroyGenericList(pSetupData->LayoutList, TRUE);
885         pSetupData->LayoutList = NULL;
886     }
887 
888     /* Destroy the languages list */
889     if (pSetupData->LanguageList != NULL)
890     {
891         DestroyGenericList(pSetupData->LanguageList, FALSE);
892         pSetupData->LanguageList = NULL;
893     }
894 
895     /* Close the Setup INF */
896     SpInfCloseInfFile(pSetupData->SetupInf);
897 }
898 
899 /*
900  * SIDEEFFECTS
901  *  Calls RegInitializeRegistry
902  *  Calls ImportRegistryFile
903  *  Calls SetDefaultPagefile
904  *  Calls SetMountedDeviceValues
905  */
906 ERROR_NUMBER
907 UpdateRegistry(
908     IN OUT PUSETUP_DATA pSetupData,
909     /**/IN BOOLEAN RepairUpdateFlag,     /* HACK HACK! */
910     /**/IN PPARTLIST PartitionList,      /* HACK HACK! */
911     /**/IN WCHAR DestinationDriveLetter, /* HACK HACK! */
912     /**/IN PCWSTR SelectedLanguageId,    /* HACK HACK! */
913     IN PREGISTRY_STATUS_ROUTINE StatusRoutine OPTIONAL,
914     IN PFONTSUBSTSETTINGS SubstSettings OPTIONAL)
915 {
916     ERROR_NUMBER ErrorNumber;
917     NTSTATUS Status;
918     INFCONTEXT InfContext;
919     PCWSTR Action;
920     PCWSTR File;
921     PCWSTR Section;
922     BOOLEAN Success;
923     BOOLEAN ShouldRepairRegistry = FALSE;
924     BOOLEAN Delete;
925 
926     if (RepairUpdateFlag)
927     {
928         DPRINT1("TODO: Updating / repairing the registry is not completely implemented yet!\n");
929 
930         /* Verify the registry hives and check whether we need to update or repair any of them */
931         Status = VerifyRegistryHives(&pSetupData->DestinationPath, &ShouldRepairRegistry);
932         if (!NT_SUCCESS(Status))
933         {
934             DPRINT1("VerifyRegistryHives failed, Status 0x%08lx\n", Status);
935             ShouldRepairRegistry = FALSE;
936         }
937         if (!ShouldRepairRegistry)
938             DPRINT1("No need to repair the registry\n");
939     }
940 
941 DoUpdate:
942     ErrorNumber = ERROR_SUCCESS;
943 
944     /* Update the registry */
945     if (StatusRoutine) StatusRoutine(RegHiveUpdate);
946 
947     /* Initialize the registry and setup the registry hives */
948     Status = RegInitializeRegistry(&pSetupData->DestinationPath);
949     if (!NT_SUCCESS(Status))
950     {
951         DPRINT1("RegInitializeRegistry() failed\n");
952         /********** HACK!!!!!!!!!!! **********/
953         if (Status == STATUS_NOT_IMPLEMENTED)
954         {
955             /* The hack was called, return its corresponding error */
956             return ERROR_INITIALIZE_REGISTRY;
957         }
958         else
959         /*************************************/
960         {
961             /* Something else failed */
962             return ERROR_CREATE_HIVE;
963         }
964     }
965 
966     if (!RepairUpdateFlag || ShouldRepairRegistry)
967     {
968         /*
969          * We fully setup the hives, in case we are doing a fresh installation
970          * (RepairUpdateFlag == FALSE), or in case we are doing an update
971          * (RepairUpdateFlag == TRUE) BUT we have some registry hives to
972          * "repair" (aka. recreate: ShouldRepairRegistry == TRUE).
973          */
974 
975         Success = SpInfFindFirstLine(pSetupData->SetupInf, L"HiveInfs.Fresh", NULL, &InfContext);       // Windows-compatible
976         if (!Success)
977             Success = SpInfFindFirstLine(pSetupData->SetupInf, L"HiveInfs.Install", NULL, &InfContext); // ReactOS-specific
978 
979         if (!Success)
980         {
981             DPRINT1("SpInfFindFirstLine() failed\n");
982             ErrorNumber = ERROR_FIND_REGISTRY;
983             goto Cleanup;
984         }
985     }
986     else // if (RepairUpdateFlag && !ShouldRepairRegistry)
987     {
988         /*
989          * In case we are doing an update (RepairUpdateFlag == TRUE) and
990          * NO registry hives need a repair (ShouldRepairRegistry == FALSE),
991          * we only update the hives.
992          */
993 
994         Success = SpInfFindFirstLine(pSetupData->SetupInf, L"HiveInfs.Upgrade", NULL, &InfContext);
995         if (!Success)
996         {
997             /* Nothing to do for update! */
998             DPRINT1("No update needed for the registry!\n");
999             goto Cleanup;
1000         }
1001     }
1002 
1003     do
1004     {
1005         INF_GetDataField(&InfContext, 0, &Action);
1006         INF_GetDataField(&InfContext, 1, &File);
1007         INF_GetDataField(&InfContext, 2, &Section);
1008 
1009         DPRINT("Action: %S  File: %S  Section %S\n", Action, File, Section);
1010 
1011         if (Action == NULL)
1012         {
1013             INF_FreeData(Action);
1014             INF_FreeData(File);
1015             INF_FreeData(Section);
1016             break; // Hackfix
1017         }
1018 
1019         if (!_wcsicmp(Action, L"AddReg"))
1020             Delete = FALSE;
1021         else if (!_wcsicmp(Action, L"DelReg"))
1022             Delete = TRUE;
1023         else
1024         {
1025             DPRINT1("Unrecognized registry INF action '%S'\n", Action);
1026             INF_FreeData(Action);
1027             INF_FreeData(File);
1028             INF_FreeData(Section);
1029             continue;
1030         }
1031 
1032         INF_FreeData(Action);
1033 
1034         if (StatusRoutine) StatusRoutine(ImportRegHive, File);
1035 
1036         if (!ImportRegistryFile(pSetupData->SourcePath.Buffer,
1037                                 File, Section,
1038                                 pSetupData->LanguageId, Delete))
1039         {
1040             DPRINT1("Importing %S failed\n", File);
1041             INF_FreeData(File);
1042             INF_FreeData(Section);
1043             ErrorNumber = ERROR_IMPORT_HIVE;
1044             goto Cleanup;
1045         }
1046     } while (SpInfFindNextLine(&InfContext, &InfContext));
1047 
1048     if (!RepairUpdateFlag || ShouldRepairRegistry)
1049     {
1050         /* See the explanation for this test above */
1051 
1052         PGENERIC_LIST_ENTRY Entry;
1053         PCWSTR LanguageId; // LocaleID;
1054 
1055         Entry = GetCurrentListEntry(pSetupData->DisplayList);
1056         ASSERT(Entry);
1057         pSetupData->DisplayType = ((PGENENTRY)GetListEntryData(Entry))->Id;
1058         ASSERT(pSetupData->DisplayType);
1059 
1060         /* Update display registry settings */
1061         if (StatusRoutine) StatusRoutine(DisplaySettingsUpdate);
1062         if (!ProcessDisplayRegistry(pSetupData->SetupInf, pSetupData->DisplayType))
1063         {
1064             ErrorNumber = ERROR_UPDATE_DISPLAY_SETTINGS;
1065             goto Cleanup;
1066         }
1067 
1068         Entry = GetCurrentListEntry(pSetupData->LanguageList);
1069         ASSERT(Entry);
1070         LanguageId = ((PGENENTRY)GetListEntryData(Entry))->Id;
1071         ASSERT(LanguageId);
1072 
1073         /* Set the locale */
1074         if (StatusRoutine) StatusRoutine(LocaleSettingsUpdate);
1075         if (!ProcessLocaleRegistry(/*pSetupData->*/LanguageId))
1076         {
1077             ErrorNumber = ERROR_UPDATE_LOCALESETTINGS;
1078             goto Cleanup;
1079         }
1080 
1081         /* Add the keyboard layouts for the given language (without user override) */
1082         if (StatusRoutine) StatusRoutine(KeybLayouts);
1083         if (!AddKeyboardLayouts(SelectedLanguageId))
1084         {
1085             ErrorNumber = ERROR_ADDING_KBLAYOUTS;
1086             goto Cleanup;
1087         }
1088 
1089         if (!IsUnattendedSetup)
1090         {
1091             Entry = GetCurrentListEntry(pSetupData->LayoutList);
1092             ASSERT(Entry);
1093             pSetupData->LayoutId = ((PGENENTRY)GetListEntryData(Entry))->Id;
1094             ASSERT(pSetupData->LayoutId);
1095 
1096             /* Update keyboard layout settings with user-overridden values */
1097             // FIXME: Wouldn't it be better to do it all at once
1098             // with the AddKeyboardLayouts() step?
1099             if (StatusRoutine) StatusRoutine(KeybSettingsUpdate);
1100             if (!ProcessKeyboardLayoutRegistry(pSetupData->LayoutId, SelectedLanguageId))
1101             {
1102                 ErrorNumber = ERROR_UPDATE_KBSETTINGS;
1103                 goto Cleanup;
1104             }
1105         }
1106 
1107         /* Set GeoID */
1108         if (!SetGeoID(MUIGetGeoID(SelectedLanguageId)))
1109         {
1110             ErrorNumber = ERROR_UPDATE_GEOID;
1111             goto Cleanup;
1112         }
1113 
1114         /* Add codepage information to registry */
1115         if (StatusRoutine) StatusRoutine(CodePageInfoUpdate);
1116         if (!AddCodePage(SelectedLanguageId))
1117         {
1118             ErrorNumber = ERROR_ADDING_CODEPAGE;
1119             goto Cleanup;
1120         }
1121 
1122         /* Set the default pagefile entry */
1123         SetDefaultPagefile(DestinationDriveLetter);
1124 
1125         /* Update the mounted devices list */
1126         // FIXME: This should technically be done by mountmgr (if AutoMount is enabled)!
1127         SetMountedDeviceValues(PartitionList);
1128     }
1129 
1130 #ifdef __REACTOS__
1131     if (SubstSettings)
1132     {
1133         /* HACK */
1134         DoRegistryFontFixup(SubstSettings, wcstoul(SelectedLanguageId, NULL, 16));
1135     }
1136 #endif
1137 
1138 Cleanup:
1139     //
1140     // TODO: Unload all the registry stuff, perform cleanup,
1141     // and copy the created hive files into .sav files.
1142     //
1143     RegCleanupRegistry(&pSetupData->DestinationPath);
1144 
1145     /*
1146      * Check whether we were in update/repair mode but we were actually
1147      * repairing the registry hives. If so, we have finished repairing them,
1148      * and we now reset the flag and run the proper registry update.
1149      * Otherwise we have finished the registry update!
1150      */
1151     if (RepairUpdateFlag && ShouldRepairRegistry)
1152     {
1153         ShouldRepairRegistry = FALSE;
1154         goto DoUpdate;
1155     }
1156 
1157     return ErrorNumber;
1158 }
1159 
1160 /* EOF */
1161