[SETUPLIB] Partitioning code fixes and improvements.
[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 Quit:
200 SpInfCloseInfFile(UnattendInf);
201 }
202
203 VOID
204 InstallSetupInfFile(
205 IN OUT PUSETUP_DATA pSetupData)
206 {
207 NTSTATUS Status;
208 PINICACHE IniCache;
209
210 #if 0 // HACK FIXME!
211 PINICACHE UnattendCache;
212 PINICACHEITERATOR Iterator;
213 #else
214 // WCHAR CrLf[] = {L'\r', L'\n'};
215 CHAR CrLf[] = {'\r', '\n'};
216 HANDLE FileHandle, UnattendFileHandle, SectionHandle;
217 FILE_STANDARD_INFORMATION FileInfo;
218 ULONG FileSize;
219 PVOID ViewBase;
220 UNICODE_STRING FileName;
221 OBJECT_ATTRIBUTES ObjectAttributes;
222 IO_STATUS_BLOCK IoStatusBlock;
223 #endif
224
225 PINICACHESECTION IniSection;
226 WCHAR PathBuffer[MAX_PATH];
227 WCHAR UnattendInfPath[MAX_PATH];
228
229 /* Create a $winnt$.inf file with default entries */
230 IniCache = IniCacheCreate();
231 if (!IniCache)
232 return;
233
234 IniSection = IniCacheAppendSection(IniCache, L"SetupParams");
235 if (IniSection)
236 {
237 /* Key "skipmissingfiles" */
238 // RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
239 // L"\"%s\"", L"WinNt5.2");
240 // IniCacheInsertKey(IniSection, NULL, INSERT_LAST,
241 // L"Version", PathBuffer);
242 }
243
244 IniSection = IniCacheAppendSection(IniCache, L"Data");
245 if (IniSection)
246 {
247 RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
248 L"\"%s\"", IsUnattendedSetup ? L"yes" : L"no");
249 IniCacheInsertKey(IniSection, NULL, INSERT_LAST,
250 L"UnattendedInstall", PathBuffer);
251
252 // "floppylessbootpath" (yes/no)
253
254 RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
255 L"\"%s\"", L"winnt");
256 IniCacheInsertKey(IniSection, NULL, INSERT_LAST,
257 L"ProductType", PathBuffer);
258
259 RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
260 L"\"%s\\\"", pSetupData->SourceRootPath.Buffer);
261 IniCacheInsertKey(IniSection, NULL, INSERT_LAST,
262 L"SourcePath", PathBuffer);
263
264 // "floppyless" ("0")
265 }
266
267 #if 0
268
269 /* TODO: Append the standard unattend.inf file */
270 CombinePaths(UnattendInfPath, ARRAYSIZE(UnattendInfPath), 2,
271 pSetupData->SourcePath.Buffer, L"unattend.inf");
272 if (DoesFileExist(NULL, UnattendInfPath) == FALSE)
273 {
274 DPRINT("Does not exist: %S\n", UnattendInfPath);
275 goto Quit;
276 }
277
278 Status = IniCacheLoad(&UnattendCache, UnattendInfPath, FALSE);
279 if (!NT_SUCCESS(Status))
280 {
281 DPRINT1("Cannot load %S as an INI file!\n", UnattendInfPath);
282 goto Quit;
283 }
284
285 IniCacheDestroy(UnattendCache);
286
287 Quit:
288 CombinePaths(PathBuffer, ARRAYSIZE(PathBuffer), 2,
289 pSetupData->DestinationPath.Buffer, L"System32\\$winnt$.inf");
290 IniCacheSave(IniCache, PathBuffer);
291 IniCacheDestroy(IniCache);
292
293 #else
294
295 CombinePaths(PathBuffer, ARRAYSIZE(PathBuffer), 2,
296 pSetupData->DestinationPath.Buffer, L"System32\\$winnt$.inf");
297 IniCacheSave(IniCache, PathBuffer);
298 IniCacheDestroy(IniCache);
299
300 /* TODO: Append the standard unattend.inf file */
301 CombinePaths(UnattendInfPath, ARRAYSIZE(UnattendInfPath), 2,
302 pSetupData->SourcePath.Buffer, L"unattend.inf");
303 if (DoesFileExist(NULL, UnattendInfPath) == FALSE)
304 {
305 DPRINT("Does not exist: %S\n", UnattendInfPath);
306 return;
307 }
308
309 RtlInitUnicodeString(&FileName, PathBuffer);
310 InitializeObjectAttributes(&ObjectAttributes,
311 &FileName,
312 OBJ_CASE_INSENSITIVE | OBJ_OPENIF,
313 NULL,
314 NULL);
315 Status = NtOpenFile(&FileHandle,
316 FILE_APPEND_DATA | SYNCHRONIZE,
317 &ObjectAttributes,
318 &IoStatusBlock,
319 FILE_SHARE_READ,
320 FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE);
321 if (!NT_SUCCESS(Status))
322 {
323 DPRINT1("Cannot load %S as an INI file!\n", PathBuffer);
324 return;
325 }
326
327 /* Query the file size */
328 Status = NtQueryInformationFile(FileHandle,
329 &IoStatusBlock,
330 &FileInfo,
331 sizeof(FileInfo),
332 FileStandardInformation);
333 if (!NT_SUCCESS(Status))
334 {
335 DPRINT("NtQueryInformationFile() failed (Status %lx)\n", Status);
336 FileInfo.EndOfFile.QuadPart = 0ULL;
337 }
338
339 Status = OpenAndMapFile(NULL,
340 UnattendInfPath,
341 &UnattendFileHandle,
342 &SectionHandle,
343 &ViewBase,
344 &FileSize,
345 FALSE);
346 if (!NT_SUCCESS(Status))
347 {
348 DPRINT1("Cannot load %S !\n", UnattendInfPath);
349 NtClose(FileHandle);
350 return;
351 }
352
353 /* Write to the INI file */
354
355 /* "\r\n" */
356 Status = NtWriteFile(FileHandle,
357 NULL,
358 NULL,
359 NULL,
360 &IoStatusBlock,
361 (PVOID)CrLf,
362 sizeof(CrLf),
363 &FileInfo.EndOfFile,
364 NULL);
365
366 Status = NtWriteFile(FileHandle,
367 NULL,
368 NULL,
369 NULL,
370 &IoStatusBlock,
371 ViewBase,
372 FileSize,
373 NULL,
374 NULL);
375 if (!NT_SUCCESS(Status))
376 {
377 DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
378 }
379
380 /* Finally, unmap and close the file */
381 UnMapAndCloseFile(UnattendFileHandle, SectionHandle, ViewBase);
382
383 NtClose(FileHandle);
384 #endif
385 }
386
387 NTSTATUS
388 GetSourcePaths(
389 OUT PUNICODE_STRING SourcePath,
390 OUT PUNICODE_STRING SourceRootPath,
391 OUT PUNICODE_STRING SourceRootDir)
392 {
393 NTSTATUS Status;
394 HANDLE LinkHandle;
395 OBJECT_ATTRIBUTES ObjectAttributes;
396 UCHAR ImageFileBuffer[sizeof(UNICODE_STRING) + MAX_PATH * sizeof(WCHAR)];
397 PUNICODE_STRING InstallSourcePath = (PUNICODE_STRING)&ImageFileBuffer;
398 WCHAR SystemRootBuffer[MAX_PATH] = L"";
399 UNICODE_STRING SystemRootPath = RTL_CONSTANT_STRING(L"\\SystemRoot");
400 ULONG BufferSize;
401 PWCHAR Ptr;
402
403 /* Determine the installation source path via the full path of the installer */
404 RtlInitEmptyUnicodeString(InstallSourcePath,
405 (PWSTR)((ULONG_PTR)ImageFileBuffer + sizeof(UNICODE_STRING)),
406 sizeof(ImageFileBuffer) - sizeof(UNICODE_STRING)
407 /* Reserve space for a NULL terminator */ - sizeof(UNICODE_NULL));
408 BufferSize = sizeof(ImageFileBuffer);
409 Status = NtQueryInformationProcess(NtCurrentProcess(),
410 ProcessImageFileName,
411 InstallSourcePath,
412 BufferSize,
413 NULL);
414 // STATUS_INFO_LENGTH_MISMATCH or STATUS_BUFFER_TOO_SMALL ?
415 if (!NT_SUCCESS(Status))
416 return Status;
417
418 /* Manually NULL-terminate */
419 InstallSourcePath->Buffer[InstallSourcePath->Length / sizeof(WCHAR)] = UNICODE_NULL;
420
421 /* Strip the trailing file name */
422 Ptr = wcsrchr(InstallSourcePath->Buffer, OBJ_NAME_PATH_SEPARATOR);
423 if (Ptr)
424 *Ptr = UNICODE_NULL;
425 InstallSourcePath->Length = wcslen(InstallSourcePath->Buffer) * sizeof(WCHAR);
426
427
428 /*
429 * Now resolve the full path to \SystemRoot. In case it prefixes
430 * the installation source path determined from the full path of
431 * the installer, we use instead the resolved \SystemRoot as the
432 * installation source path.
433 * Otherwise, we use instead the path from the full installer path.
434 */
435
436 InitializeObjectAttributes(&ObjectAttributes,
437 &SystemRootPath,
438 OBJ_CASE_INSENSITIVE,
439 NULL,
440 NULL);
441
442 Status = NtOpenSymbolicLinkObject(&LinkHandle,
443 SYMBOLIC_LINK_QUERY,
444 &ObjectAttributes);
445 if (!NT_SUCCESS(Status))
446 {
447 /*
448 * We failed at opening the \SystemRoot link (usually due to wrong
449 * access rights). Do not consider this as a fatal error, but use
450 * instead the image file path as the installation source path.
451 */
452 DPRINT1("NtOpenSymbolicLinkObject(%wZ) failed with Status 0x%08lx\n",
453 &SystemRootPath, Status);
454 goto InitPaths;
455 }
456
457 RtlInitEmptyUnicodeString(&SystemRootPath,
458 SystemRootBuffer,
459 sizeof(SystemRootBuffer));
460
461 /* Resolve the link and close its handle */
462 Status = NtQuerySymbolicLinkObject(LinkHandle,
463 &SystemRootPath,
464 &BufferSize);
465 NtClose(LinkHandle);
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 PCWSTR 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 SpInfOpenInfFile(FileNameBuffer,
518 NULL,
519 INF_STYLE_WIN4,
520 pSetupData->LanguageId,
521 &ErrorLine);
522 if (pSetupData->SetupInf == INVALID_HANDLE_VALUE)
523 return ERROR_LOAD_TXTSETUPSIF;
524
525 /* Open 'Version' section */
526 if (!SpInfFindFirstLine(pSetupData->SetupInf, L"Version", L"Signature", &Context))
527 return ERROR_CORRUPT_TXTSETUPSIF;
528
529 /* Get pointer 'Signature' key */
530 if (!INF_GetData(&Context, NULL, &Value))
531 return ERROR_CORRUPT_TXTSETUPSIF;
532
533 /* Check 'Signature' string */
534 if (_wcsicmp(Value, L"$ReactOS$") != 0 &&
535 _wcsicmp(Value, L"$Windows NT$") != 0)
536 {
537 INF_FreeData(Value);
538 return ERROR_SIGNATURE_TXTSETUPSIF;
539 }
540
541 INF_FreeData(Value);
542
543 /* Open 'DiskSpaceRequirements' section */
544 if (!SpInfFindFirstLine(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 (!SpInfGetIntField(&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 (SpInfFindFirstLine(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 (!SpInfFindFirstLine(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 (SpInfFindFirstLine(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 // FIXME! Which variable to choose?
636 if (!InstallationDir)
637 InstallationDir = pSetupData->InstallationDirectory;
638
639 /** Equivalent of 'NTOS_INSTALLATION::SystemArcPath' **/
640 /* Create 'pSetupData->DestinationArcPath' */
641 RtlFreeUnicodeString(&pSetupData->DestinationArcPath);
642 RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
643 L"multi(0)disk(0)rdisk(%lu)partition(%lu)\\",
644 DiskEntry->BiosDiskNumber,
645 PartEntry->OnDiskPartitionNumber);
646 ConcatPaths(PathBuffer, ARRAYSIZE(PathBuffer), 1, InstallationDir);
647 RtlCreateUnicodeString(&pSetupData->DestinationArcPath, PathBuffer);
648
649 /** Equivalent of 'NTOS_INSTALLATION::SystemNtPath' **/
650 /* Create 'pSetupData->DestinationPath' string */
651 RtlFreeUnicodeString(&pSetupData->DestinationPath);
652 CombinePaths(PathBuffer, ARRAYSIZE(PathBuffer), 2,
653 pSetupData->DestinationRootPath.Buffer, InstallationDir);
654 RtlCreateUnicodeString(&pSetupData->DestinationPath, PathBuffer);
655
656 /** Equivalent of 'NTOS_INSTALLATION::PathComponent' **/
657 // FIXME: This is only temporary!! Must be removed later!
658 /***/RtlCreateUnicodeString(&pSetupData->InstallPath, InstallationDir);/***/
659
660 return STATUS_SUCCESS;
661 }
662
663 // NTSTATUS
664 ERROR_NUMBER
665 InitializeSetup(
666 IN OUT PUSETUP_DATA pSetupData,
667 IN ULONG InitPhase)
668 {
669 if (InitPhase == 0)
670 {
671 RtlZeroMemory(pSetupData, sizeof(*pSetupData));
672
673 // pSetupData->ComputerList = NULL;
674 // pSetupData->DisplayList = NULL;
675 // pSetupData->KeyboardList = NULL;
676 // pSetupData->LayoutList = NULL;
677 // pSetupData->LanguageList = NULL;
678
679 /* Initialize error handling */
680 pSetupData->LastErrorNumber = ERROR_SUCCESS;
681 pSetupData->ErrorRoutine = NULL;
682
683 /* Initialize global unicode strings */
684 RtlInitUnicodeString(&pSetupData->SourcePath, NULL);
685 RtlInitUnicodeString(&pSetupData->SourceRootPath, NULL);
686 RtlInitUnicodeString(&pSetupData->SourceRootDir, NULL);
687 RtlInitUnicodeString(&pSetupData->DestinationArcPath, NULL);
688 RtlInitUnicodeString(&pSetupData->DestinationPath, NULL);
689 RtlInitUnicodeString(&pSetupData->DestinationRootPath, NULL);
690 RtlInitUnicodeString(&pSetupData->SystemRootPath, NULL);
691
692 // FIXME: This is only temporary!! Must be removed later!
693 /***/RtlInitUnicodeString(&pSetupData->InstallPath, NULL);/***/
694
695 //
696 // TODO: Load and start SetupDD, and ask it for the information
697 //
698
699 return ERROR_SUCCESS;
700 }
701 else
702 if (InitPhase == 1)
703 {
704 ERROR_NUMBER Error;
705 NTSTATUS Status;
706
707 /* Get the source path and source root path */
708 //
709 // NOTE: Sometimes the source path may not be in SystemRoot !!
710 // (and this is the case when using the 1st-stage GUI setup!)
711 //
712 Status = GetSourcePaths(&pSetupData->SourcePath,
713 &pSetupData->SourceRootPath,
714 &pSetupData->SourceRootDir);
715 if (!NT_SUCCESS(Status))
716 {
717 DPRINT1("GetSourcePaths() failed (Status 0x%08lx)", Status);
718 return ERROR_NO_SOURCE_DRIVE;
719 }
720 /*
721 * Example of output:
722 * SourcePath: '\Device\CdRom0\I386'
723 * SourceRootPath: '\Device\CdRom0'
724 * SourceRootDir: '\I386'
725 */
726 DPRINT1("SourcePath (1): '%wZ'\n", &pSetupData->SourcePath);
727 DPRINT1("SourceRootPath (1): '%wZ'\n", &pSetupData->SourceRootPath);
728 DPRINT1("SourceRootDir (1): '%wZ'\n", &pSetupData->SourceRootDir);
729
730 /* Load 'txtsetup.sif' from the installation media */
731 Error = LoadSetupInf(pSetupData);
732 if (Error != ERROR_SUCCESS)
733 {
734 DPRINT1("LoadSetupInf() failed (Error 0x%lx)", Error);
735 return Error;
736 }
737 DPRINT1("SourcePath (2): '%wZ'\n", &pSetupData->SourcePath);
738 DPRINT1("SourceRootPath (2): '%wZ'\n", &pSetupData->SourceRootPath);
739 DPRINT1("SourceRootDir (2): '%wZ'\n", &pSetupData->SourceRootDir);
740
741 return ERROR_SUCCESS;
742 }
743
744 return ERROR_SUCCESS;
745 }
746
747 VOID
748 FinishSetup(
749 IN OUT PUSETUP_DATA pSetupData)
750 {
751 /* Destroy the computer settings list */
752 if (pSetupData->ComputerList != NULL)
753 {
754 DestroyGenericList(pSetupData->ComputerList, TRUE);
755 pSetupData->ComputerList = NULL;
756 }
757
758 /* Destroy the display settings list */
759 if (pSetupData->DisplayList != NULL)
760 {
761 DestroyGenericList(pSetupData->DisplayList, TRUE);
762 pSetupData->DisplayList = NULL;
763 }
764
765 /* Destroy the keyboard settings list */
766 if (pSetupData->KeyboardList != NULL)
767 {
768 DestroyGenericList(pSetupData->KeyboardList, TRUE);
769 pSetupData->KeyboardList = NULL;
770 }
771
772 /* Destroy the keyboard layout list */
773 if (pSetupData->LayoutList != NULL)
774 {
775 DestroyGenericList(pSetupData->LayoutList, TRUE);
776 pSetupData->LayoutList = NULL;
777 }
778
779 /* Destroy the languages list */
780 if (pSetupData->LanguageList != NULL)
781 {
782 DestroyGenericList(pSetupData->LanguageList, FALSE);
783 pSetupData->LanguageList = NULL;
784 }
785
786 /* Close the Setup INF */
787 SpInfCloseInfFile(pSetupData->SetupInf);
788 }
789
790 /*
791 * SIDEEFFECTS
792 * Calls RegInitializeRegistry
793 * Calls ImportRegistryFile
794 * Calls SetDefaultPagefile
795 * Calls SetMountedDeviceValues
796 */
797 ERROR_NUMBER
798 UpdateRegistry(
799 IN OUT PUSETUP_DATA pSetupData,
800 /**/IN BOOLEAN RepairUpdateFlag, /* HACK HACK! */
801 /**/IN PPARTLIST PartitionList, /* HACK HACK! */
802 /**/IN WCHAR DestinationDriveLetter, /* HACK HACK! */
803 /**/IN PCWSTR SelectedLanguageId, /* HACK HACK! */
804 IN PREGISTRY_STATUS_ROUTINE StatusRoutine OPTIONAL)
805 {
806 ERROR_NUMBER ErrorNumber;
807 NTSTATUS Status;
808 INFCONTEXT InfContext;
809 PCWSTR Action;
810 PCWSTR File;
811 PCWSTR Section;
812 BOOLEAN Success;
813 BOOLEAN ShouldRepairRegistry = FALSE;
814 BOOLEAN Delete;
815
816 if (RepairUpdateFlag)
817 {
818 DPRINT1("TODO: Updating / repairing the registry is not completely implemented yet!\n");
819
820 /* Verify the registry hives and check whether we need to update or repair any of them */
821 Status = VerifyRegistryHives(&pSetupData->DestinationPath, &ShouldRepairRegistry);
822 if (!NT_SUCCESS(Status))
823 {
824 DPRINT1("VerifyRegistryHives failed, Status 0x%08lx\n", Status);
825 ShouldRepairRegistry = FALSE;
826 }
827 if (!ShouldRepairRegistry)
828 DPRINT1("No need to repair the registry\n");
829 }
830
831 DoUpdate:
832 ErrorNumber = ERROR_SUCCESS;
833
834 /* Update the registry */
835 if (StatusRoutine) StatusRoutine(RegHiveUpdate);
836
837 /* Initialize the registry and setup the registry hives */
838 Status = RegInitializeRegistry(&pSetupData->DestinationPath);
839 if (!NT_SUCCESS(Status))
840 {
841 DPRINT1("RegInitializeRegistry() failed\n");
842 /********** HACK!!!!!!!!!!! **********/
843 if (Status == STATUS_NOT_IMPLEMENTED)
844 {
845 /* The hack was called, return its corresponding error */
846 return ERROR_INITIALIZE_REGISTRY;
847 }
848 else
849 /*************************************/
850 {
851 /* Something else failed */
852 return ERROR_CREATE_HIVE;
853 }
854 }
855
856 if (!RepairUpdateFlag || ShouldRepairRegistry)
857 {
858 /*
859 * We fully setup the hives, in case we are doing a fresh installation
860 * (RepairUpdateFlag == FALSE), or in case we are doing an update
861 * (RepairUpdateFlag == TRUE) BUT we have some registry hives to
862 * "repair" (aka. recreate: ShouldRepairRegistry == TRUE).
863 */
864
865 Success = SpInfFindFirstLine(pSetupData->SetupInf, L"HiveInfs.Fresh", NULL, &InfContext); // Windows-compatible
866 if (!Success)
867 Success = SpInfFindFirstLine(pSetupData->SetupInf, L"HiveInfs.Install", NULL, &InfContext); // ReactOS-specific
868
869 if (!Success)
870 {
871 DPRINT1("SpInfFindFirstLine() failed\n");
872 ErrorNumber = ERROR_FIND_REGISTRY;
873 goto Cleanup;
874 }
875 }
876 else // if (RepairUpdateFlag && !ShouldRepairRegistry)
877 {
878 /*
879 * In case we are doing an update (RepairUpdateFlag == TRUE) and
880 * NO registry hives need a repair (ShouldRepairRegistry == FALSE),
881 * we only update the hives.
882 */
883
884 Success = SpInfFindFirstLine(pSetupData->SetupInf, L"HiveInfs.Upgrade", NULL, &InfContext);
885 if (!Success)
886 {
887 /* Nothing to do for update! */
888 DPRINT1("No update needed for the registry!\n");
889 goto Cleanup;
890 }
891 }
892
893 do
894 {
895 INF_GetDataField(&InfContext, 0, &Action);
896 INF_GetDataField(&InfContext, 1, &File);
897 INF_GetDataField(&InfContext, 2, &Section);
898
899 DPRINT("Action: %S File: %S Section %S\n", Action, File, Section);
900
901 if (Action == NULL)
902 {
903 INF_FreeData(Action);
904 INF_FreeData(File);
905 INF_FreeData(Section);
906 break; // Hackfix
907 }
908
909 if (!_wcsicmp(Action, L"AddReg"))
910 Delete = FALSE;
911 else if (!_wcsicmp(Action, L"DelReg"))
912 Delete = TRUE;
913 else
914 {
915 DPRINT1("Unrecognized registry INF action '%S'\n", Action);
916 INF_FreeData(Action);
917 INF_FreeData(File);
918 INF_FreeData(Section);
919 continue;
920 }
921
922 INF_FreeData(Action);
923
924 if (StatusRoutine) StatusRoutine(ImportRegHive, File);
925
926 if (!ImportRegistryFile(pSetupData->SourcePath.Buffer,
927 File, Section,
928 pSetupData->LanguageId, Delete))
929 {
930 DPRINT1("Importing %S failed\n", File);
931 INF_FreeData(File);
932 INF_FreeData(Section);
933 ErrorNumber = ERROR_IMPORT_HIVE;
934 goto Cleanup;
935 }
936 } while (SpInfFindNextLine(&InfContext, &InfContext));
937
938 if (!RepairUpdateFlag || ShouldRepairRegistry)
939 {
940 /* See the explanation for this test above */
941
942 /* Update display registry settings */
943 if (StatusRoutine) StatusRoutine(DisplaySettingsUpdate);
944 if (!ProcessDisplayRegistry(pSetupData->SetupInf, pSetupData->DisplayList))
945 {
946 ErrorNumber = ERROR_UPDATE_DISPLAY_SETTINGS;
947 goto Cleanup;
948 }
949
950 /* Set the locale */
951 if (StatusRoutine) StatusRoutine(LocaleSettingsUpdate);
952 if (!ProcessLocaleRegistry(pSetupData->LanguageList))
953 {
954 ErrorNumber = ERROR_UPDATE_LOCALESETTINGS;
955 goto Cleanup;
956 }
957
958 /* Add keyboard layouts */
959 if (StatusRoutine) StatusRoutine(KeybLayouts);
960 if (!AddKeyboardLayouts(SelectedLanguageId))
961 {
962 ErrorNumber = ERROR_ADDING_KBLAYOUTS;
963 goto Cleanup;
964 }
965
966 /* Set GeoID */
967 if (!SetGeoID(MUIGetGeoID(SelectedLanguageId)))
968 {
969 ErrorNumber = ERROR_UPDATE_GEOID;
970 goto Cleanup;
971 }
972
973 if (!IsUnattendedSetup)
974 {
975 /* Update keyboard layout settings */
976 if (StatusRoutine) StatusRoutine(KeybSettingsUpdate);
977 if (!ProcessKeyboardLayoutRegistry(pSetupData->LayoutList, SelectedLanguageId))
978 {
979 ErrorNumber = ERROR_UPDATE_KBSETTINGS;
980 goto Cleanup;
981 }
982 }
983
984 /* Add codepage information to registry */
985 if (StatusRoutine) StatusRoutine(CodePageInfoUpdate);
986 if (!AddCodePage(SelectedLanguageId))
987 {
988 ErrorNumber = ERROR_ADDING_CODEPAGE;
989 goto Cleanup;
990 }
991
992 /* Set the default pagefile entry */
993 SetDefaultPagefile(DestinationDriveLetter);
994
995 /* Update the mounted devices list */
996 // FIXME: This should technically be done by mountmgr (if AutoMount is enabled)!
997 SetMountedDeviceValues(PartitionList);
998 }
999
1000 Cleanup:
1001 //
1002 // TODO: Unload all the registry stuff, perform cleanup,
1003 // and copy the created hive files into .sav files.
1004 //
1005 RegCleanupRegistry(&pSetupData->DestinationPath);
1006
1007 /*
1008 * Check whether we were in update/repair mode but we were actually
1009 * repairing the registry hives. If so, we have finished repairing them,
1010 * and we now reset the flag and run the proper registry update.
1011 * Otherwise we have finished the registry update!
1012 */
1013 if (RepairUpdateFlag && ShouldRepairRegistry)
1014 {
1015 ShouldRepairRegistry = FALSE;
1016 goto DoUpdate;
1017 }
1018
1019 return ErrorNumber;
1020 }
1021
1022 /* EOF */