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