xref: /reactos/base/setup/lib/setuplib.c (revision 2f03b146)
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         /* Initialize error handling */
793         pSetupData->LastErrorNumber = ERROR_SUCCESS;
794         pSetupData->ErrorRoutine = NULL;
795 
796         /* Initialize global unicode strings */
797         RtlInitUnicodeString(&pSetupData->SourcePath, NULL);
798         RtlInitUnicodeString(&pSetupData->SourceRootPath, NULL);
799         RtlInitUnicodeString(&pSetupData->SourceRootDir, NULL);
800         RtlInitUnicodeString(&pSetupData->DestinationArcPath, NULL);
801         RtlInitUnicodeString(&pSetupData->DestinationPath, NULL);
802         RtlInitUnicodeString(&pSetupData->DestinationRootPath, NULL);
803         RtlInitUnicodeString(&pSetupData->SystemRootPath, NULL);
804 
805         // FIXME: This is only temporary!! Must be removed later!
806         /***/RtlInitUnicodeString(&pSetupData->InstallPath, NULL);/***/
807 
808         //
809         // TODO: Load and start SetupDD, and ask it for the information
810         //
811 
812         return ERROR_SUCCESS;
813     }
814     else
815     if (InitPhase == 1)
816     {
817         ERROR_NUMBER Error;
818         NTSTATUS Status;
819 
820         /* Get the source path and source root path */
821         //
822         // NOTE: Sometimes the source path may not be in SystemRoot !!
823         // (and this is the case when using the 1st-stage GUI setup!)
824         //
825         Status = GetSourcePaths(&pSetupData->SourcePath,
826                                 &pSetupData->SourceRootPath,
827                                 &pSetupData->SourceRootDir);
828         if (!NT_SUCCESS(Status))
829         {
830             DPRINT1("GetSourcePaths() failed (Status 0x%08lx)\n", Status);
831             return ERROR_NO_SOURCE_DRIVE;
832         }
833         /*
834          * Example of output:
835          *   SourcePath: '\Device\CdRom0\I386'
836          *   SourceRootPath: '\Device\CdRom0'
837          *   SourceRootDir: '\I386'
838          */
839         DPRINT1("SourcePath (1): '%wZ'\n", &pSetupData->SourcePath);
840         DPRINT1("SourceRootPath (1): '%wZ'\n", &pSetupData->SourceRootPath);
841         DPRINT1("SourceRootDir (1): '%wZ'\n", &pSetupData->SourceRootDir);
842 
843         /* Load 'txtsetup.sif' from the installation media */
844         Error = LoadSetupInf(pSetupData);
845         if (Error != ERROR_SUCCESS)
846         {
847             DPRINT1("LoadSetupInf() failed (Error 0x%lx)\n", Error);
848             return Error;
849         }
850         DPRINT1("SourcePath (2): '%wZ'\n", &pSetupData->SourcePath);
851         DPRINT1("SourceRootPath (2): '%wZ'\n", &pSetupData->SourceRootPath);
852         DPRINT1("SourceRootDir (2): '%wZ'\n", &pSetupData->SourceRootDir);
853 
854         return ERROR_SUCCESS;
855     }
856 
857     return ERROR_SUCCESS;
858 }
859 
860 VOID
861 FinishSetup(
862     IN OUT PUSETUP_DATA pSetupData)
863 {
864     /* Destroy the computer settings list */
865     if (pSetupData->ComputerList != NULL)
866     {
867         DestroyGenericList(pSetupData->ComputerList, TRUE);
868         pSetupData->ComputerList = NULL;
869     }
870 
871     /* Destroy the display settings list */
872     if (pSetupData->DisplayList != NULL)
873     {
874         DestroyGenericList(pSetupData->DisplayList, TRUE);
875         pSetupData->DisplayList = NULL;
876     }
877 
878     /* Destroy the keyboard settings list */
879     if (pSetupData->KeyboardList != NULL)
880     {
881         DestroyGenericList(pSetupData->KeyboardList, TRUE);
882         pSetupData->KeyboardList = NULL;
883     }
884 
885     /* Destroy the keyboard layout list */
886     if (pSetupData->LayoutList != NULL)
887     {
888         DestroyGenericList(pSetupData->LayoutList, TRUE);
889         pSetupData->LayoutList = NULL;
890     }
891 
892     /* Destroy the languages list */
893     if (pSetupData->LanguageList != NULL)
894     {
895         DestroyGenericList(pSetupData->LanguageList, FALSE);
896         pSetupData->LanguageList = NULL;
897     }
898 
899     /* Close the Setup INF */
900     SpInfCloseInfFile(pSetupData->SetupInf);
901 }
902 
903 /*
904  * SIDEEFFECTS
905  *  Calls RegInitializeRegistry
906  *  Calls ImportRegistryFile
907  *  Calls SetDefaultPagefile
908  *  Calls SetMountedDeviceValues
909  */
910 ERROR_NUMBER
911 UpdateRegistry(
912     IN OUT PUSETUP_DATA pSetupData,
913     /**/IN BOOLEAN RepairUpdateFlag,     /* HACK HACK! */
914     /**/IN PPARTLIST PartitionList,      /* HACK HACK! */
915     /**/IN WCHAR DestinationDriveLetter, /* HACK HACK! */
916     /**/IN PCWSTR SelectedLanguageId,    /* HACK HACK! */
917     IN PREGISTRY_STATUS_ROUTINE StatusRoutine OPTIONAL,
918     IN PFONTSUBSTSETTINGS SubstSettings OPTIONAL)
919 {
920     ERROR_NUMBER ErrorNumber;
921     NTSTATUS Status;
922     INFCONTEXT InfContext;
923     PCWSTR Action;
924     PCWSTR File;
925     PCWSTR Section;
926     BOOLEAN Success;
927     BOOLEAN ShouldRepairRegistry = FALSE;
928     BOOLEAN Delete;
929 
930     if (RepairUpdateFlag)
931     {
932         DPRINT1("TODO: Updating / repairing the registry is not completely implemented yet!\n");
933 
934         /* Verify the registry hives and check whether we need to update or repair any of them */
935         Status = VerifyRegistryHives(&pSetupData->DestinationPath, &ShouldRepairRegistry);
936         if (!NT_SUCCESS(Status))
937         {
938             DPRINT1("VerifyRegistryHives failed, Status 0x%08lx\n", Status);
939             ShouldRepairRegistry = FALSE;
940         }
941         if (!ShouldRepairRegistry)
942             DPRINT1("No need to repair the registry\n");
943     }
944 
945 DoUpdate:
946     ErrorNumber = ERROR_SUCCESS;
947 
948     /* Update the registry */
949     if (StatusRoutine) StatusRoutine(RegHiveUpdate);
950 
951     /* Initialize the registry and setup the registry hives */
952     Status = RegInitializeRegistry(&pSetupData->DestinationPath);
953     if (!NT_SUCCESS(Status))
954     {
955         DPRINT1("RegInitializeRegistry() failed\n");
956         /********** HACK!!!!!!!!!!! **********/
957         if (Status == STATUS_NOT_IMPLEMENTED)
958         {
959             /* The hack was called, return its corresponding error */
960             return ERROR_INITIALIZE_REGISTRY;
961         }
962         else
963         /*************************************/
964         {
965             /* Something else failed */
966             return ERROR_CREATE_HIVE;
967         }
968     }
969 
970     if (!RepairUpdateFlag || ShouldRepairRegistry)
971     {
972         /*
973          * We fully setup the hives, in case we are doing a fresh installation
974          * (RepairUpdateFlag == FALSE), or in case we are doing an update
975          * (RepairUpdateFlag == TRUE) BUT we have some registry hives to
976          * "repair" (aka. recreate: ShouldRepairRegistry == TRUE).
977          */
978 
979         Success = SpInfFindFirstLine(pSetupData->SetupInf, L"HiveInfs.Fresh", NULL, &InfContext);       // Windows-compatible
980         if (!Success)
981             Success = SpInfFindFirstLine(pSetupData->SetupInf, L"HiveInfs.Install", NULL, &InfContext); // ReactOS-specific
982 
983         if (!Success)
984         {
985             DPRINT1("SpInfFindFirstLine() failed\n");
986             ErrorNumber = ERROR_FIND_REGISTRY;
987             goto Cleanup;
988         }
989     }
990     else // if (RepairUpdateFlag && !ShouldRepairRegistry)
991     {
992         /*
993          * In case we are doing an update (RepairUpdateFlag == TRUE) and
994          * NO registry hives need a repair (ShouldRepairRegistry == FALSE),
995          * we only update the hives.
996          */
997 
998         Success = SpInfFindFirstLine(pSetupData->SetupInf, L"HiveInfs.Upgrade", NULL, &InfContext);
999         if (!Success)
1000         {
1001             /* Nothing to do for update! */
1002             DPRINT1("No update needed for the registry!\n");
1003             goto Cleanup;
1004         }
1005     }
1006 
1007     do
1008     {
1009         INF_GetDataField(&InfContext, 0, &Action);
1010         INF_GetDataField(&InfContext, 1, &File);
1011         INF_GetDataField(&InfContext, 2, &Section);
1012 
1013         DPRINT("Action: %S  File: %S  Section %S\n", Action, File, Section);
1014 
1015         if (Action == NULL)
1016         {
1017             INF_FreeData(Action);
1018             INF_FreeData(File);
1019             INF_FreeData(Section);
1020             break; // Hackfix
1021         }
1022 
1023         if (!_wcsicmp(Action, L"AddReg"))
1024             Delete = FALSE;
1025         else if (!_wcsicmp(Action, L"DelReg"))
1026             Delete = TRUE;
1027         else
1028         {
1029             DPRINT1("Unrecognized registry INF action '%S'\n", Action);
1030             INF_FreeData(Action);
1031             INF_FreeData(File);
1032             INF_FreeData(Section);
1033             continue;
1034         }
1035 
1036         INF_FreeData(Action);
1037 
1038         if (StatusRoutine) StatusRoutine(ImportRegHive, File);
1039 
1040         if (!ImportRegistryFile(pSetupData->SourcePath.Buffer,
1041                                 File, Section,
1042                                 pSetupData->LanguageId, Delete))
1043         {
1044             DPRINT1("Importing %S failed\n", File);
1045             INF_FreeData(File);
1046             INF_FreeData(Section);
1047             ErrorNumber = ERROR_IMPORT_HIVE;
1048             goto Cleanup;
1049         }
1050     } while (SpInfFindNextLine(&InfContext, &InfContext));
1051 
1052     if (!RepairUpdateFlag || ShouldRepairRegistry)
1053     {
1054         /* See the explanation for this test above */
1055 
1056         PGENERIC_LIST_ENTRY Entry;
1057         PCWSTR LanguageId; // LocaleID;
1058 
1059         Entry = GetCurrentListEntry(pSetupData->DisplayList);
1060         ASSERT(Entry);
1061         pSetupData->DisplayType = ((PGENENTRY)GetListEntryData(Entry))->Id;
1062         ASSERT(pSetupData->DisplayType);
1063 
1064         /* Update display registry settings */
1065         if (StatusRoutine) StatusRoutine(DisplaySettingsUpdate);
1066         if (!ProcessDisplayRegistry(pSetupData->SetupInf, pSetupData->DisplayType))
1067         {
1068             ErrorNumber = ERROR_UPDATE_DISPLAY_SETTINGS;
1069             goto Cleanup;
1070         }
1071 
1072         Entry = GetCurrentListEntry(pSetupData->LanguageList);
1073         ASSERT(Entry);
1074         LanguageId = ((PGENENTRY)GetListEntryData(Entry))->Id;
1075         ASSERT(LanguageId);
1076 
1077         /* Set the locale */
1078         if (StatusRoutine) StatusRoutine(LocaleSettingsUpdate);
1079         if (!ProcessLocaleRegistry(/*pSetupData->*/LanguageId))
1080         {
1081             ErrorNumber = ERROR_UPDATE_LOCALESETTINGS;
1082             goto Cleanup;
1083         }
1084 
1085         /* Add the keyboard layouts for the given language (without user override) */
1086         if (StatusRoutine) StatusRoutine(KeybLayouts);
1087         if (!AddKeyboardLayouts(SelectedLanguageId))
1088         {
1089             ErrorNumber = ERROR_ADDING_KBLAYOUTS;
1090             goto Cleanup;
1091         }
1092 
1093         if (!IsUnattendedSetup)
1094         {
1095             Entry = GetCurrentListEntry(pSetupData->LayoutList);
1096             ASSERT(Entry);
1097             pSetupData->LayoutId = ((PGENENTRY)GetListEntryData(Entry))->Id;
1098             ASSERT(pSetupData->LayoutId);
1099 
1100             /* Update keyboard layout settings with user-overridden values */
1101             // FIXME: Wouldn't it be better to do it all at once
1102             // with the AddKeyboardLayouts() step?
1103             if (StatusRoutine) StatusRoutine(KeybSettingsUpdate);
1104             if (!ProcessKeyboardLayoutRegistry(pSetupData->LayoutId, SelectedLanguageId))
1105             {
1106                 ErrorNumber = ERROR_UPDATE_KBSETTINGS;
1107                 goto Cleanup;
1108             }
1109         }
1110 
1111         /* Set GeoID */
1112         if (!SetGeoID(MUIGetGeoID(SelectedLanguageId)))
1113         {
1114             ErrorNumber = ERROR_UPDATE_GEOID;
1115             goto Cleanup;
1116         }
1117 
1118         /* Add codepage information to registry */
1119         if (StatusRoutine) StatusRoutine(CodePageInfoUpdate);
1120         if (!AddCodePage(SelectedLanguageId))
1121         {
1122             ErrorNumber = ERROR_ADDING_CODEPAGE;
1123             goto Cleanup;
1124         }
1125 
1126         /* Set the default pagefile entry */
1127         SetDefaultPagefile(DestinationDriveLetter);
1128 
1129         /* Update the mounted devices list */
1130         // FIXME: This should technically be done by mountmgr (if AutoMount is enabled)!
1131         SetMountedDeviceValues(PartitionList);
1132     }
1133 
1134 #ifdef __REACTOS__
1135     if (SubstSettings)
1136     {
1137         /* HACK */
1138         DoRegistryFontFixup(SubstSettings, wcstoul(SelectedLanguageId, NULL, 16));
1139     }
1140 #endif
1141 
1142 Cleanup:
1143     //
1144     // TODO: Unload all the registry stuff, perform cleanup,
1145     // and copy the created hive files into .sav files.
1146     //
1147     RegCleanupRegistry(&pSetupData->DestinationPath);
1148 
1149     /*
1150      * Check whether we were in update/repair mode but we were actually
1151      * repairing the registry hives. If so, we have finished repairing them,
1152      * and we now reset the flag and run the proper registry update.
1153      * Otherwise we have finished the registry update!
1154      */
1155     if (RepairUpdateFlag && ShouldRepairRegistry)
1156     {
1157         ShouldRepairRegistry = FALSE;
1158         goto DoUpdate;
1159     }
1160 
1161     return ErrorNumber;
1162 }
1163 
1164 /* EOF */
1165