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