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