c5a8a6f981504f919910742abe12a70f59a1c9c1
[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 // HACK!
20 #include <strsafe.h>
21
22 #define NDEBUG
23 #include <debug.h>
24
25
26 /* GLOBALS ******************************************************************/
27
28 /* FUNCTIONS ****************************************************************/
29
30 VOID
31 CheckUnattendedSetup(
32 IN OUT PUSETUP_DATA pSetupData)
33 {
34 INFCONTEXT Context;
35 HINF UnattendInf;
36 UINT ErrorLine;
37 INT IntValue;
38 PWCHAR Value;
39 WCHAR UnattendInfPath[MAX_PATH];
40
41 CombinePaths(UnattendInfPath, ARRAYSIZE(UnattendInfPath), 2,
42 pSetupData->SourcePath.Buffer, L"unattend.inf");
43
44 DPRINT("UnattendInf path: '%S'\n", UnattendInfPath);
45
46 if (DoesFileExist(NULL, UnattendInfPath) == FALSE)
47 {
48 DPRINT("Does not exist: %S\n", UnattendInfPath);
49 return;
50 }
51
52 /* Load 'unattend.inf' from installation media */
53 UnattendInf = SetupOpenInfFileExW(UnattendInfPath,
54 NULL,
55 INF_STYLE_OLDNT,
56 pSetupData->LanguageId,
57 &ErrorLine);
58
59 if (UnattendInf == INVALID_HANDLE_VALUE)
60 {
61 DPRINT("SetupOpenInfFileExW() failed\n");
62 return;
63 }
64
65 /* Open 'Unattend' section */
66 if (!SetupFindFirstLineW(UnattendInf, L"Unattend", L"Signature", &Context))
67 {
68 DPRINT("SetupFindFirstLineW() failed for section 'Unattend'\n");
69 goto Quit;
70 }
71
72 /* Get pointer 'Signature' key */
73 if (!INF_GetData(&Context, NULL, &Value))
74 {
75 DPRINT("INF_GetData() failed for key 'Signature'\n");
76 goto Quit;
77 }
78
79 /* Check 'Signature' string */
80 if (_wcsicmp(Value, L"$ReactOS$") != 0)
81 {
82 DPRINT("Signature not $ReactOS$\n");
83 INF_FreeData(Value);
84 goto Quit;
85 }
86
87 INF_FreeData(Value);
88
89 /* Check if Unattend setup is enabled */
90 if (!SetupFindFirstLineW(UnattendInf, L"Unattend", L"UnattendSetupEnabled", &Context))
91 {
92 DPRINT("Can't find key 'UnattendSetupEnabled'\n");
93 goto Quit;
94 }
95
96 if (!INF_GetData(&Context, NULL, &Value))
97 {
98 DPRINT("Can't read key 'UnattendSetupEnabled'\n");
99 goto Quit;
100 }
101
102 if (_wcsicmp(Value, L"yes") != 0)
103 {
104 DPRINT("Unattend setup is disabled by 'UnattendSetupEnabled' key!\n");
105 INF_FreeData(Value);
106 goto Quit;
107 }
108
109 INF_FreeData(Value);
110
111 /* Search for 'DestinationDiskNumber' in the 'Unattend' section */
112 if (!SetupFindFirstLineW(UnattendInf, L"Unattend", L"DestinationDiskNumber", &Context))
113 {
114 DPRINT("SetupFindFirstLine() failed for key 'DestinationDiskNumber'\n");
115 goto Quit;
116 }
117
118 if (!SetupGetIntField(&Context, 1, &IntValue))
119 {
120 DPRINT("SetupGetIntField() failed for key 'DestinationDiskNumber'\n");
121 goto Quit;
122 }
123
124 pSetupData->DestinationDiskNumber = (LONG)IntValue;
125
126 /* Search for 'DestinationPartitionNumber' in the 'Unattend' section */
127 if (!SetupFindFirstLineW(UnattendInf, L"Unattend", L"DestinationPartitionNumber", &Context))
128 {
129 DPRINT("SetupFindFirstLine() failed for key 'DestinationPartitionNumber'\n");
130 goto Quit;
131 }
132
133 if (!SetupGetIntField(&Context, 1, &IntValue))
134 {
135 DPRINT("SetupGetIntField() failed for key 'DestinationPartitionNumber'\n");
136 goto Quit;
137 }
138
139 pSetupData->DestinationPartitionNumber = (LONG)IntValue;
140
141 /* Search for 'InstallationDirectory' in the 'Unattend' section (optional) */
142 if (SetupFindFirstLineW(UnattendInf, L"Unattend", L"InstallationDirectory", &Context))
143 {
144 /* Get pointer 'InstallationDirectory' key */
145 if (!INF_GetData(&Context, NULL, &Value))
146 {
147 DPRINT("INF_GetData() failed for key 'InstallationDirectory'\n");
148 goto Quit;
149 }
150 wcscpy(pSetupData->InstallationDirectory, Value);
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 (SetupFindFirstLineW(UnattendInf, L"Unattend", L"MBRInstallType", &Context))
160 {
161 if (SetupGetIntField(&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 (SetupFindFirstLineW(UnattendInf, L"Unattend", L"FormatPartition", &Context))
170 {
171 if (SetupGetIntField(&Context, 1, &IntValue))
172 {
173 pSetupData->FormatPartition = IntValue;
174 }
175 }
176
177 pSetupData->AutoPartition = 0;
178 if (SetupFindFirstLineW(UnattendInf, L"Unattend", L"AutoPartition", &Context))
179 {
180 if (SetupGetIntField(&Context, 1, &IntValue))
181 {
182 pSetupData->AutoPartition = IntValue;
183 }
184 }
185
186 /* Search for LocaleID in the 'Unattend' section */
187 if (SetupFindFirstLineW(UnattendInf, L"Unattend", L"LocaleID", &Context))
188 {
189 if (INF_GetData(&Context, NULL, &Value))
190 {
191 LONG Id = wcstol(Value, NULL, 16);
192 swprintf(pSetupData->LocaleID, L"%08lx", Id);
193 INF_FreeData(Value);
194 }
195 }
196
197 Quit:
198 SetupCloseInfFile(UnattendInf);
199 }
200
201 VOID
202 InstallSetupInfFile(
203 IN OUT PUSETUP_DATA pSetupData)
204 {
205 NTSTATUS Status;
206 PINICACHE IniCache;
207
208 #if 0 // HACK FIXME!
209 PINICACHE UnattendCache;
210 PINICACHEITERATOR Iterator;
211 #else
212 // PCWSTR CrLf = L"\r\n";
213 PCSTR CrLf = "\r\n";
214 HANDLE FileHandle, UnattendFileHandle, SectionHandle;
215 FILE_STANDARD_INFORMATION FileInfo;
216 ULONG FileSize;
217 PVOID ViewBase;
218 UNICODE_STRING FileName;
219 OBJECT_ATTRIBUTES ObjectAttributes;
220 IO_STATUS_BLOCK IoStatusBlock;
221 #endif
222
223 PINICACHESECTION IniSection;
224 WCHAR PathBuffer[MAX_PATH];
225 WCHAR UnattendInfPath[MAX_PATH];
226
227 /* Create a $winnt$.inf file with default entries */
228 IniCache = IniCacheCreate();
229 if (!IniCache)
230 return;
231
232 IniSection = IniCacheAppendSection(IniCache, L"SetupParams");
233 if (IniSection)
234 {
235 /* Key "skipmissingfiles" */
236 // StringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
237 // L"\"%s\"", L"WinNt5.2");
238 // IniCacheInsertKey(IniSection, NULL, INSERT_LAST,
239 // L"Version", PathBuffer);
240 }
241
242 IniSection = IniCacheAppendSection(IniCache, L"Data");
243 if (IniSection)
244 {
245 StringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
246 L"\"%s\"", IsUnattendedSetup ? L"yes" : L"no");
247 IniCacheInsertKey(IniSection, NULL, INSERT_LAST,
248 L"UnattendedInstall", PathBuffer);
249
250 // "floppylessbootpath" (yes/no)
251
252 StringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
253 L"\"%s\"", L"winnt");
254 IniCacheInsertKey(IniSection, NULL, INSERT_LAST,
255 L"ProductType", PathBuffer);
256
257 StringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
258 L"\"%s\\\"", pSetupData->SourceRootPath.Buffer);
259 IniCacheInsertKey(IniSection, NULL, INSERT_LAST,
260 L"SourcePath", PathBuffer);
261
262 // "floppyless" ("0")
263 }
264
265 #if 0
266
267 /* TODO: Append the standard unattend.inf file */
268 CombinePaths(UnattendInfPath, ARRAYSIZE(UnattendInfPath), 2,
269 pSetupData->SourcePath.Buffer, L"unattend.inf");
270 if (DoesFileExist(NULL, UnattendInfPath) == FALSE)
271 {
272 DPRINT("Does not exist: %S\n", UnattendInfPath);
273 goto Quit;
274 }
275
276 Status = IniCacheLoad(&UnattendCache, UnattendInfPath, FALSE);
277 if (!NT_SUCCESS(Status))
278 {
279 DPRINT1("Cannot load %S as an INI file!\n", UnattendInfPath);
280 goto Quit;
281 }
282
283 IniCacheDestroy(UnattendCache);
284
285 Quit:
286 CombinePaths(PathBuffer, ARRAYSIZE(PathBuffer), 2,
287 pSetupData->DestinationPath.Buffer, L"System32\\$winnt$.inf");
288 IniCacheSave(IniCache, PathBuffer);
289 IniCacheDestroy(IniCache);
290
291 #else
292
293 CombinePaths(PathBuffer, ARRAYSIZE(PathBuffer), 2,
294 pSetupData->DestinationPath.Buffer, L"System32\\$winnt$.inf");
295 IniCacheSave(IniCache, PathBuffer);
296 IniCacheDestroy(IniCache);
297
298 /* TODO: Append the standard unattend.inf file */
299 CombinePaths(UnattendInfPath, ARRAYSIZE(UnattendInfPath), 2,
300 pSetupData->SourcePath.Buffer, L"unattend.inf");
301 if (DoesFileExist(NULL, UnattendInfPath) == FALSE)
302 {
303 DPRINT("Does not exist: %S\n", UnattendInfPath);
304 return;
305 }
306
307 RtlInitUnicodeString(&FileName, PathBuffer);
308 InitializeObjectAttributes(&ObjectAttributes,
309 &FileName,
310 OBJ_CASE_INSENSITIVE | OBJ_OPENIF,
311 NULL,
312 NULL);
313 Status = NtOpenFile(&FileHandle,
314 FILE_APPEND_DATA | SYNCHRONIZE,
315 &ObjectAttributes,
316 &IoStatusBlock,
317 FILE_SHARE_READ,
318 FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE);
319 if (!NT_SUCCESS(Status))
320 {
321 DPRINT1("Cannot load %S as an INI file!\n", PathBuffer);
322 return;
323 }
324
325 /* Query the file size */
326 Status = NtQueryInformationFile(FileHandle,
327 &IoStatusBlock,
328 &FileInfo,
329 sizeof(FileInfo),
330 FileStandardInformation);
331 if (!NT_SUCCESS(Status))
332 {
333 DPRINT("NtQueryInformationFile() failed (Status %lx)\n", Status);
334 FileInfo.EndOfFile.QuadPart = 0ULL;
335 }
336
337 Status = OpenAndMapFile(NULL,
338 UnattendInfPath,
339 &UnattendFileHandle,
340 &SectionHandle,
341 &ViewBase,
342 &FileSize,
343 FALSE);
344 if (!NT_SUCCESS(Status))
345 {
346 DPRINT1("Cannot load %S !\n", UnattendInfPath);
347 NtClose(FileHandle);
348 return;
349 }
350
351 /* Write to the INI file */
352
353 /* "\r\n" */
354 Status = NtWriteFile(FileHandle,
355 NULL,
356 NULL,
357 NULL,
358 &IoStatusBlock,
359 (PVOID)CrLf,
360 2 * sizeof(CHAR), // 2 * sizeof(WCHAR),
361 &FileInfo.EndOfFile,
362 NULL);
363
364 Status = NtWriteFile(FileHandle,
365 NULL,
366 NULL,
367 NULL,
368 &IoStatusBlock,
369 ViewBase,
370 FileSize,
371 NULL,
372 NULL);
373 if (!NT_SUCCESS(Status))
374 {
375 DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
376 }
377
378 /* Finally, unmap and close the file */
379 UnMapAndCloseFile(UnattendFileHandle, SectionHandle, ViewBase);
380
381 NtClose(FileHandle);
382 #endif
383 }
384
385 NTSTATUS
386 GetSourcePaths(
387 OUT PUNICODE_STRING SourcePath,
388 OUT PUNICODE_STRING SourceRootPath,
389 OUT PUNICODE_STRING SourceRootDir)
390 {
391 NTSTATUS Status;
392 HANDLE Handle;
393 OBJECT_ATTRIBUTES ObjectAttributes;
394 UCHAR ImageFileBuffer[sizeof(UNICODE_STRING) + MAX_PATH * sizeof(WCHAR)];
395 PUNICODE_STRING InstallSourcePath = (PUNICODE_STRING)&ImageFileBuffer;
396 WCHAR SystemRootBuffer[MAX_PATH] = L"";
397 UNICODE_STRING SystemRootPath = RTL_CONSTANT_STRING(L"\\SystemRoot");
398 ULONG BufferSize;
399 PWCHAR Ptr;
400
401 /* Determine the installation source path via the full path of the installer */
402 RtlInitEmptyUnicodeString(InstallSourcePath,
403 (PWSTR)((ULONG_PTR)ImageFileBuffer + sizeof(UNICODE_STRING)),
404 sizeof(ImageFileBuffer) - sizeof(UNICODE_STRING)
405 /* Reserve space for a NULL terminator */ - sizeof(UNICODE_NULL));
406 BufferSize = sizeof(ImageFileBuffer);
407 Status = NtQueryInformationProcess(NtCurrentProcess(),
408 ProcessImageFileName,
409 InstallSourcePath,
410 BufferSize,
411 NULL);
412 // STATUS_INFO_LENGTH_MISMATCH or STATUS_BUFFER_TOO_SMALL ?
413 if (!NT_SUCCESS(Status))
414 return Status;
415
416 /* Manually NULL-terminate */
417 InstallSourcePath->Buffer[InstallSourcePath->Length / sizeof(WCHAR)] = UNICODE_NULL;
418
419 /* Strip the trailing file name */
420 Ptr = wcsrchr(InstallSourcePath->Buffer, OBJ_NAME_PATH_SEPARATOR);
421 if (Ptr)
422 *Ptr = UNICODE_NULL;
423 InstallSourcePath->Length = wcslen(InstallSourcePath->Buffer) * sizeof(WCHAR);
424
425
426 /*
427 * Now resolve the full path to \SystemRoot. In case it prefixes
428 * the installation source path determined from the full path of
429 * the installer, we use instead the resolved \SystemRoot as the
430 * installation source path.
431 * Otherwise, we use instead the path from the full installer path.
432 */
433
434 InitializeObjectAttributes(&ObjectAttributes,
435 &SystemRootPath,
436 OBJ_CASE_INSENSITIVE,
437 NULL,
438 NULL);
439
440 Status = NtOpenSymbolicLinkObject(&Handle,
441 SYMBOLIC_LINK_QUERY,
442 &ObjectAttributes);
443 if (!NT_SUCCESS(Status))
444 {
445 /*
446 * We failed at opening the \SystemRoot link (usually due to wrong
447 * access rights). Do not consider this as a fatal error, but use
448 * instead the image file path as the installation source path.
449 */
450 DPRINT1("NtOpenSymbolicLinkObject(%wZ) failed with Status 0x%08lx\n",
451 &SystemRootPath, Status);
452 goto InitPaths;
453 }
454
455 RtlInitEmptyUnicodeString(&SystemRootPath,
456 SystemRootBuffer,
457 sizeof(SystemRootBuffer));
458
459 Status = NtQuerySymbolicLinkObject(Handle,
460 &SystemRootPath,
461 &BufferSize);
462 NtClose(Handle);
463
464 if (!NT_SUCCESS(Status))
465 return Status; // Unexpected error
466
467 /* Check whether the resolved \SystemRoot is a prefix of the image file path */
468 if (RtlPrefixUnicodeString(&SystemRootPath, InstallSourcePath, TRUE))
469 {
470 /* Yes it is, so we use instead SystemRoot as the installation source path */
471 InstallSourcePath = &SystemRootPath;
472 }
473
474
475 InitPaths:
476 /*
477 * Retrieve the different source path components
478 */
479 RtlCreateUnicodeString(SourcePath, InstallSourcePath->Buffer);
480
481 /* Strip trailing directory */
482 Ptr = wcsrchr(InstallSourcePath->Buffer, OBJ_NAME_PATH_SEPARATOR);
483 if (Ptr)
484 {
485 RtlCreateUnicodeString(SourceRootDir, Ptr);
486 *Ptr = UNICODE_NULL;
487 }
488 else
489 {
490 RtlCreateUnicodeString(SourceRootDir, L"");
491 }
492
493 RtlCreateUnicodeString(SourceRootPath, InstallSourcePath->Buffer);
494
495 return STATUS_SUCCESS;
496 }
497
498 ERROR_NUMBER
499 LoadSetupInf(
500 OUT HINF* SetupInf,
501 IN OUT PUSETUP_DATA pSetupData)
502 {
503 INFCONTEXT Context;
504 UINT ErrorLine;
505 INT IntValue;
506 PWCHAR Value;
507 WCHAR FileNameBuffer[MAX_PATH];
508
509 CombinePaths(FileNameBuffer, ARRAYSIZE(FileNameBuffer), 2,
510 pSetupData->SourcePath.Buffer, L"txtsetup.sif");
511
512 DPRINT("SetupInf path: '%S'\n", FileNameBuffer);
513
514 *SetupInf = SetupOpenInfFileExW(FileNameBuffer,
515 NULL,
516 INF_STYLE_WIN4 | INF_STYLE_OLDNT,
517 pSetupData->LanguageId,
518 &ErrorLine);
519
520 if (*SetupInf == INVALID_HANDLE_VALUE)
521 return ERROR_LOAD_TXTSETUPSIF;
522
523 /* Open 'Version' section */
524 if (!SetupFindFirstLineW(*SetupInf, L"Version", L"Signature", &Context))
525 return ERROR_CORRUPT_TXTSETUPSIF;
526
527 /* Get pointer 'Signature' key */
528 if (!INF_GetData(&Context, NULL, &Value))
529 return ERROR_CORRUPT_TXTSETUPSIF;
530
531 /* Check 'Signature' string */
532 if (_wcsicmp(Value, L"$ReactOS$") != 0)
533 {
534 INF_FreeData(Value);
535 return ERROR_SIGNATURE_TXTSETUPSIF;
536 }
537
538 INF_FreeData(Value);
539
540 /* Open 'DiskSpaceRequirements' section */
541 if (!SetupFindFirstLineW(*SetupInf, L"DiskSpaceRequirements", L"FreeSysPartDiskSpace", &Context))
542 return ERROR_CORRUPT_TXTSETUPSIF;
543
544 pSetupData->RequiredPartitionDiskSpace = ~0;
545
546 /* Get the 'FreeSysPartDiskSpace' value */
547 if (!SetupGetIntField(&Context, 1, &IntValue))
548 return ERROR_CORRUPT_TXTSETUPSIF;
549
550 pSetupData->RequiredPartitionDiskSpace = (ULONG)IntValue;
551
552 //
553 // TODO: Support "SetupSourceDevice" and "SetupSourcePath" in txtsetup.sif
554 // See CORE-9023
555 //
556
557 /* Search for 'DefaultPath' in the 'SetupData' section */
558 if (SetupFindFirstLineW(*SetupInf, L"SetupData", L"DefaultPath", &Context))
559 {
560 /* Get pointer 'DefaultPath' key */
561 if (!INF_GetData(&Context, NULL, &Value))
562 return ERROR_CORRUPT_TXTSETUPSIF;
563
564 wcscpy(pSetupData->InstallationDirectory, Value);
565 INF_FreeData(Value);
566 }
567
568 return ERROR_SUCCESS;
569 }
570
571 /* EOF */