cb48fd3fa36ced8ffc87741ed46918dfceee341c
[reactos.git] / base / setup / lib / setuplib.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Setup Library
4 * FILE: base/setup/lib/setuplib.c
5 * PURPOSE: Setup Library - Main initialization helpers
6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
8 */
9
10 /* INCLUDES *****************************************************************/
11
12 #include "precomp.h"
13 #include "filesup.h"
14 #include "infsupp.h"
15 #include "inicache.h"
16
17 #include "setuplib.h"
18
19 #define NDEBUG
20 #include <debug.h>
21
22
23 /* GLOBALS ******************************************************************/
24
25 /* FUNCTIONS ****************************************************************/
26
27 VOID
28 CheckUnattendedSetup(
29 IN OUT PUSETUP_DATA pSetupData)
30 {
31 INFCONTEXT Context;
32 HINF UnattendInf;
33 UINT ErrorLine;
34 INT IntValue;
35 PWCHAR 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 = SetupOpenInfFileExW(UnattendInfPath,
51 NULL,
52 INF_STYLE_OLDNT,
53 pSetupData->LanguageId,
54 &ErrorLine);
55
56 if (UnattendInf == INVALID_HANDLE_VALUE)
57 {
58 DPRINT("SetupOpenInfFileExW() failed\n");
59 return;
60 }
61
62 /* Open 'Unattend' section */
63 if (!SetupFindFirstLineW(UnattendInf, L"Unattend", L"Signature", &Context))
64 {
65 DPRINT("SetupFindFirstLineW() failed for section 'Unattend'\n");
66 goto Quit;
67 }
68
69 /* Get pointer 'Signature' key */
70 if (!INF_GetData(&Context, NULL, &Value))
71 {
72 DPRINT("INF_GetData() failed for key 'Signature'\n");
73 goto Quit;
74 }
75
76 /* Check 'Signature' string */
77 if (_wcsicmp(Value, L"$ReactOS$") != 0)
78 {
79 DPRINT("Signature not $ReactOS$\n");
80 INF_FreeData(Value);
81 goto Quit;
82 }
83
84 INF_FreeData(Value);
85
86 /* Check if Unattend setup is enabled */
87 if (!SetupFindFirstLineW(UnattendInf, L"Unattend", L"UnattendSetupEnabled", &Context))
88 {
89 DPRINT("Can't find key 'UnattendSetupEnabled'\n");
90 goto Quit;
91 }
92
93 if (!INF_GetData(&Context, NULL, &Value))
94 {
95 DPRINT("Can't read key 'UnattendSetupEnabled'\n");
96 goto Quit;
97 }
98
99 if (_wcsicmp(Value, L"yes") != 0)
100 {
101 DPRINT("Unattend setup is disabled by 'UnattendSetupEnabled' key!\n");
102 INF_FreeData(Value);
103 goto Quit;
104 }
105
106 INF_FreeData(Value);
107
108 /* Search for 'DestinationDiskNumber' in the 'Unattend' section */
109 if (!SetupFindFirstLineW(UnattendInf, L"Unattend", L"DestinationDiskNumber", &Context))
110 {
111 DPRINT("SetupFindFirstLine() failed for key 'DestinationDiskNumber'\n");
112 goto Quit;
113 }
114
115 if (!SetupGetIntField(&Context, 1, &IntValue))
116 {
117 DPRINT("SetupGetIntField() failed for key 'DestinationDiskNumber'\n");
118 goto Quit;
119 }
120
121 pSetupData->DestinationDiskNumber = (LONG)IntValue;
122
123 /* Search for 'DestinationPartitionNumber' in the 'Unattend' section */
124 if (!SetupFindFirstLineW(UnattendInf, L"Unattend", L"DestinationPartitionNumber", &Context))
125 {
126 DPRINT("SetupFindFirstLine() failed for key 'DestinationPartitionNumber'\n");
127 goto Quit;
128 }
129
130 if (!SetupGetIntField(&Context, 1, &IntValue))
131 {
132 DPRINT("SetupGetIntField() failed for key 'DestinationPartitionNumber'\n");
133 goto Quit;
134 }
135
136 pSetupData->DestinationPartitionNumber = (LONG)IntValue;
137
138 /* Search for 'InstallationDirectory' in the 'Unattend' section (optional) */
139 if (SetupFindFirstLineW(UnattendInf, L"Unattend", L"InstallationDirectory", &Context))
140 {
141 /* Get pointer 'InstallationDirectory' key */
142 if (!INF_GetData(&Context, NULL, &Value))
143 {
144 DPRINT("INF_GetData() failed for key 'InstallationDirectory'\n");
145 goto Quit;
146 }
147
148 RtlStringCchCopyW(pSetupData->InstallationDirectory,
149 ARRAYSIZE(pSetupData->InstallationDirectory),
150 Value);
151
152 INF_FreeData(Value);
153 }
154
155 IsUnattendedSetup = TRUE;
156 DPRINT("Running unattended setup\n");
157
158 /* Search for 'MBRInstallType' in the 'Unattend' section */
159 pSetupData->MBRInstallType = -1;
160 if (SetupFindFirstLineW(UnattendInf, L"Unattend", L"MBRInstallType", &Context))
161 {
162 if (SetupGetIntField(&Context, 1, &IntValue))
163 {
164 pSetupData->MBRInstallType = IntValue;
165 }
166 }
167
168 /* Search for 'FormatPartition' in the 'Unattend' section */
169 pSetupData->FormatPartition = 0;
170 if (SetupFindFirstLineW(UnattendInf, L"Unattend", L"FormatPartition", &Context))
171 {
172 if (SetupGetIntField(&Context, 1, &IntValue))
173 {
174 pSetupData->FormatPartition = IntValue;
175 }
176 }
177
178 pSetupData->AutoPartition = 0;
179 if (SetupFindFirstLineW(UnattendInf, L"Unattend", L"AutoPartition", &Context))
180 {
181 if (SetupGetIntField(&Context, 1, &IntValue))
182 {
183 pSetupData->AutoPartition = IntValue;
184 }
185 }
186
187 /* Search for LocaleID in the 'Unattend' section */
188 if (SetupFindFirstLineW(UnattendInf, L"Unattend", L"LocaleID", &Context))
189 {
190 if (INF_GetData(&Context, NULL, &Value))
191 {
192 LONG Id = wcstol(Value, NULL, 16);
193 RtlStringCchPrintfW(pSetupData->LocaleID,
194 ARRAYSIZE(pSetupData->LocaleID),
195 L"%08lx", Id);
196 INF_FreeData(Value);
197 }
198 }
199
200 Quit:
201 SetupCloseInfFile(UnattendInf);
202 }
203
204 VOID
205 InstallSetupInfFile(
206 IN OUT PUSETUP_DATA pSetupData)
207 {
208 NTSTATUS Status;
209 PINICACHE IniCache;
210
211 #if 0 // HACK FIXME!
212 PINICACHE UnattendCache;
213 PINICACHEITERATOR Iterator;
214 #else
215 // WCHAR CrLf[] = {L'\r', L'\n'};
216 CHAR CrLf[] = {'\r', '\n'};
217 HANDLE FileHandle, UnattendFileHandle, SectionHandle;
218 FILE_STANDARD_INFORMATION FileInfo;
219 ULONG FileSize;
220 PVOID ViewBase;
221 UNICODE_STRING FileName;
222 OBJECT_ATTRIBUTES ObjectAttributes;
223 IO_STATUS_BLOCK IoStatusBlock;
224 #endif
225
226 PINICACHESECTION IniSection;
227 WCHAR PathBuffer[MAX_PATH];
228 WCHAR UnattendInfPath[MAX_PATH];
229
230 /* Create a $winnt$.inf file with default entries */
231 IniCache = IniCacheCreate();
232 if (!IniCache)
233 return;
234
235 IniSection = IniCacheAppendSection(IniCache, L"SetupParams");
236 if (IniSection)
237 {
238 /* Key "skipmissingfiles" */
239 // RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
240 // L"\"%s\"", L"WinNt5.2");
241 // IniCacheInsertKey(IniSection, NULL, INSERT_LAST,
242 // L"Version", PathBuffer);
243 }
244
245 IniSection = IniCacheAppendSection(IniCache, L"Data");
246 if (IniSection)
247 {
248 RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
249 L"\"%s\"", IsUnattendedSetup ? L"yes" : L"no");
250 IniCacheInsertKey(IniSection, NULL, INSERT_LAST,
251 L"UnattendedInstall", PathBuffer);
252
253 // "floppylessbootpath" (yes/no)
254
255 RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
256 L"\"%s\"", L"winnt");
257 IniCacheInsertKey(IniSection, NULL, INSERT_LAST,
258 L"ProductType", PathBuffer);
259
260 RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
261 L"\"%s\\\"", pSetupData->SourceRootPath.Buffer);
262 IniCacheInsertKey(IniSection, NULL, INSERT_LAST,
263 L"SourcePath", PathBuffer);
264
265 // "floppyless" ("0")
266 }
267
268 #if 0
269
270 /* TODO: Append the standard unattend.inf file */
271 CombinePaths(UnattendInfPath, ARRAYSIZE(UnattendInfPath), 2,
272 pSetupData->SourcePath.Buffer, L"unattend.inf");
273 if (DoesFileExist(NULL, UnattendInfPath) == FALSE)
274 {
275 DPRINT("Does not exist: %S\n", UnattendInfPath);
276 goto Quit;
277 }
278
279 Status = IniCacheLoad(&UnattendCache, UnattendInfPath, FALSE);
280 if (!NT_SUCCESS(Status))
281 {
282 DPRINT1("Cannot load %S as an INI file!\n", UnattendInfPath);
283 goto Quit;
284 }
285
286 IniCacheDestroy(UnattendCache);
287
288 Quit:
289 CombinePaths(PathBuffer, ARRAYSIZE(PathBuffer), 2,
290 pSetupData->DestinationPath.Buffer, L"System32\\$winnt$.inf");
291 IniCacheSave(IniCache, PathBuffer);
292 IniCacheDestroy(IniCache);
293
294 #else
295
296 CombinePaths(PathBuffer, ARRAYSIZE(PathBuffer), 2,
297 pSetupData->DestinationPath.Buffer, L"System32\\$winnt$.inf");
298 IniCacheSave(IniCache, PathBuffer);
299 IniCacheDestroy(IniCache);
300
301 /* TODO: Append the standard unattend.inf file */
302 CombinePaths(UnattendInfPath, ARRAYSIZE(UnattendInfPath), 2,
303 pSetupData->SourcePath.Buffer, L"unattend.inf");
304 if (DoesFileExist(NULL, UnattendInfPath) == FALSE)
305 {
306 DPRINT("Does not exist: %S\n", UnattendInfPath);
307 return;
308 }
309
310 RtlInitUnicodeString(&FileName, PathBuffer);
311 InitializeObjectAttributes(&ObjectAttributes,
312 &FileName,
313 OBJ_CASE_INSENSITIVE | OBJ_OPENIF,
314 NULL,
315 NULL);
316 Status = NtOpenFile(&FileHandle,
317 FILE_APPEND_DATA | SYNCHRONIZE,
318 &ObjectAttributes,
319 &IoStatusBlock,
320 FILE_SHARE_READ,
321 FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE);
322 if (!NT_SUCCESS(Status))
323 {
324 DPRINT1("Cannot load %S as an INI file!\n", PathBuffer);
325 return;
326 }
327
328 /* Query the file size */
329 Status = NtQueryInformationFile(FileHandle,
330 &IoStatusBlock,
331 &FileInfo,
332 sizeof(FileInfo),
333 FileStandardInformation);
334 if (!NT_SUCCESS(Status))
335 {
336 DPRINT("NtQueryInformationFile() failed (Status %lx)\n", Status);
337 FileInfo.EndOfFile.QuadPart = 0ULL;
338 }
339
340 Status = OpenAndMapFile(NULL,
341 UnattendInfPath,
342 &UnattendFileHandle,
343 &SectionHandle,
344 &ViewBase,
345 &FileSize,
346 FALSE);
347 if (!NT_SUCCESS(Status))
348 {
349 DPRINT1("Cannot load %S !\n", UnattendInfPath);
350 NtClose(FileHandle);
351 return;
352 }
353
354 /* Write to the INI file */
355
356 /* "\r\n" */
357 Status = NtWriteFile(FileHandle,
358 NULL,
359 NULL,
360 NULL,
361 &IoStatusBlock,
362 (PVOID)CrLf,
363 sizeof(CrLf),
364 &FileInfo.EndOfFile,
365 NULL);
366
367 Status = NtWriteFile(FileHandle,
368 NULL,
369 NULL,
370 NULL,
371 &IoStatusBlock,
372 ViewBase,
373 FileSize,
374 NULL,
375 NULL);
376 if (!NT_SUCCESS(Status))
377 {
378 DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
379 }
380
381 /* Finally, unmap and close the file */
382 UnMapAndCloseFile(UnattendFileHandle, SectionHandle, ViewBase);
383
384 NtClose(FileHandle);
385 #endif
386 }
387
388 NTSTATUS
389 GetSourcePaths(
390 OUT PUNICODE_STRING SourcePath,
391 OUT PUNICODE_STRING SourceRootPath,
392 OUT PUNICODE_STRING SourceRootDir)
393 {
394 NTSTATUS Status;
395 HANDLE Handle;
396 OBJECT_ATTRIBUTES ObjectAttributes;
397 UCHAR ImageFileBuffer[sizeof(UNICODE_STRING) + MAX_PATH * sizeof(WCHAR)];
398 PUNICODE_STRING InstallSourcePath = (PUNICODE_STRING)&ImageFileBuffer;
399 WCHAR SystemRootBuffer[MAX_PATH] = L"";
400 UNICODE_STRING SystemRootPath = RTL_CONSTANT_STRING(L"\\SystemRoot");
401 ULONG BufferSize;
402 PWCHAR Ptr;
403
404 /* Determine the installation source path via the full path of the installer */
405 RtlInitEmptyUnicodeString(InstallSourcePath,
406 (PWSTR)((ULONG_PTR)ImageFileBuffer + sizeof(UNICODE_STRING)),
407 sizeof(ImageFileBuffer) - sizeof(UNICODE_STRING)
408 /* Reserve space for a NULL terminator */ - sizeof(UNICODE_NULL));
409 BufferSize = sizeof(ImageFileBuffer);
410 Status = NtQueryInformationProcess(NtCurrentProcess(),
411 ProcessImageFileName,
412 InstallSourcePath,
413 BufferSize,
414 NULL);
415 // STATUS_INFO_LENGTH_MISMATCH or STATUS_BUFFER_TOO_SMALL ?
416 if (!NT_SUCCESS(Status))
417 return Status;
418
419 /* Manually NULL-terminate */
420 InstallSourcePath->Buffer[InstallSourcePath->Length / sizeof(WCHAR)] = UNICODE_NULL;
421
422 /* Strip the trailing file name */
423 Ptr = wcsrchr(InstallSourcePath->Buffer, OBJ_NAME_PATH_SEPARATOR);
424 if (Ptr)
425 *Ptr = UNICODE_NULL;
426 InstallSourcePath->Length = wcslen(InstallSourcePath->Buffer) * sizeof(WCHAR);
427
428
429 /*
430 * Now resolve the full path to \SystemRoot. In case it prefixes
431 * the installation source path determined from the full path of
432 * the installer, we use instead the resolved \SystemRoot as the
433 * installation source path.
434 * Otherwise, we use instead the path from the full installer path.
435 */
436
437 InitializeObjectAttributes(&ObjectAttributes,
438 &SystemRootPath,
439 OBJ_CASE_INSENSITIVE,
440 NULL,
441 NULL);
442
443 Status = NtOpenSymbolicLinkObject(&Handle,
444 SYMBOLIC_LINK_QUERY,
445 &ObjectAttributes);
446 if (!NT_SUCCESS(Status))
447 {
448 /*
449 * We failed at opening the \SystemRoot link (usually due to wrong
450 * access rights). Do not consider this as a fatal error, but use
451 * instead the image file path as the installation source path.
452 */
453 DPRINT1("NtOpenSymbolicLinkObject(%wZ) failed with Status 0x%08lx\n",
454 &SystemRootPath, Status);
455 goto InitPaths;
456 }
457
458 RtlInitEmptyUnicodeString(&SystemRootPath,
459 SystemRootBuffer,
460 sizeof(SystemRootBuffer));
461
462 Status = NtQuerySymbolicLinkObject(Handle,
463 &SystemRootPath,
464 &BufferSize);
465 NtClose(Handle);
466
467 if (!NT_SUCCESS(Status))
468 return Status; // Unexpected error
469
470 /* Check whether the resolved \SystemRoot is a prefix of the image file path */
471 if (RtlPrefixUnicodeString(&SystemRootPath, InstallSourcePath, TRUE))
472 {
473 /* Yes it is, so we use instead SystemRoot as the installation source path */
474 InstallSourcePath = &SystemRootPath;
475 }
476
477
478 InitPaths:
479 /*
480 * Retrieve the different source path components
481 */
482 RtlCreateUnicodeString(SourcePath, InstallSourcePath->Buffer);
483
484 /* Strip trailing directory */
485 Ptr = wcsrchr(InstallSourcePath->Buffer, OBJ_NAME_PATH_SEPARATOR);
486 if (Ptr)
487 {
488 RtlCreateUnicodeString(SourceRootDir, Ptr);
489 *Ptr = UNICODE_NULL;
490 }
491 else
492 {
493 RtlCreateUnicodeString(SourceRootDir, L"");
494 }
495
496 RtlCreateUnicodeString(SourceRootPath, InstallSourcePath->Buffer);
497
498 return STATUS_SUCCESS;
499 }
500
501 ERROR_NUMBER
502 LoadSetupInf(
503 IN OUT PUSETUP_DATA pSetupData)
504 {
505 INFCONTEXT Context;
506 UINT ErrorLine;
507 INT IntValue;
508 PWCHAR Value;
509 WCHAR FileNameBuffer[MAX_PATH];
510
511 CombinePaths(FileNameBuffer, ARRAYSIZE(FileNameBuffer), 2,
512 pSetupData->SourcePath.Buffer, L"txtsetup.sif");
513
514 DPRINT("SetupInf path: '%S'\n", FileNameBuffer);
515
516 pSetupData->SetupInf =
517 SetupOpenInfFileExW(FileNameBuffer,
518 NULL,
519 /* INF_STYLE_WIN4 | */ INF_STYLE_OLDNT,
520 pSetupData->LanguageId,
521 &ErrorLine);
522
523 if (pSetupData->SetupInf == INVALID_HANDLE_VALUE)
524 return ERROR_LOAD_TXTSETUPSIF;
525
526 /* Open 'Version' section */
527 if (!SetupFindFirstLineW(pSetupData->SetupInf, L"Version", L"Signature", &Context))
528 return ERROR_CORRUPT_TXTSETUPSIF;
529
530 /* Get pointer 'Signature' key */
531 if (!INF_GetData(&Context, NULL, &Value))
532 return ERROR_CORRUPT_TXTSETUPSIF;
533
534 /* Check 'Signature' string */
535 if (_wcsicmp(Value, L"$ReactOS$") != 0)
536 {
537 INF_FreeData(Value);
538 return ERROR_SIGNATURE_TXTSETUPSIF;
539 }
540
541 INF_FreeData(Value);
542
543 /* Open 'DiskSpaceRequirements' section */
544 if (!SetupFindFirstLineW(pSetupData->SetupInf, L"DiskSpaceRequirements", L"FreeSysPartDiskSpace", &Context))
545 return ERROR_CORRUPT_TXTSETUPSIF;
546
547 pSetupData->RequiredPartitionDiskSpace = ~0;
548
549 /* Get the 'FreeSysPartDiskSpace' value */
550 if (!SetupGetIntField(&Context, 1, &IntValue))
551 return ERROR_CORRUPT_TXTSETUPSIF;
552
553 pSetupData->RequiredPartitionDiskSpace = (ULONG)IntValue;
554
555 //
556 // Support "SetupSourceDevice" and "SetupSourcePath" in txtsetup.sif
557 // See CORE-9023
558 // Support for that should also be added in setupldr.
559 //
560
561 /* Update the Setup Source paths */
562 if (SetupFindFirstLineW(pSetupData->SetupInf, L"SetupData", L"SetupSourceDevice", &Context))
563 {
564 /*
565 * Get optional pointer 'SetupSourceDevice' key, its presence
566 * will dictate whether we also need 'SetupSourcePath'.
567 */
568 if (INF_GetData(&Context, NULL, &Value))
569 {
570 /* Free the old source root path string and create the new one */
571 RtlFreeUnicodeString(&pSetupData->SourceRootPath);
572 RtlCreateUnicodeString(&pSetupData->SourceRootPath, Value);
573 INF_FreeData(Value);
574
575 if (!SetupFindFirstLineW(pSetupData->SetupInf, L"SetupData", L"SetupSourcePath", &Context))
576 {
577 /* The 'SetupSourcePath' value is mandatory! */
578 return ERROR_CORRUPT_TXTSETUPSIF;
579 }
580
581 /* Get pointer 'SetupSourcePath' key */
582 if (!INF_GetData(&Context, NULL, &Value))
583 {
584 /* The 'SetupSourcePath' value is mandatory! */
585 return ERROR_CORRUPT_TXTSETUPSIF;
586 }
587
588 /* Free the old source path string and create the new one */
589 RtlFreeUnicodeString(&pSetupData->SourceRootDir);
590 RtlCreateUnicodeString(&pSetupData->SourceRootDir, Value);
591 INF_FreeData(Value);
592 }
593 }
594
595 /* Search for 'DefaultPath' in the 'SetupData' section */
596 pSetupData->InstallationDirectory[0] = 0;
597 if (SetupFindFirstLineW(pSetupData->SetupInf, L"SetupData", L"DefaultPath", &Context))
598 {
599 /* Get pointer 'DefaultPath' key */
600 if (!INF_GetData(&Context, NULL, &Value))
601 return ERROR_CORRUPT_TXTSETUPSIF;
602
603 RtlStringCchCopyW(pSetupData->InstallationDirectory,
604 ARRAYSIZE(pSetupData->InstallationDirectory),
605 Value);
606
607 INF_FreeData(Value);
608 }
609
610 return ERROR_SUCCESS;
611 }
612
613 NTSTATUS
614 InitDestinationPaths(
615 IN OUT PUSETUP_DATA pSetupData,
616 IN PCWSTR InstallationDir,
617 IN PDISKENTRY DiskEntry, // FIXME: HACK!
618 IN PPARTENTRY PartEntry) // FIXME: HACK!
619 {
620 WCHAR PathBuffer[MAX_PATH];
621
622 //
623 // TODO: Check return status values of the functions!
624 //
625
626 /* Create 'pSetupData->DestinationRootPath' string */
627 RtlFreeUnicodeString(&pSetupData->DestinationRootPath);
628 RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
629 L"\\Device\\Harddisk%lu\\Partition%lu\\",
630 DiskEntry->DiskNumber,
631 PartEntry->PartitionNumber);
632 RtlCreateUnicodeString(&pSetupData->DestinationRootPath, PathBuffer);
633 DPRINT("DestinationRootPath: %wZ\n", &pSetupData->DestinationRootPath);
634
635 /** Equivalent of 'NTOS_INSTALLATION::SystemArcPath' **/
636 /* Create 'pSetupData->DestinationArcPath' */
637 RtlFreeUnicodeString(&pSetupData->DestinationArcPath);
638 RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
639 L"multi(0)disk(0)rdisk(%lu)partition(%lu)\\",
640 DiskEntry->BiosDiskNumber,
641 PartEntry->PartitionNumber);
642 ConcatPaths(PathBuffer, ARRAYSIZE(PathBuffer), 1, InstallationDir);
643 RtlCreateUnicodeString(&pSetupData->DestinationArcPath, PathBuffer);
644
645 /** Equivalent of 'NTOS_INSTALLATION::SystemNtPath' **/
646 /* Create 'pSetupData->DestinationPath' string */
647 RtlFreeUnicodeString(&pSetupData->DestinationPath);
648 CombinePaths(PathBuffer, ARRAYSIZE(PathBuffer), 2,
649 pSetupData->DestinationRootPath.Buffer, InstallationDir);
650 RtlCreateUnicodeString(&pSetupData->DestinationPath, PathBuffer);
651
652 /** Equivalent of 'NTOS_INSTALLATION::PathComponent' **/
653 // FIXME: This is only temporary!! Must be removed later!
654 /***/RtlCreateUnicodeString(&pSetupData->InstallPath, InstallationDir);/***/
655
656 return STATUS_SUCCESS;
657 }
658
659 // NTSTATUS
660 ERROR_NUMBER
661 InitializeSetup(
662 IN OUT PUSETUP_DATA pSetupData,
663 IN ULONG InitPhase)
664 {
665 if (InitPhase == 0)
666 {
667 RtlZeroMemory(pSetupData, sizeof(*pSetupData));
668
669 // pSetupData->ComputerList = NULL;
670 // pSetupData->DisplayList = NULL;
671 // pSetupData->KeyboardList = NULL;
672 // pSetupData->LayoutList = NULL;
673 // pSetupData->LanguageList = NULL;
674
675 /* Initialize global unicode strings */
676 RtlInitUnicodeString(&pSetupData->SourcePath, NULL);
677 RtlInitUnicodeString(&pSetupData->SourceRootPath, NULL);
678 RtlInitUnicodeString(&pSetupData->SourceRootDir, NULL);
679 RtlInitUnicodeString(&pSetupData->DestinationArcPath, NULL);
680 RtlInitUnicodeString(&pSetupData->DestinationPath, NULL);
681 RtlInitUnicodeString(&pSetupData->DestinationRootPath, NULL);
682 RtlInitUnicodeString(&pSetupData->SystemRootPath, NULL);
683
684 // FIXME: This is only temporary!! Must be removed later!
685 /***/RtlInitUnicodeString(&pSetupData->InstallPath, NULL);/***/
686
687 //
688 // TODO: Load and start SetupDD, and ask it for the information
689 //
690
691 return ERROR_SUCCESS;
692 }
693 else
694 if (InitPhase == 1)
695 {
696 ERROR_NUMBER Error;
697 NTSTATUS Status;
698
699 /* Get the source path and source root path */
700 //
701 // NOTE: Sometimes the source path may not be in SystemRoot !!
702 // (and this is the case when using the 1st-stage GUI setup!)
703 //
704 Status = GetSourcePaths(&pSetupData->SourcePath,
705 &pSetupData->SourceRootPath,
706 &pSetupData->SourceRootDir);
707 if (!NT_SUCCESS(Status))
708 {
709 DPRINT1("GetSourcePaths() failed (Status 0x%08lx)", Status);
710 return ERROR_NO_SOURCE_DRIVE;
711 }
712 /*
713 * Example of output:
714 * SourcePath: '\Device\CdRom0\I386'
715 * SourceRootPath: '\Device\CdRom0'
716 * SourceRootDir: '\I386'
717 */
718 DPRINT1("SourcePath (1): '%wZ'\n", &pSetupData->SourcePath);
719 DPRINT1("SourceRootPath (1): '%wZ'\n", &pSetupData->SourceRootPath);
720 DPRINT1("SourceRootDir (1): '%wZ'\n", &pSetupData->SourceRootDir);
721
722 /* Load 'txtsetup.sif' from the installation media */
723 Error = LoadSetupInf(pSetupData);
724 if (Error != ERROR_SUCCESS)
725 {
726 DPRINT1("LoadSetupInf() failed (Error 0x%lx)", Error);
727 return Error;
728 }
729 DPRINT1("SourcePath (2): '%wZ'\n", &pSetupData->SourcePath);
730 DPRINT1("SourceRootPath (2): '%wZ'\n", &pSetupData->SourceRootPath);
731 DPRINT1("SourceRootDir (2): '%wZ'\n", &pSetupData->SourceRootDir);
732
733 return ERROR_SUCCESS;
734 }
735
736 return ERROR_SUCCESS;
737 }
738
739 VOID
740 FinishSetup(
741 IN OUT PUSETUP_DATA pSetupData)
742 {
743 /* Destroy the computer settings list */
744 if (pSetupData->ComputerList != NULL)
745 {
746 DestroyGenericList(pSetupData->ComputerList, TRUE);
747 pSetupData->ComputerList = NULL;
748 }
749
750 /* Destroy the display settings list */
751 if (pSetupData->DisplayList != NULL)
752 {
753 DestroyGenericList(pSetupData->DisplayList, TRUE);
754 pSetupData->DisplayList = NULL;
755 }
756
757 /* Destroy the keyboard settings list */
758 if (pSetupData->KeyboardList != NULL)
759 {
760 DestroyGenericList(pSetupData->KeyboardList, TRUE);
761 pSetupData->KeyboardList = NULL;
762 }
763
764 /* Destroy the keyboard layout list */
765 if (pSetupData->LayoutList != NULL)
766 {
767 DestroyGenericList(pSetupData->LayoutList, TRUE);
768 pSetupData->LayoutList = NULL;
769 }
770
771 /* Destroy the languages list */
772 if (pSetupData->LanguageList != NULL)
773 {
774 DestroyGenericList(pSetupData->LanguageList, FALSE);
775 pSetupData->LanguageList = NULL;
776 }
777
778 /* Close the Setup INF */
779 SetupCloseInfFile(pSetupData->SetupInf);
780 }
781
782 /*
783 * SIDEEFFECTS
784 * Calls RegInitializeRegistry
785 * Calls ImportRegistryFile
786 * Calls SetDefaultPagefile
787 * Calls SetMountedDeviceValues
788 */
789 ERROR_NUMBER
790 UpdateRegistry(
791 IN HINF SetupInf,
792 IN OUT PUSETUP_DATA pSetupData,
793 /**/IN BOOLEAN RepairUpdateFlag, /* HACK HACK! */
794 /**/IN PPARTLIST PartitionList, /* HACK HACK! */
795 /**/IN WCHAR DestinationDriveLetter, /* HACK HACK! */
796 /**/IN PCWSTR SelectedLanguageId, /* HACK HACK! */
797 IN PGENERIC_LIST DisplayList,
798 IN PGENERIC_LIST LayoutList,
799 IN PGENERIC_LIST LanguageList,
800 IN PREGISTRY_STATUS_ROUTINE StatusRoutine OPTIONAL)
801 {
802 ERROR_NUMBER ErrorNumber;
803 NTSTATUS Status;
804 INFCONTEXT InfContext;
805 PWSTR Action;
806 PWSTR File;
807 PWSTR Section;
808 BOOLEAN Success;
809 BOOLEAN ShouldRepairRegistry = FALSE;
810 BOOLEAN Delete;
811
812 if (RepairUpdateFlag)
813 {
814 DPRINT1("TODO: Updating / repairing the registry is not completely implemented yet!\n");
815
816 /* Verify the registry hives and check whether we need to update or repair any of them */
817 Status = VerifyRegistryHives(&pSetupData->DestinationPath, &ShouldRepairRegistry);
818 if (!NT_SUCCESS(Status))
819 {
820 DPRINT1("VerifyRegistryHives failed, Status 0x%08lx\n", Status);
821 ShouldRepairRegistry = FALSE;
822 }
823 if (!ShouldRepairRegistry)
824 DPRINT1("No need to repair the registry\n");
825 }
826
827 DoUpdate:
828 ErrorNumber = ERROR_SUCCESS;
829
830 /* Update the registry */
831 if (StatusRoutine) StatusRoutine(RegHiveUpdate);
832
833 /* Initialize the registry and setup the registry hives */
834 Status = RegInitializeRegistry(&pSetupData->DestinationPath);
835 if (!NT_SUCCESS(Status))
836 {
837 DPRINT1("RegInitializeRegistry() failed\n");
838 /********** HACK!!!!!!!!!!! **********/
839 if (Status == STATUS_NOT_IMPLEMENTED)
840 {
841 /* The hack was called, return its corresponding error */
842 return ERROR_INITIALIZE_REGISTRY;
843 }
844 else
845 /*************************************/
846 {
847 /* Something else failed */
848 return ERROR_CREATE_HIVE;
849 }
850 }
851
852 if (!RepairUpdateFlag || ShouldRepairRegistry)
853 {
854 /*
855 * We fully setup the hives, in case we are doing a fresh installation
856 * (RepairUpdateFlag == FALSE), or in case we are doing an update
857 * (RepairUpdateFlag == TRUE) BUT we have some registry hives to
858 * "repair" (aka. recreate: ShouldRepairRegistry == TRUE).
859 */
860
861 Success = SetupFindFirstLineW(SetupInf, L"HiveInfs.Fresh", NULL, &InfContext); // Windows-compatible
862 if (!Success)
863 Success = SetupFindFirstLineW(SetupInf, L"HiveInfs.Install", NULL, &InfContext); // ReactOS-specific
864
865 if (!Success)
866 {
867 DPRINT1("SetupFindFirstLine() failed\n");
868 ErrorNumber = ERROR_FIND_REGISTRY;
869 goto Cleanup;
870 }
871 }
872 else // if (RepairUpdateFlag && !ShouldRepairRegistry)
873 {
874 /*
875 * In case we are doing an update (RepairUpdateFlag == TRUE) and
876 * NO registry hives need a repair (ShouldRepairRegistry == FALSE),
877 * we only update the hives.
878 */
879
880 Success = SetupFindFirstLineW(SetupInf, L"HiveInfs.Upgrade", NULL, &InfContext);
881 if (!Success)
882 {
883 /* Nothing to do for update! */
884 DPRINT1("No update needed for the registry!\n");
885 goto Cleanup;
886 }
887 }
888
889 do
890 {
891 INF_GetDataField(&InfContext, 0, &Action);
892 INF_GetDataField(&InfContext, 1, &File);
893 INF_GetDataField(&InfContext, 2, &Section);
894
895 DPRINT("Action: %S File: %S Section %S\n", Action, File, Section);
896
897 if (Action == NULL)
898 {
899 INF_FreeData(Action);
900 INF_FreeData(File);
901 INF_FreeData(Section);
902 break; // Hackfix
903 }
904
905 if (!_wcsicmp(Action, L"AddReg"))
906 Delete = FALSE;
907 else if (!_wcsicmp(Action, L"DelReg"))
908 Delete = TRUE;
909 else
910 {
911 DPRINT1("Unrecognized registry INF action '%S'\n", Action);
912 INF_FreeData(Action);
913 INF_FreeData(File);
914 INF_FreeData(Section);
915 continue;
916 }
917
918 INF_FreeData(Action);
919
920 if (StatusRoutine) StatusRoutine(ImportRegHive, File);
921
922 if (!ImportRegistryFile(pSetupData->SourcePath.Buffer,
923 File, Section,
924 pSetupData->LanguageId, Delete))
925 {
926 DPRINT1("Importing %S failed\n", File);
927 INF_FreeData(File);
928 INF_FreeData(Section);
929 ErrorNumber = ERROR_IMPORT_HIVE;
930 goto Cleanup;
931 }
932 } while (SetupFindNextLine(&InfContext, &InfContext));
933
934 if (!RepairUpdateFlag || ShouldRepairRegistry)
935 {
936 /* See the explanation for this test above */
937
938 /* Update display registry settings */
939 if (StatusRoutine) StatusRoutine(DisplaySettingsUpdate);
940 if (!ProcessDisplayRegistry(SetupInf, DisplayList))
941 {
942 ErrorNumber = ERROR_UPDATE_DISPLAY_SETTINGS;
943 goto Cleanup;
944 }
945
946 /* Set the locale */
947 if (StatusRoutine) StatusRoutine(LocaleSettingsUpdate);
948 if (!ProcessLocaleRegistry(LanguageList))
949 {
950 ErrorNumber = ERROR_UPDATE_LOCALESETTINGS;
951 goto Cleanup;
952 }
953
954 /* Add keyboard layouts */
955 if (StatusRoutine) StatusRoutine(KeybLayouts);
956 if (!AddKeyboardLayouts(SelectedLanguageId))
957 {
958 ErrorNumber = ERROR_ADDING_KBLAYOUTS;
959 goto Cleanup;
960 }
961
962 /* Set GeoID */
963 if (!SetGeoID(MUIGetGeoID(SelectedLanguageId)))
964 {
965 ErrorNumber = ERROR_UPDATE_GEOID;
966 goto Cleanup;
967 }
968
969 if (!IsUnattendedSetup)
970 {
971 /* Update keyboard layout settings */
972 if (StatusRoutine) StatusRoutine(KeybSettingsUpdate);
973 if (!ProcessKeyboardLayoutRegistry(LayoutList, SelectedLanguageId))
974 {
975 ErrorNumber = ERROR_UPDATE_KBSETTINGS;
976 goto Cleanup;
977 }
978 }
979
980 /* Add codepage information to registry */
981 if (StatusRoutine) StatusRoutine(CodePageInfoUpdate);
982 if (!AddCodePage(SelectedLanguageId))
983 {
984 ErrorNumber = ERROR_ADDING_CODEPAGE;
985 goto Cleanup;
986 }
987
988 /* Set the default pagefile entry */
989 SetDefaultPagefile(DestinationDriveLetter);
990
991 /* Update the mounted devices list */
992 // FIXME: This should technically be done by mountmgr (if AutoMount is enabled)!
993 SetMountedDeviceValues(PartitionList);
994 }
995
996 Cleanup:
997 //
998 // TODO: Unload all the registry stuff, perform cleanup,
999 // and copy the created hive files into .sav files.
1000 //
1001 RegCleanupRegistry(&pSetupData->DestinationPath);
1002
1003 /*
1004 * Check whether we were in update/repair mode but we were actually
1005 * repairing the registry hives. If so, we have finished repairing them,
1006 * and we now reset the flag and run the proper registry update.
1007 * Otherwise we have finished the registry update!
1008 */
1009 if (RepairUpdateFlag && ShouldRepairRegistry)
1010 {
1011 ShouldRepairRegistry = FALSE;
1012 goto DoUpdate;
1013 }
1014
1015 return ErrorNumber;
1016 }
1017
1018 /* EOF */