xref: /reactos/base/setup/lib/setuplib.c (revision 3ddb05d4)
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
CheckUnattendedSetup(IN OUT PUSETUP_DATA pSetupData)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' */
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' */
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' (optional) */
138     if (SpInfFindFirstLine(UnattendInf, L"Unattend", L"InstallationDirectory", &Context))
139     {
140         if (INF_GetData(&Context, NULL, &Value))
141         {
142             RtlStringCchCopyW(pSetupData->InstallationDirectory,
143                               ARRAYSIZE(pSetupData->InstallationDirectory),
144                               Value);
145             INF_FreeData(Value);
146         }
147         else
148         {
149             DPRINT("INF_GetData() failed for key 'InstallationDirectory'\n");
150         }
151     }
152 
153     IsUnattendedSetup = TRUE;
154     DPRINT("Running unattended setup\n");
155 
156     /* Search for 'BootLoaderLocation' (optional) */
157     if (SpInfFindFirstLine(UnattendInf, L"Unattend", L"BootLoaderLocation", &Context))
158     {
159         if (SpInfGetIntField(&Context, 1, &IntValue))
160             pSetupData->BootLoaderLocation = IntValue;
161     }
162 
163     /* Search for 'FormatPartition' (optional) */
164     if (SpInfFindFirstLine(UnattendInf, L"Unattend", L"FormatPartition", &Context))
165     {
166         if (SpInfGetIntField(&Context, 1, &IntValue))
167             pSetupData->FormatPartition = IntValue;
168     }
169 
170     /* Search for 'AutoPartition' (optional) */
171     if (SpInfFindFirstLine(UnattendInf, L"Unattend", L"AutoPartition", &Context))
172     {
173         if (SpInfGetIntField(&Context, 1, &IntValue))
174             pSetupData->AutoPartition = IntValue;
175     }
176 
177     /* Search for 'LocaleID' (optional) */
178     if (SpInfFindFirstLine(UnattendInf, L"Unattend", L"LocaleID", &Context))
179     {
180         if (INF_GetData(&Context, NULL, &Value))
181         {
182             LONG Id = wcstol(Value, NULL, 16);
183             RtlStringCchPrintfW(pSetupData->LocaleID,
184                                 ARRAYSIZE(pSetupData->LocaleID),
185                                 L"%08lx", Id);
186             INF_FreeData(Value);
187         }
188     }
189 
190     /* Search for 'FsType' (optional) */
191     if (SpInfFindFirstLine(UnattendInf, L"Unattend", L"FsType", &Context))
192     {
193         if (SpInfGetIntField(&Context, 1, &IntValue))
194             pSetupData->FsType = IntValue;
195     }
196 
197 Quit:
198     SpInfCloseInfFile(UnattendInf);
199 }
200 
201 VOID
InstallSetupInfFile(IN OUT PUSETUP_DATA pSetupData)202 InstallSetupInfFile(
203     IN OUT PUSETUP_DATA pSetupData)
204 {
205     NTSTATUS Status;
206     PINICACHE IniCache;
207 
208 #if 0 // HACK FIXME!
209     PINICACHE UnattendCache;
210     PINICACHEITERATOR Iterator;
211 #else
212     // WCHAR CrLf[] = {L'\r', L'\n'};
213     CHAR CrLf[] = {'\r', '\n'};
214     HANDLE FileHandle, UnattendFileHandle, SectionHandle;
215     FILE_STANDARD_INFORMATION FileInfo;
216     ULONG FileSize;
217     PVOID ViewBase;
218     UNICODE_STRING FileName;
219     OBJECT_ATTRIBUTES ObjectAttributes;
220     IO_STATUS_BLOCK IoStatusBlock;
221 #endif
222 
223     PINI_SECTION IniSection;
224     WCHAR PathBuffer[MAX_PATH];
225     WCHAR UnattendInfPath[MAX_PATH];
226 
227     /* Create a $winnt$.inf file with default entries */
228     IniCache = IniCacheCreate();
229     if (!IniCache)
230         return;
231 
232     IniSection = IniAddSection(IniCache, L"SetupParams");
233     if (IniSection)
234     {
235         /* Key "skipmissingfiles" */
236         // RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
237                             // L"\"%s\"", L"WinNt5.2");
238         // IniAddKey(IniSection, L"Version", PathBuffer);
239     }
240 
241     IniSection = IniAddSection(IniCache, L"Data");
242     if (IniSection)
243     {
244         RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
245                             L"\"%s\"", IsUnattendedSetup ? L"yes" : L"no");
246         IniAddKey(IniSection, L"UnattendedInstall", PathBuffer);
247 
248         // "floppylessbootpath" (yes/no)
249 
250         RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
251                             L"\"%s\"", L"winnt");
252         IniAddKey(IniSection, L"ProductType", PathBuffer);
253 
254         RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
255                             L"\"%s\\\"", pSetupData->SourceRootPath.Buffer);
256         IniAddKey(IniSection, L"SourcePath", PathBuffer);
257 
258         // "floppyless" ("0")
259     }
260 
261 #if 0
262 
263     /* TODO: Append the standard unattend.inf file */
264     CombinePaths(UnattendInfPath, ARRAYSIZE(UnattendInfPath), 2,
265                  pSetupData->SourcePath.Buffer, L"unattend.inf");
266     if (DoesFileExist(NULL, UnattendInfPath) == FALSE)
267     {
268         DPRINT("Does not exist: %S\n", UnattendInfPath);
269         goto Quit;
270     }
271 
272     Status = IniCacheLoad(&UnattendCache, UnattendInfPath, FALSE);
273     if (!NT_SUCCESS(Status))
274     {
275         DPRINT1("Cannot load %S as an INI file!\n", UnattendInfPath);
276         goto Quit;
277     }
278 
279     IniCacheDestroy(UnattendCache);
280 
281 Quit:
282     CombinePaths(PathBuffer, ARRAYSIZE(PathBuffer), 2,
283                  pSetupData->DestinationPath.Buffer, L"System32\\$winnt$.inf");
284     IniCacheSave(IniCache, PathBuffer);
285     IniCacheDestroy(IniCache);
286 
287 #else
288 
289     CombinePaths(PathBuffer, ARRAYSIZE(PathBuffer), 2,
290                  pSetupData->DestinationPath.Buffer, L"System32\\$winnt$.inf");
291     IniCacheSave(IniCache, PathBuffer);
292     IniCacheDestroy(IniCache);
293 
294     /* TODO: Append the standard unattend.inf file */
295     CombinePaths(UnattendInfPath, ARRAYSIZE(UnattendInfPath), 2,
296                  pSetupData->SourcePath.Buffer, L"unattend.inf");
297     if (DoesFileExist(NULL, UnattendInfPath) == FALSE)
298     {
299         DPRINT("Does not exist: %S\n", UnattendInfPath);
300         return;
301     }
302 
303     RtlInitUnicodeString(&FileName, PathBuffer);
304     InitializeObjectAttributes(&ObjectAttributes,
305                                &FileName,
306                                OBJ_CASE_INSENSITIVE | OBJ_OPENIF,
307                                NULL,
308                                NULL);
309     Status = NtOpenFile(&FileHandle,
310                         FILE_APPEND_DATA | SYNCHRONIZE,
311                         &ObjectAttributes,
312                         &IoStatusBlock,
313                         FILE_SHARE_READ,
314                         FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE);
315     if (!NT_SUCCESS(Status))
316     {
317         DPRINT1("Cannot load %S as an INI file!\n", PathBuffer);
318         return;
319     }
320 
321     /* Query the file size */
322     Status = NtQueryInformationFile(FileHandle,
323                                     &IoStatusBlock,
324                                     &FileInfo,
325                                     sizeof(FileInfo),
326                                     FileStandardInformation);
327     if (!NT_SUCCESS(Status))
328     {
329         DPRINT("NtQueryInformationFile() failed (Status %lx)\n", Status);
330         FileInfo.EndOfFile.QuadPart = 0ULL;
331     }
332 
333     Status = OpenAndMapFile(NULL,
334                             UnattendInfPath,
335                             &UnattendFileHandle,
336                             &FileSize,
337                             &SectionHandle,
338                             &ViewBase,
339                             FALSE);
340     if (!NT_SUCCESS(Status))
341     {
342         DPRINT1("Cannot load %S !\n", UnattendInfPath);
343         NtClose(FileHandle);
344         return;
345     }
346 
347     /* Write to the INI file */
348 
349     /* "\r\n" */
350     Status = NtWriteFile(FileHandle,
351                          NULL,
352                          NULL,
353                          NULL,
354                          &IoStatusBlock,
355                          (PVOID)CrLf,
356                          sizeof(CrLf),
357                          &FileInfo.EndOfFile,
358                          NULL);
359 
360     Status = NtWriteFile(FileHandle,
361                          NULL,
362                          NULL,
363                          NULL,
364                          &IoStatusBlock,
365                          ViewBase,
366                          FileSize,
367                          NULL,
368                          NULL);
369     if (!NT_SUCCESS(Status))
370     {
371         DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
372     }
373 
374     /* Finally, unmap and close the file */
375     UnMapAndCloseFile(UnattendFileHandle, SectionHandle, ViewBase);
376 
377     NtClose(FileHandle);
378 #endif
379 }
380 
381 /**
382  * @brief
383  * Determine the installation source path and isolate its useful
384  * path components (root path and source sub-directory).
385  *
386  * The installation source path is based either on the installer's
387  * image file path, or on the \SystemRoot full path.
388  *
389  * In case the \SystemRoot full path prefixes the image file path,
390  * use the resolved \SystemRoot as the installation source path.
391  * Otherwise, use the image file path.
392  *
393  * The returned strings are allocated with RtlCreateUnicodeString(),
394  * and need to be freed with RtlFreeUnicodeString() after being used.
395  *
396  * Example of output:
397  *   SourcePath: '\Device\CdRom0\I386'
398  *   SourceRootPath: '\Device\CdRom0'
399  *   SourceRootDir: '\I386'
400  **/
401 NTSTATUS
GetSourcePaths(_Out_ PUNICODE_STRING SourcePath,_Out_ PUNICODE_STRING SourceRootPath,_Out_ PUNICODE_STRING SourceRootDir)402 GetSourcePaths(
403     _Out_ PUNICODE_STRING SourcePath,
404     _Out_ PUNICODE_STRING SourceRootPath,
405     _Out_ PUNICODE_STRING SourceRootDir)
406 {
407     NTSTATUS Status;
408     ULONG BufferSize;
409     PWCHAR Ptr;
410     HANDLE LinkHandle;
411     OBJECT_ATTRIBUTES ObjectAttributes;
412     IO_STATUS_BLOCK IoStatusBlock;
413     struct { OBJECT_NAME_INFORMATION; WCHAR Buffer[MAX_PATH]; } ImageFileBuffer;
414     PUNICODE_STRING InstallSourcePath = &ImageFileBuffer.Name;
415     struct { OBJECT_NAME_INFORMATION; WCHAR Buffer[MAX_PATH]; } SystemRootBuffer;
416     PUNICODE_STRING SystemRootPath = &SystemRootBuffer.Name;
417     const UNICODE_STRING SystemRoot = RTL_CONSTANT_STRING(L"\\SystemRoot");
418 
419     /* Retrieve the installer's full image file path */
420     RtlInitEmptyUnicodeString(InstallSourcePath,
421                               ImageFileBuffer.Buffer,
422                               sizeof(ImageFileBuffer.Buffer));
423     BufferSize = sizeof(ImageFileBuffer);
424     Status = NtQueryInformationProcess(NtCurrentProcess(),
425                                        ProcessImageFileName,
426                                        InstallSourcePath,
427                                        BufferSize,
428                                        NULL);
429     // STATUS_INFO_LENGTH_MISMATCH or STATUS_BUFFER_TOO_SMALL ?
430     if (!NT_SUCCESS(Status))
431         return Status;
432     ASSERT(InstallSourcePath->Length < InstallSourcePath->MaximumLength);
433 
434     /* Go to the beginning of the path component, stop at the separator */
435     Ptr = ImageFileBuffer.Buffer + (InstallSourcePath->Length / sizeof(WCHAR));
436     while ((Ptr > ImageFileBuffer.Buffer) && (*Ptr != OBJ_NAME_PATH_SEPARATOR))
437         --Ptr;
438     /* Strip the trailing file name (at the separator or beginning of buffer)
439      * and manually NULL-terminate */
440     InstallSourcePath->Length = (ULONG_PTR)Ptr - (ULONG_PTR)ImageFileBuffer.Buffer;
441     InstallSourcePath->Buffer[InstallSourcePath->Length / sizeof(WCHAR)] = UNICODE_NULL;
442 
443 
444     /*
445      * Now, resolve the \SystemRoot symlink target full path.
446      *
447      * The symlink target path resolution requires reparsing, because it
448      * can reference other symlinks. This is what happens, for example when
449      * booting the installation from a removable hard-disk. We can have:
450      *
451      *          \SystemRoot ---> \Device\Harddisk1\Partition1\ReactOS
452      * and:     \Device\Harddisk1\Partition1 ---> \Device\HarddiskVolume2
453      * etc.
454      * and we wish to resolve \SystemRoot to: \Device\HarddiskVolume2\ReactOS
455      *
456      * We then verify whether it prefixes the image file path obtained
457      * from the step above, which is a fully reparsed path.
458      *
459      * - Using NtOpenSymbolicLinkObject(SYMBOLIC_LINK_QUERY) followed by
460      *   NtQuerySymbolicLinkObject() would only resolve the first symlink
461      *   but not the others (\Device\Harddisk1\Partition1 left as is).
462      *
463      * - Since \SystemRoot has to point to a directory, we try opening
464      *   the directory itself: NtOpenFile(..., FILE_DIRECTORY_FILE).
465      *
466      * - A call to NtQueryInformationFile(FileNameInformation) alone on
467      *   the obtained handle would only retrieve the FS directory name,
468      *   i.e. \ReactOS , but not the whole NT path.
469      *
470      * - We therefore use NtQueryObject(), which allows retrieving the
471      *   full resolved NT path (device name + FS directory name).
472      */
473 
474     InitializeObjectAttributes(&ObjectAttributes,
475                                (PUNICODE_STRING)&SystemRoot,
476                                OBJ_CASE_INSENSITIVE,
477                                NULL,
478                                NULL);
479 
480     RtlInitEmptyUnicodeString(SystemRootPath,
481                               SystemRootBuffer.Buffer,
482                               sizeof(SystemRootBuffer.Buffer));
483 
484     Status = NtOpenFile(&LinkHandle,
485                         SYNCHRONIZE,
486                         &ObjectAttributes,
487                         &IoStatusBlock,
488                         FILE_SHARE_READ | FILE_SHARE_WRITE,
489                         FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT
490                             /*| FILE_OPEN_FOR_BACKUP_INTENT*/);
491     if (NT_SUCCESS(Status))
492     {
493         /* Resolve the path and close its handle */
494         Status = NtQueryObject(LinkHandle,
495                                ObjectNameInformation,
496                                &SystemRootBuffer,
497                                sizeof(SystemRootBuffer),
498                                &BufferSize);
499         NtClose(LinkHandle);
500     }
501     /* If any of the calls above failed, try to naively resolve the symlink */
502     if (!NT_SUCCESS(Status))
503     {
504         RtlInitEmptyUnicodeString(SystemRootPath,
505                                   SystemRootBuffer.Buffer,
506                                   sizeof(SystemRootBuffer.Buffer));
507 
508         Status = NtOpenSymbolicLinkObject(&LinkHandle,
509                                           SYMBOLIC_LINK_QUERY,
510                                           &ObjectAttributes);
511         if (NT_SUCCESS(Status))
512         {
513             /* Resolve the link and close its handle */
514             Status = NtQuerySymbolicLinkObject(LinkHandle,
515                                                SystemRootPath,
516                                                &BufferSize);
517             NtClose(LinkHandle);
518         }
519     }
520     ASSERT(SystemRootPath->Length < SystemRootPath->MaximumLength);
521 
522     /*
523      * If the resolved \SystemRoot is a prefix of the image file path,
524      * use \SystemRoot instead as the installation source path.
525      *
526      * If opening the \SystemRoot link failed (usually due to wrong
527      * access rights), do not consider this as a fatal error, and
528      * use the image file path as the installation source path.
529      */
530     if (NT_SUCCESS(Status) && RtlPrefixUnicodeString(SystemRootPath, InstallSourcePath, TRUE))
531         InstallSourcePath = SystemRootPath;
532 
533 
534     /*
535      * Retrieve the different source path components.
536      */
537     RtlCreateUnicodeString(SourcePath, InstallSourcePath->Buffer);
538 
539     /* Isolate and strip the trailing (source root) directory */
540     Ptr = wcsrchr(InstallSourcePath->Buffer, OBJ_NAME_PATH_SEPARATOR);
541     if (Ptr)
542     {
543         RtlCreateUnicodeString(SourceRootDir, Ptr);
544         *Ptr = UNICODE_NULL;
545     }
546     else
547     {
548         RtlCreateUnicodeString(SourceRootDir, L"");
549     }
550 
551     RtlCreateUnicodeString(SourceRootPath, InstallSourcePath->Buffer);
552 
553     return STATUS_SUCCESS;
554 }
555 
556 ERROR_NUMBER
LoadSetupInf(IN OUT PUSETUP_DATA pSetupData)557 LoadSetupInf(
558     IN OUT PUSETUP_DATA pSetupData)
559 {
560     INFCONTEXT Context;
561     UINT ErrorLine;
562     INT IntValue;
563     PCWSTR Value;
564     WCHAR FileNameBuffer[MAX_PATH];
565 
566     CombinePaths(FileNameBuffer, ARRAYSIZE(FileNameBuffer), 2,
567                  pSetupData->SourcePath.Buffer, L"txtsetup.sif");
568 
569     DPRINT("SetupInf path: '%S'\n", FileNameBuffer);
570 
571     pSetupData->SetupInf =
572         SpInfOpenInfFile(FileNameBuffer,
573                          NULL,
574                          INF_STYLE_WIN4,
575                          pSetupData->LanguageId,
576                          &ErrorLine);
577     if (pSetupData->SetupInf == INVALID_HANDLE_VALUE)
578         return ERROR_LOAD_TXTSETUPSIF;
579 
580     /* Open 'Version' section */
581     if (!SpInfFindFirstLine(pSetupData->SetupInf, L"Version", L"Signature", &Context))
582         return ERROR_CORRUPT_TXTSETUPSIF;
583 
584     /* Get pointer 'Signature' key */
585     if (!INF_GetData(&Context, NULL, &Value))
586         return ERROR_CORRUPT_TXTSETUPSIF;
587 
588     /* Check 'Signature' string */
589     if (_wcsicmp(Value, L"$ReactOS$") != 0 &&
590         _wcsicmp(Value, L"$Windows NT$") != 0)
591     {
592         INF_FreeData(Value);
593         return ERROR_SIGNATURE_TXTSETUPSIF;
594     }
595 
596     INF_FreeData(Value);
597 
598     /* Open 'DiskSpaceRequirements' section */
599     if (!SpInfFindFirstLine(pSetupData->SetupInf, L"DiskSpaceRequirements", L"FreeSysPartDiskSpace", &Context))
600         return ERROR_CORRUPT_TXTSETUPSIF;
601 
602     pSetupData->RequiredPartitionDiskSpace = ~0;
603 
604     /* Get the 'FreeSysPartDiskSpace' value */
605     if (!SpInfGetIntField(&Context, 1, &IntValue))
606         return ERROR_CORRUPT_TXTSETUPSIF;
607 
608     pSetupData->RequiredPartitionDiskSpace = (ULONG)IntValue;
609 
610     //
611     // Support "SetupSourceDevice" and "SetupSourcePath" in txtsetup.sif
612     // See CORE-9023
613     // Support for that should also be added in setupldr.
614     //
615 
616     /* Update the Setup Source paths */
617     if (SpInfFindFirstLine(pSetupData->SetupInf, L"SetupData", L"SetupSourceDevice", &Context))
618     {
619         /*
620          * Get optional pointer 'SetupSourceDevice' key, its presence
621          * will dictate whether we also need 'SetupSourcePath'.
622          */
623         if (INF_GetData(&Context, NULL, &Value))
624         {
625             /* Free the old source root path string and create the new one */
626             RtlFreeUnicodeString(&pSetupData->SourceRootPath);
627             RtlCreateUnicodeString(&pSetupData->SourceRootPath, Value);
628             INF_FreeData(Value);
629 
630             if (!SpInfFindFirstLine(pSetupData->SetupInf, L"SetupData", L"SetupSourcePath", &Context))
631             {
632                 /* The 'SetupSourcePath' value is mandatory! */
633                 return ERROR_CORRUPT_TXTSETUPSIF;
634             }
635 
636             /* Get pointer 'SetupSourcePath' key */
637             if (!INF_GetData(&Context, NULL, &Value))
638             {
639                 /* The 'SetupSourcePath' value is mandatory! */
640                 return ERROR_CORRUPT_TXTSETUPSIF;
641             }
642 
643             /* Free the old source path string and create the new one */
644             RtlFreeUnicodeString(&pSetupData->SourceRootDir);
645             RtlCreateUnicodeString(&pSetupData->SourceRootDir, Value);
646             INF_FreeData(Value);
647         }
648     }
649 
650     /* Search for 'DefaultPath' in the 'SetupData' section */
651     pSetupData->InstallationDirectory[0] = 0;
652     if (SpInfFindFirstLine(pSetupData->SetupInf, L"SetupData", L"DefaultPath", &Context))
653     {
654         /* Get pointer 'DefaultPath' key */
655         if (!INF_GetData(&Context, NULL, &Value))
656             return ERROR_CORRUPT_TXTSETUPSIF;
657 
658         RtlStringCchCopyW(pSetupData->InstallationDirectory,
659                           ARRAYSIZE(pSetupData->InstallationDirectory),
660                           Value);
661 
662         INF_FreeData(Value);
663     }
664 
665     return ERROR_SUCCESS;
666 }
667 
668 /**
669  * @brief   Find or set the active system partition.
670  **/
671 BOOLEAN
InitSystemPartition(_In_ PPARTLIST PartitionList,_In_ PPARTENTRY InstallPartition,_Out_ PPARTENTRY * pSystemPartition,_In_opt_ PFSVOL_CALLBACK FsVolCallback,_In_opt_ PVOID Context)672 InitSystemPartition(
673     /**/_In_ PPARTLIST PartitionList,       /* HACK HACK! */
674     /**/_In_ PPARTENTRY InstallPartition,   /* HACK HACK! */
675     /**/_Out_ PPARTENTRY* pSystemPartition, /* HACK HACK! */
676     _In_opt_ PFSVOL_CALLBACK FsVolCallback,
677     _In_opt_ PVOID Context)
678 {
679     FSVOL_OP Result;
680     PPARTENTRY SystemPartition;
681     PPARTENTRY OldActivePart;
682 
683     /*
684      * If we install on a fixed disk, try to find a supported system
685      * partition on the system. Otherwise if we install on a removable disk
686      * use the install partition as the system partition.
687      */
688     if (InstallPartition->DiskEntry->MediaType == FixedMedia)
689     {
690         SystemPartition = FindSupportedSystemPartition(PartitionList,
691                                                        FALSE,
692                                                        InstallPartition->DiskEntry,
693                                                        InstallPartition);
694         /* Use the original system partition as the old active partition hint */
695         OldActivePart = PartitionList->SystemPartition;
696 
697         if ( SystemPartition && PartitionList->SystemPartition &&
698             (SystemPartition != PartitionList->SystemPartition) )
699         {
700             DPRINT1("We are using a different system partition!!\n");
701 
702             Result = FsVolCallback(Context,
703                                    ChangeSystemPartition,
704                                    (ULONG_PTR)SystemPartition,
705                                    0);
706             if (Result != FSVOL_DOIT)
707                 return FALSE;
708         }
709     }
710     else // if (InstallPartition->DiskEntry->MediaType == RemovableMedia)
711     {
712         SystemPartition = InstallPartition;
713         /* Don't specify any old active partition hint */
714         OldActivePart = NULL;
715     }
716 
717     if (!SystemPartition)
718     {
719         FsVolCallback(Context,
720                       FSVOLNOTIFY_PARTITIONERROR,
721                       ERROR_SYSTEM_PARTITION_NOT_FOUND,
722                       0);
723         return FALSE;
724     }
725 
726     *pSystemPartition = SystemPartition;
727 
728     /*
729      * If the system partition can be created in some
730      * non-partitioned space, create it now.
731      */
732     if (!SystemPartition->IsPartitioned)
733     {
734         /* Automatically create the partition; it will be
735          * formatted later with default parameters */
736         // FIXME: Don't use the whole empty space, but a minimal size
737         // specified from the TXTSETUP.SIF or unattended setup.
738         CreatePartition(PartitionList,
739                         SystemPartition,
740                         0ULL,
741                         0);
742         ASSERT(SystemPartition->IsPartitioned);
743     }
744 
745     /* Set it as such */
746     if (!SetActivePartition(PartitionList, SystemPartition, OldActivePart))
747     {
748         DPRINT1("SetActivePartition(0x%p) failed?!\n", SystemPartition);
749         ASSERT(FALSE);
750     }
751 
752     /*
753      * In all cases, whether or not we are going to perform a formatting,
754      * we must perform a filesystem check of the system partition.
755      */
756     if (SystemPartition->Volume)
757         SystemPartition->Volume->NeedsCheck = TRUE;
758 
759     return TRUE;
760 }
761 
762 
763 #define IS_PATH_SEPARATOR(c)    ((c) == L'\\' || (c) == L'/')
764 
765 /**
766  * @brief
767  * Verify whether the given directory is suitable for ReactOS installation.
768  * Each path component must be a valid 8.3 name.
769  **/
770 BOOLEAN
IsValidInstallDirectory(_In_ PCWSTR InstallDir)771 IsValidInstallDirectory(
772     _In_ PCWSTR InstallDir)
773 {
774     PCWCH p;
775 
776     /* As with the NT installer, fail if the path is empty or "\\" */
777     p = InstallDir;
778     if (!*p || (IS_PATH_SEPARATOR(*p) && !*(p + 1)))
779         return FALSE;
780 
781     /* The path must contain only valid characters */
782     for (p = InstallDir; *p; ++p)
783     {
784         if (!IS_VALID_INSTALL_PATH_CHAR(*p))
785             return FALSE;
786     }
787 
788     /*
789      * Loop over each path component and verify that each is a valid 8.3 name.
790      */
791     for (p = InstallDir; *p;)
792     {
793         PCWSTR Path;
794         SIZE_T Length;
795         UNICODE_STRING Name;
796         BOOLEAN IsNameLegal, SpacesInName;
797 
798         /* Skip any first separator */
799         if (IS_PATH_SEPARATOR(*p))
800             ++p;
801 
802         /* Now skip past the path component until we reach the next separator */
803         Path = p;
804         while (*p && !IS_PATH_SEPARATOR(*p))
805             ++p;
806         if (p == Path)
807         {
808             /* Succeed if nothing else follows this separator; otherwise
809              * it's a separator and consecutive ones are not supported */
810             return (!*p);
811         }
812 
813         /* Calculate the path component length */
814         Length = p - Path;
815 
816         /* As with the NT installer, fail for '.' and '..';
817          * RtlIsNameLegalDOS8Dot3() would succeed otherwise */
818         if ((Length == 1 && *Path == '.') || (Length == 2 && *Path == '.' && *(Path + 1) == '.'))
819             return FALSE;
820 
821         /* As with the NT installer, allow _only ONE trailing_ dot in
822          * the path component (but not 2 or more), by reducing Length
823          * in that case; RtlIsNameLegalDOS8Dot3() would fail otherwise */
824         if (Length > 1 && *(p - 2) != L'.' && *(p - 1) == L'.')
825             --Length;
826 
827         if (Length == 0)
828             return FALSE;
829 
830         /* Verify that the path component is a valid 8.3 name */
831         // if (Length > 8+1+3)
832         //     return FALSE;
833         Name.Length = Name.MaximumLength = (USHORT)(Length * sizeof(WCHAR));
834         Name.Buffer = (PWCHAR)Path;
835         SpacesInName = FALSE;
836         IsNameLegal = RtlIsNameLegalDOS8Dot3(&Name, NULL, &SpacesInName);
837 
838         /* If it isn't legal or contain spaces, fail */
839         if (!IsNameLegal || SpacesInName)
840         {
841             DPRINT("'%wZ' is %s 8.3 filename %s spaces\n",
842                    &Name,
843                    (IsNameLegal ? "a valid" : "an invalid"),
844                    (SpacesInName ? "with" : "without"));
845             return FALSE;
846         }
847         /* Go to the next path component */
848     }
849 
850     return TRUE;
851 }
852 
853 
854 NTSTATUS
InitDestinationPaths(_Inout_ PUSETUP_DATA pSetupData,_In_ PCWSTR InstallationDir,_In_ PVOLENTRY Volume)855 InitDestinationPaths(
856     _Inout_ PUSETUP_DATA pSetupData,
857     _In_ PCWSTR InstallationDir,
858     _In_ PVOLENTRY Volume)
859 {
860     NTSTATUS Status;
861     PPARTENTRY PartEntry = Volume->PartEntry;
862     PDISKENTRY DiskEntry = PartEntry->DiskEntry;
863     WCHAR PathBuffer[RTL_NUMBER_OF_FIELD(VOLINFO, DeviceName) + 1];
864 
865     ASSERT(PartEntry->IsPartitioned && PartEntry->PartitionNumber != 0);
866 
867     /* Create 'pSetupData->DestinationRootPath' string */
868     RtlFreeUnicodeString(&pSetupData->DestinationRootPath);
869     Status = RtlStringCchPrintfW(PathBuffer, _countof(PathBuffer),
870                                  L"%s\\", Volume->Info.DeviceName);
871     if (!NT_SUCCESS(Status))
872     {
873         DPRINT1("RtlStringCchPrintfW() failed with status 0x%08lx\n", Status);
874         return Status;
875     }
876 
877     Status = RtlCreateUnicodeString(&pSetupData->DestinationRootPath, PathBuffer) ? STATUS_SUCCESS : STATUS_NO_MEMORY;
878 
879     if (!NT_SUCCESS(Status))
880     {
881         DPRINT1("RtlCreateUnicodeString() failed with status 0x%08lx\n", Status);
882         return Status;
883     }
884 
885     DPRINT("DestinationRootPath: %wZ\n", &pSetupData->DestinationRootPath);
886 
887     // FIXME! Which variable to choose?
888     if (!InstallationDir)
889         InstallationDir = pSetupData->InstallationDirectory;
890 
891 /** Equivalent of 'NTOS_INSTALLATION::SystemArcPath' **/
892     /* Create 'pSetupData->DestinationArcPath' */
893     RtlFreeUnicodeString(&pSetupData->DestinationArcPath);
894 
895     if (DiskEntry->MediaType == FixedMedia)
896     {
897         if (DiskEntry->BiosFound)
898         {
899 #if 1
900             Status = RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
901                              L"multi(0)disk(0)rdisk(%lu)partition(%lu)\\",
902                              DiskEntry->HwFixedDiskNumber,
903                              PartEntry->OnDiskPartitionNumber);
904 #else
905             Status = RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
906                              L"multi(%lu)disk(%lu)rdisk(%lu)partition(%lu)\\",
907                              DiskEntry->HwAdapterNumber,
908                              DiskEntry->HwControllerNumber,
909                              DiskEntry->HwFixedDiskNumber,
910                              PartEntry->OnDiskPartitionNumber);
911 #endif
912             DPRINT1("Fixed disk found by BIOS, using MULTI ARC path '%S'\n", PathBuffer);
913         }
914         else
915         {
916             Status = RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
917                              L"scsi(%u)disk(%u)rdisk(%u)partition(%lu)\\",
918                              DiskEntry->Port,
919                              DiskEntry->Bus,
920                              DiskEntry->Id,
921                              PartEntry->OnDiskPartitionNumber);
922             DPRINT1("Fixed disk not found by BIOS, using SCSI ARC path '%S'\n", PathBuffer);
923         }
924     }
925     else // if (DiskEntry->MediaType == RemovableMedia)
926     {
927 #if 1
928         Status = RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
929                          L"multi(0)disk(0)rdisk(%lu)partition(%lu)\\",
930                          0, 1);
931         DPRINT1("Removable disk, using MULTI ARC path '%S'\n", PathBuffer);
932 #else
933         Status = RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
934                          L"signature(%08x)disk(%u)rdisk(%u)partition(%lu)\\",
935                          DiskEntry->LayoutBuffer->Signature,
936                          DiskEntry->Bus,
937                          DiskEntry->Id,
938                          PartEntry->OnDiskPartitionNumber);
939         DPRINT1("Removable disk, using SIGNATURE ARC path '%S'\n", PathBuffer);
940 #endif
941     }
942 
943     if (!NT_SUCCESS(Status))
944     {
945         DPRINT1("RtlStringCchPrintfW() failed with status 0x%08lx\n", Status);
946         RtlFreeUnicodeString(&pSetupData->DestinationRootPath);
947         return Status;
948     }
949 
950     Status = ConcatPaths(PathBuffer, ARRAYSIZE(PathBuffer), 1, InstallationDir);
951 
952     if (!NT_SUCCESS(Status))
953     {
954         DPRINT1("ConcatPaths() failed with status 0x%08lx\n", Status);
955         RtlFreeUnicodeString(&pSetupData->DestinationRootPath);
956         return Status;
957     }
958 
959     Status = RtlCreateUnicodeString(&pSetupData->DestinationArcPath, PathBuffer) ? STATUS_SUCCESS : STATUS_NO_MEMORY;
960 
961     if (!NT_SUCCESS(Status))
962     {
963         DPRINT1("RtlCreateUnicodeString() failed with status 0x%08lx\n", Status);
964         RtlFreeUnicodeString(&pSetupData->DestinationRootPath);
965         return Status;
966     }
967 
968 /** Equivalent of 'NTOS_INSTALLATION::SystemNtPath' **/
969     /* Create 'pSetupData->DestinationPath' string */
970     RtlFreeUnicodeString(&pSetupData->DestinationPath);
971     Status = CombinePaths(PathBuffer, ARRAYSIZE(PathBuffer), 2,
972                           pSetupData->DestinationRootPath.Buffer, InstallationDir);
973 
974     if (!NT_SUCCESS(Status))
975     {
976         DPRINT1("CombinePaths() failed with status 0x%08lx\n", Status);
977         RtlFreeUnicodeString(&pSetupData->DestinationArcPath);
978         RtlFreeUnicodeString(&pSetupData->DestinationRootPath);
979         return Status;
980     }
981 
982     Status = RtlCreateUnicodeString(&pSetupData->DestinationPath, PathBuffer) ? STATUS_SUCCESS : STATUS_NO_MEMORY;
983 
984     if (!NT_SUCCESS(Status))
985     {
986         DPRINT1("RtlCreateUnicodeString() failed with status 0x%08lx\n", Status);
987         RtlFreeUnicodeString(&pSetupData->DestinationArcPath);
988         RtlFreeUnicodeString(&pSetupData->DestinationRootPath);
989         return Status;
990     }
991 
992 /** Equivalent of 'NTOS_INSTALLATION::PathComponent' **/
993     // FIXME: This is only temporary!! Must be removed later!
994     Status = RtlCreateUnicodeString(&pSetupData->InstallPath, InstallationDir) ? STATUS_SUCCESS : STATUS_NO_MEMORY;
995 
996     if (!NT_SUCCESS(Status))
997     {
998         DPRINT1("RtlCreateUnicodeString() failed with status 0x%08lx\n", Status);
999         RtlFreeUnicodeString(&pSetupData->DestinationPath);
1000         RtlFreeUnicodeString(&pSetupData->DestinationArcPath);
1001         RtlFreeUnicodeString(&pSetupData->DestinationRootPath);
1002         return Status;
1003     }
1004 
1005     return STATUS_SUCCESS;
1006 }
1007 
1008 // NTSTATUS
1009 ERROR_NUMBER
InitializeSetup(IN OUT PUSETUP_DATA pSetupData,IN ULONG InitPhase)1010 InitializeSetup(
1011     IN OUT PUSETUP_DATA pSetupData,
1012     IN ULONG InitPhase)
1013 {
1014     if (InitPhase == 0)
1015     {
1016         RtlZeroMemory(pSetupData, sizeof(*pSetupData));
1017 
1018         /* Initialize error handling */
1019         pSetupData->LastErrorNumber = ERROR_SUCCESS;
1020         pSetupData->ErrorRoutine = NULL;
1021 
1022         /* Initialize global unicode strings */
1023         RtlInitUnicodeString(&pSetupData->SourcePath, NULL);
1024         RtlInitUnicodeString(&pSetupData->SourceRootPath, NULL);
1025         RtlInitUnicodeString(&pSetupData->SourceRootDir, NULL);
1026         RtlInitUnicodeString(&pSetupData->DestinationArcPath, NULL);
1027         RtlInitUnicodeString(&pSetupData->DestinationPath, NULL);
1028         RtlInitUnicodeString(&pSetupData->DestinationRootPath, NULL);
1029         RtlInitUnicodeString(&pSetupData->SystemRootPath, NULL);
1030 
1031         // FIXME: This is only temporary!! Must be removed later!
1032         /***/RtlInitUnicodeString(&pSetupData->InstallPath, NULL);/***/
1033 
1034         //
1035         // TODO: Load and start SetupDD, and ask it for the information
1036         //
1037 
1038         return ERROR_SUCCESS;
1039     }
1040     else
1041     if (InitPhase == 1)
1042     {
1043         ERROR_NUMBER Error;
1044         NTSTATUS Status;
1045 
1046         /* Get the source path and source root path */
1047         Status = GetSourcePaths(&pSetupData->SourcePath,
1048                                 &pSetupData->SourceRootPath,
1049                                 &pSetupData->SourceRootDir);
1050         if (!NT_SUCCESS(Status))
1051         {
1052             DPRINT1("GetSourcePaths() failed (Status 0x%08lx)\n", Status);
1053             return ERROR_NO_SOURCE_DRIVE;
1054         }
1055         DPRINT1("SourcePath (1): '%wZ'\n", &pSetupData->SourcePath);
1056         DPRINT1("SourceRootPath (1): '%wZ'\n", &pSetupData->SourceRootPath);
1057         DPRINT1("SourceRootDir (1): '%wZ'\n", &pSetupData->SourceRootDir);
1058 
1059         /* Set up default values */
1060         pSetupData->DestinationDiskNumber = 0;
1061         pSetupData->DestinationPartitionNumber = 1;
1062         pSetupData->BootLoaderLocation = 2; // Default to "System partition"
1063         pSetupData->FormatPartition = 0;
1064         pSetupData->AutoPartition = 0;
1065         pSetupData->FsType = 0;
1066 
1067         /* Load 'txtsetup.sif' from the installation media */
1068         Error = LoadSetupInf(pSetupData);
1069         if (Error != ERROR_SUCCESS)
1070         {
1071             DPRINT1("LoadSetupInf() failed (Error 0x%lx)\n", Error);
1072             return Error;
1073         }
1074         DPRINT1("SourcePath (2): '%wZ'\n", &pSetupData->SourcePath);
1075         DPRINT1("SourceRootPath (2): '%wZ'\n", &pSetupData->SourceRootPath);
1076         DPRINT1("SourceRootDir (2): '%wZ'\n", &pSetupData->SourceRootDir);
1077 
1078         /* Retrieve the target machine architecture type */
1079         // FIXME: This should be determined at runtime!!
1080         // FIXME: Allow for (pre-)installing on an architecture
1081         //        different from the current one?
1082 #if defined(SARCH_XBOX)
1083         pSetupData->ArchType = ARCH_Xbox;
1084 // #elif defined(SARCH_PC98)
1085 #else // TODO: Arc, UEFI
1086         pSetupData->ArchType = (IsNEC_98 ? ARCH_NEC98x86 : ARCH_PcAT);
1087 #endif
1088 
1089         return ERROR_SUCCESS;
1090     }
1091 
1092     return ERROR_SUCCESS;
1093 }
1094 
1095 VOID
FinishSetup(IN OUT PUSETUP_DATA pSetupData)1096 FinishSetup(
1097     IN OUT PUSETUP_DATA pSetupData)
1098 {
1099     /* Destroy the computer settings list */
1100     if (pSetupData->ComputerList != NULL)
1101     {
1102         DestroyGenericList(pSetupData->ComputerList, TRUE);
1103         pSetupData->ComputerList = NULL;
1104     }
1105 
1106     /* Destroy the display settings list */
1107     if (pSetupData->DisplayList != NULL)
1108     {
1109         DestroyGenericList(pSetupData->DisplayList, TRUE);
1110         pSetupData->DisplayList = NULL;
1111     }
1112 
1113     /* Destroy the keyboard settings list */
1114     if (pSetupData->KeyboardList != NULL)
1115     {
1116         DestroyGenericList(pSetupData->KeyboardList, TRUE);
1117         pSetupData->KeyboardList = NULL;
1118     }
1119 
1120     /* Destroy the keyboard layout list */
1121     if (pSetupData->LayoutList != NULL)
1122     {
1123         DestroyGenericList(pSetupData->LayoutList, TRUE);
1124         pSetupData->LayoutList = NULL;
1125     }
1126 
1127     /* Destroy the languages list */
1128     if (pSetupData->LanguageList != NULL)
1129     {
1130         DestroyGenericList(pSetupData->LanguageList, FALSE);
1131         pSetupData->LanguageList = NULL;
1132     }
1133 
1134     /* Close the Setup INF */
1135     SpInfCloseInfFile(pSetupData->SetupInf);
1136 }
1137 
1138 /*
1139  * SIDEEFFECTS
1140  *  Calls RegInitializeRegistry
1141  *  Calls ImportRegistryFile
1142  *  Calls SetDefaultPagefile
1143  *  Calls SetMountedDeviceValues
1144  */
1145 ERROR_NUMBER
UpdateRegistry(IN OUT PUSETUP_DATA pSetupData,IN BOOLEAN RepairUpdateFlag,IN PPARTLIST PartitionList,IN WCHAR DestinationDriveLetter,IN PCWSTR SelectedLanguageId,IN PREGISTRY_STATUS_ROUTINE StatusRoutine OPTIONAL,IN PFONTSUBSTSETTINGS SubstSettings OPTIONAL)1146 UpdateRegistry(
1147     IN OUT PUSETUP_DATA pSetupData,
1148     /**/IN BOOLEAN RepairUpdateFlag,     /* HACK HACK! */
1149     /**/IN PPARTLIST PartitionList,      /* HACK HACK! */
1150     /**/IN WCHAR DestinationDriveLetter, /* HACK HACK! */
1151     /**/IN PCWSTR SelectedLanguageId,    /* HACK HACK! */
1152     IN PREGISTRY_STATUS_ROUTINE StatusRoutine OPTIONAL,
1153     IN PFONTSUBSTSETTINGS SubstSettings OPTIONAL)
1154 {
1155     ERROR_NUMBER ErrorNumber;
1156     NTSTATUS Status;
1157     INFCONTEXT InfContext;
1158     PCWSTR Action;
1159     PCWSTR File;
1160     PCWSTR Section;
1161     BOOLEAN Success;
1162     BOOLEAN ShouldRepairRegistry = FALSE;
1163     BOOLEAN Delete;
1164 
1165     if (RepairUpdateFlag)
1166     {
1167         DPRINT1("TODO: Updating / repairing the registry is not completely implemented yet!\n");
1168 
1169         /* Verify the registry hives and check whether we need to update or repair any of them */
1170         Status = VerifyRegistryHives(&pSetupData->DestinationPath, &ShouldRepairRegistry);
1171         if (!NT_SUCCESS(Status))
1172         {
1173             DPRINT1("VerifyRegistryHives failed, Status 0x%08lx\n", Status);
1174             ShouldRepairRegistry = FALSE;
1175         }
1176         if (!ShouldRepairRegistry)
1177             DPRINT1("No need to repair the registry\n");
1178     }
1179 
1180 DoUpdate:
1181     ErrorNumber = ERROR_SUCCESS;
1182 
1183     /* Update the registry */
1184     if (StatusRoutine) StatusRoutine(RegHiveUpdate);
1185 
1186     /* Initialize the registry and setup the registry hives */
1187     Status = RegInitializeRegistry(&pSetupData->DestinationPath);
1188     if (!NT_SUCCESS(Status))
1189     {
1190         DPRINT1("RegInitializeRegistry() failed\n");
1191         /********** HACK!!!!!!!!!!! **********/
1192         if (Status == STATUS_NOT_IMPLEMENTED)
1193         {
1194             /* The hack was called, return its corresponding error */
1195             return ERROR_INITIALIZE_REGISTRY;
1196         }
1197         else
1198         /*************************************/
1199         {
1200             /* Something else failed */
1201             return ERROR_CREATE_HIVE;
1202         }
1203     }
1204 
1205     if (!RepairUpdateFlag || ShouldRepairRegistry)
1206     {
1207         /*
1208          * We fully setup the hives, in case we are doing a fresh installation
1209          * (RepairUpdateFlag == FALSE), or in case we are doing an update
1210          * (RepairUpdateFlag == TRUE) BUT we have some registry hives to
1211          * "repair" (aka. recreate: ShouldRepairRegistry == TRUE).
1212          */
1213 
1214         Success = SpInfFindFirstLine(pSetupData->SetupInf, L"HiveInfs.Fresh", NULL, &InfContext);       // Windows-compatible
1215         if (!Success)
1216             Success = SpInfFindFirstLine(pSetupData->SetupInf, L"HiveInfs.Install", NULL, &InfContext); // ReactOS-specific
1217 
1218         if (!Success)
1219         {
1220             DPRINT1("SpInfFindFirstLine() failed\n");
1221             ErrorNumber = ERROR_FIND_REGISTRY;
1222             goto Cleanup;
1223         }
1224     }
1225     else // if (RepairUpdateFlag && !ShouldRepairRegistry)
1226     {
1227         /*
1228          * In case we are doing an update (RepairUpdateFlag == TRUE) and
1229          * NO registry hives need a repair (ShouldRepairRegistry == FALSE),
1230          * we only update the hives.
1231          */
1232 
1233         Success = SpInfFindFirstLine(pSetupData->SetupInf, L"HiveInfs.Upgrade", NULL, &InfContext);
1234         if (!Success)
1235         {
1236             /* Nothing to do for update! */
1237             DPRINT1("No update needed for the registry!\n");
1238             goto Cleanup;
1239         }
1240     }
1241 
1242     do
1243     {
1244         INF_GetDataField(&InfContext, 0, &Action);
1245         INF_GetDataField(&InfContext, 1, &File);
1246         INF_GetDataField(&InfContext, 2, &Section);
1247 
1248         DPRINT("Action: %S  File: %S  Section %S\n", Action, File, Section);
1249 
1250         if (Action == NULL)
1251         {
1252             INF_FreeData(Action);
1253             INF_FreeData(File);
1254             INF_FreeData(Section);
1255             break; // Hackfix
1256         }
1257 
1258         if (!_wcsicmp(Action, L"AddReg"))
1259             Delete = FALSE;
1260         else if (!_wcsicmp(Action, L"DelReg"))
1261             Delete = TRUE;
1262         else
1263         {
1264             DPRINT1("Unrecognized registry INF action '%S'\n", Action);
1265             INF_FreeData(Action);
1266             INF_FreeData(File);
1267             INF_FreeData(Section);
1268             continue;
1269         }
1270 
1271         INF_FreeData(Action);
1272 
1273         if (StatusRoutine) StatusRoutine(ImportRegHive, File);
1274 
1275         if (!ImportRegistryFile(pSetupData->SourcePath.Buffer,
1276                                 File, Section,
1277                                 pSetupData->LanguageId, Delete))
1278         {
1279             DPRINT1("Importing %S failed\n", File);
1280             INF_FreeData(File);
1281             INF_FreeData(Section);
1282             ErrorNumber = ERROR_IMPORT_HIVE;
1283             goto Cleanup;
1284         }
1285     } while (SpInfFindNextLine(&InfContext, &InfContext));
1286 
1287     if (!RepairUpdateFlag || ShouldRepairRegistry)
1288     {
1289         /* See the explanation for this test above */
1290 
1291         PGENERIC_LIST_ENTRY Entry;
1292         PCWSTR LanguageId; // LocaleID;
1293 
1294         Entry = GetCurrentListEntry(pSetupData->DisplayList);
1295         ASSERT(Entry);
1296         pSetupData->DisplayType = ((PGENENTRY)GetListEntryData(Entry))->Id;
1297         ASSERT(pSetupData->DisplayType);
1298 
1299         /* Update display registry settings */
1300         if (StatusRoutine) StatusRoutine(DisplaySettingsUpdate);
1301         if (!ProcessDisplayRegistry(pSetupData->SetupInf, pSetupData->DisplayType))
1302         {
1303             ErrorNumber = ERROR_UPDATE_DISPLAY_SETTINGS;
1304             goto Cleanup;
1305         }
1306 
1307         Entry = GetCurrentListEntry(pSetupData->LanguageList);
1308         ASSERT(Entry);
1309         LanguageId = ((PGENENTRY)GetListEntryData(Entry))->Id;
1310         ASSERT(LanguageId);
1311 
1312         /* Set the locale */
1313         if (StatusRoutine) StatusRoutine(LocaleSettingsUpdate);
1314         if (!ProcessLocaleRegistry(/*pSetupData->*/LanguageId))
1315         {
1316             ErrorNumber = ERROR_UPDATE_LOCALESETTINGS;
1317             goto Cleanup;
1318         }
1319 
1320         /* Add the keyboard layouts for the given language (without user override) */
1321         if (StatusRoutine) StatusRoutine(KeybLayouts);
1322         if (!AddKeyboardLayouts(SelectedLanguageId))
1323         {
1324             ErrorNumber = ERROR_ADDING_KBLAYOUTS;
1325             goto Cleanup;
1326         }
1327 
1328         if (!IsUnattendedSetup)
1329         {
1330             Entry = GetCurrentListEntry(pSetupData->LayoutList);
1331             ASSERT(Entry);
1332             pSetupData->LayoutId = ((PGENENTRY)GetListEntryData(Entry))->Id;
1333             ASSERT(pSetupData->LayoutId);
1334 
1335             /* Update keyboard layout settings with user-overridden values */
1336             // FIXME: Wouldn't it be better to do it all at once
1337             // with the AddKeyboardLayouts() step?
1338             if (StatusRoutine) StatusRoutine(KeybSettingsUpdate);
1339             if (!ProcessKeyboardLayoutRegistry(pSetupData->LayoutId, SelectedLanguageId))
1340             {
1341                 ErrorNumber = ERROR_UPDATE_KBSETTINGS;
1342                 goto Cleanup;
1343             }
1344         }
1345 
1346         /* Set GeoID */
1347         if (!SetGeoID(MUIGetGeoID(SelectedLanguageId)))
1348         {
1349             ErrorNumber = ERROR_UPDATE_GEOID;
1350             goto Cleanup;
1351         }
1352 
1353         /* Add codepage information to registry */
1354         if (StatusRoutine) StatusRoutine(CodePageInfoUpdate);
1355         if (!AddCodePage(SelectedLanguageId))
1356         {
1357             ErrorNumber = ERROR_ADDING_CODEPAGE;
1358             goto Cleanup;
1359         }
1360 
1361         /* Set the default pagefile entry */
1362         SetDefaultPagefile(DestinationDriveLetter);
1363 
1364         /* Update the mounted devices list */
1365         // FIXME: This should technically be done by mountmgr (if AutoMount is enabled)!
1366         SetMountedDeviceValues(PartitionList);
1367     }
1368 
1369 #ifdef __REACTOS__
1370     if (SubstSettings)
1371     {
1372         /* HACK */
1373         DoRegistryFontFixup(SubstSettings, wcstoul(SelectedLanguageId, NULL, 16));
1374     }
1375 #endif
1376 
1377 Cleanup:
1378     //
1379     // TODO: Unload all the registry stuff, perform cleanup,
1380     // and copy the created hive files into .sav files.
1381     //
1382     RegCleanupRegistry(&pSetupData->DestinationPath);
1383 
1384     /*
1385      * Check whether we were in update/repair mode but we were actually
1386      * repairing the registry hives. If so, we have finished repairing them,
1387      * and we now reset the flag and run the proper registry update.
1388      * Otherwise we have finished the registry update!
1389      */
1390     if (RepairUpdateFlag && ShouldRepairRegistry)
1391     {
1392         ShouldRepairRegistry = FALSE;
1393         goto DoUpdate;
1394     }
1395 
1396     return ErrorNumber;
1397 }
1398 
1399 /* EOF */
1400