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