[USETUP][SETUPLIB] Code refactoring.
[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 if (DoesFileExist(NULL, UnattendInfPath) == FALSE)
45 {
46 DPRINT("Does not exist: %S\n", UnattendInfPath);
47 return;
48 }
49
50 /* Load 'unattend.inf' from installation media */
51 UnattendInf = SetupOpenInfFileExW(UnattendInfPath,
52 NULL,
53 INF_STYLE_WIN4,
54 pSetupData->LanguageId,
55 &ErrorLine);
56
57 if (UnattendInf == INVALID_HANDLE_VALUE)
58 {
59 DPRINT("SetupOpenInfFileExW() failed\n");
60 return;
61 }
62
63 /* Open 'Unattend' section */
64 if (!SetupFindFirstLineW(UnattendInf, L"Unattend", L"Signature", &Context))
65 {
66 DPRINT("SetupFindFirstLineW() failed for section 'Unattend'\n");
67 goto Quit;
68 }
69
70 /* Get pointer 'Signature' key */
71 if (!INF_GetData(&Context, NULL, &Value))
72 {
73 DPRINT("INF_GetData() failed for key 'Signature'\n");
74 goto Quit;
75 }
76
77 /* Check 'Signature' string */
78 if (_wcsicmp(Value, L"$ReactOS$") != 0)
79 {
80 DPRINT("Signature not $ReactOS$\n");
81 INF_FreeData(Value);
82 goto Quit;
83 }
84
85 INF_FreeData(Value);
86
87 /* Check if Unattend setup is enabled */
88 if (!SetupFindFirstLineW(UnattendInf, L"Unattend", L"UnattendSetupEnabled", &Context))
89 {
90 DPRINT("Can't find key 'UnattendSetupEnabled'\n");
91 goto Quit;
92 }
93
94 if (!INF_GetData(&Context, NULL, &Value))
95 {
96 DPRINT("Can't read key 'UnattendSetupEnabled'\n");
97 goto Quit;
98 }
99
100 if (_wcsicmp(Value, L"yes") != 0)
101 {
102 DPRINT("Unattend setup is disabled by 'UnattendSetupEnabled' key!\n");
103 INF_FreeData(Value);
104 goto Quit;
105 }
106
107 INF_FreeData(Value);
108
109 /* Search for 'DestinationDiskNumber' in the 'Unattend' section */
110 if (!SetupFindFirstLineW(UnattendInf, L"Unattend", L"DestinationDiskNumber", &Context))
111 {
112 DPRINT("SetupFindFirstLine() failed for key 'DestinationDiskNumber'\n");
113 goto Quit;
114 }
115
116 if (!SetupGetIntField(&Context, 1, &IntValue))
117 {
118 DPRINT("SetupGetIntField() failed for key 'DestinationDiskNumber'\n");
119 goto Quit;
120 }
121
122 pSetupData->DestinationDiskNumber = (LONG)IntValue;
123
124 /* Search for 'DestinationPartitionNumber' in the 'Unattend' section */
125 if (!SetupFindFirstLineW(UnattendInf, L"Unattend", L"DestinationPartitionNumber", &Context))
126 {
127 DPRINT("SetupFindFirstLine() failed for key 'DestinationPartitionNumber'\n");
128 goto Quit;
129 }
130
131 if (!SetupGetIntField(&Context, 1, &IntValue))
132 {
133 DPRINT("SetupGetIntField() failed for key 'DestinationPartitionNumber'\n");
134 goto Quit;
135 }
136
137 pSetupData->DestinationPartitionNumber = (LONG)IntValue;
138
139 /* Search for 'InstallationDirectory' in the 'Unattend' section (optional) */
140 if (SetupFindFirstLineW(UnattendInf, L"Unattend", L"InstallationDirectory", &Context))
141 {
142 /* Get pointer 'InstallationDirectory' key */
143 if (!INF_GetData(&Context, NULL, &Value))
144 {
145 DPRINT("INF_GetData() failed for key 'InstallationDirectory'\n");
146 goto Quit;
147 }
148 wcscpy(pSetupData->InstallationDirectory, Value);
149 INF_FreeData(Value);
150 }
151
152 IsUnattendedSetup = TRUE;
153 DPRINT("Running unattended setup\n");
154
155 /* Search for 'MBRInstallType' in the 'Unattend' section */
156 pSetupData->MBRInstallType = -1;
157 if (SetupFindFirstLineW(UnattendInf, L"Unattend", L"MBRInstallType", &Context))
158 {
159 if (SetupGetIntField(&Context, 1, &IntValue))
160 {
161 pSetupData->MBRInstallType = IntValue;
162 }
163 }
164
165 /* Search for 'FormatPartition' in the 'Unattend' section */
166 pSetupData->FormatPartition = 0;
167 if (SetupFindFirstLineW(UnattendInf, L"Unattend", L"FormatPartition", &Context))
168 {
169 if (SetupGetIntField(&Context, 1, &IntValue))
170 {
171 pSetupData->FormatPartition = IntValue;
172 }
173 }
174
175 pSetupData->AutoPartition = 0;
176 if (SetupFindFirstLineW(UnattendInf, L"Unattend", L"AutoPartition", &Context))
177 {
178 if (SetupGetIntField(&Context, 1, &IntValue))
179 {
180 pSetupData->AutoPartition = IntValue;
181 }
182 }
183
184 /* Search for LocaleID in the 'Unattend' section */
185 if (SetupFindFirstLineW(UnattendInf, L"Unattend", L"LocaleID", &Context))
186 {
187 if (INF_GetData(&Context, NULL, &Value))
188 {
189 LONG Id = wcstol(Value, NULL, 16);
190 swprintf(pSetupData->LocaleID, L"%08lx", Id);
191 INF_FreeData(Value);
192 }
193 }
194
195 Quit:
196 SetupCloseInfFile(UnattendInf);
197 }
198
199 VOID
200 InstallSetupInfFile(
201 IN OUT PUSETUP_DATA pSetupData)
202 {
203 NTSTATUS Status;
204 PINICACHE IniCache;
205
206 #if 0 // HACK FIXME!
207 PINICACHE UnattendCache;
208 PINICACHEITERATOR Iterator;
209 #else
210 // PCWSTR CrLf = L"\r\n";
211 PCSTR CrLf = "\r\n";
212 HANDLE FileHandle, UnattendFileHandle, SectionHandle;
213 FILE_STANDARD_INFORMATION FileInfo;
214 ULONG FileSize;
215 PVOID ViewBase;
216 UNICODE_STRING FileName;
217 OBJECT_ATTRIBUTES ObjectAttributes;
218 IO_STATUS_BLOCK IoStatusBlock;
219 #endif
220
221 PINICACHESECTION IniSection;
222 WCHAR PathBuffer[MAX_PATH];
223 WCHAR UnattendInfPath[MAX_PATH];
224
225 /* Create a $winnt$.inf file with default entries */
226 IniCache = IniCacheCreate();
227 if (!IniCache)
228 return;
229
230 IniSection = IniCacheAppendSection(IniCache, L"SetupParams");
231 if (IniSection)
232 {
233 /* Key "skipmissingfiles" */
234 // StringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
235 // L"\"%s\"", L"WinNt5.2");
236 // IniCacheInsertKey(IniSection, NULL, INSERT_LAST,
237 // L"Version", PathBuffer);
238 }
239
240 IniSection = IniCacheAppendSection(IniCache, L"Data");
241 if (IniSection)
242 {
243 StringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
244 L"\"%s\"", IsUnattendedSetup ? L"yes" : L"no");
245 IniCacheInsertKey(IniSection, NULL, INSERT_LAST,
246 L"UnattendedInstall", PathBuffer);
247
248 // "floppylessbootpath" (yes/no)
249
250 StringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
251 L"\"%s\"", L"winnt");
252 IniCacheInsertKey(IniSection, NULL, INSERT_LAST,
253 L"ProductType", PathBuffer);
254
255 StringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
256 L"\"%s\\\"", pSetupData->SourceRootPath.Buffer);
257 IniCacheInsertKey(IniSection, NULL, INSERT_LAST,
258 L"SourcePath", PathBuffer);
259
260 // "floppyless" ("0")
261 }
262
263 #if 0
264
265 /* TODO: Append the standard unattend.inf file */
266 CombinePaths(UnattendInfPath, ARRAYSIZE(UnattendInfPath), 2, pSetupData->SourcePath.Buffer, L"unattend.inf");
267 if (DoesFileExist(NULL, UnattendInfPath) == FALSE)
268 {
269 DPRINT("Does not exist: %S\n", UnattendInfPath);
270 goto Quit;
271 }
272
273 Status = IniCacheLoad(&UnattendCache, UnattendInfPath, FALSE);
274 if (!NT_SUCCESS(Status))
275 {
276 DPRINT1("Cannot load %S as an INI file!\n", UnattendInfPath);
277 goto Quit;
278 }
279
280 IniCacheDestroy(UnattendCache);
281
282 Quit:
283 CombinePaths(PathBuffer, ARRAYSIZE(PathBuffer), 2,
284 pSetupData->DestinationPath.Buffer, L"System32\\$winnt$.inf");
285 IniCacheSave(IniCache, PathBuffer);
286 IniCacheDestroy(IniCache);
287
288 #else
289
290 CombinePaths(PathBuffer, ARRAYSIZE(PathBuffer), 2,
291 pSetupData->DestinationPath.Buffer, L"System32\\$winnt$.inf");
292 IniCacheSave(IniCache, PathBuffer);
293 IniCacheDestroy(IniCache);
294
295 /* TODO: Append the standard unattend.inf file */
296 CombinePaths(UnattendInfPath, ARRAYSIZE(UnattendInfPath), 2,
297 pSetupData->SourcePath.Buffer, L"unattend.inf");
298 if (DoesFileExist(NULL, UnattendInfPath) == FALSE)
299 {
300 DPRINT("Does not exist: %S\n", UnattendInfPath);
301 return;
302 }
303
304 RtlInitUnicodeString(&FileName, PathBuffer);
305 InitializeObjectAttributes(&ObjectAttributes,
306 &FileName,
307 OBJ_CASE_INSENSITIVE | OBJ_OPENIF,
308 NULL,
309 NULL);
310 Status = NtOpenFile(&FileHandle,
311 FILE_APPEND_DATA | SYNCHRONIZE,
312 &ObjectAttributes,
313 &IoStatusBlock,
314 FILE_SHARE_READ,
315 FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE);
316 if (!NT_SUCCESS(Status))
317 {
318 DPRINT1("Cannot load %S as an INI file!\n", PathBuffer);
319 return;
320 }
321
322 /* Query the file size */
323 Status = NtQueryInformationFile(FileHandle,
324 &IoStatusBlock,
325 &FileInfo,
326 sizeof(FileInfo),
327 FileStandardInformation);
328 if (!NT_SUCCESS(Status))
329 {
330 DPRINT("NtQueryInformationFile() failed (Status %lx)\n", Status);
331 FileInfo.EndOfFile.QuadPart = 0ULL;
332 }
333
334 Status = OpenAndMapFile(NULL,
335 UnattendInfPath,
336 &UnattendFileHandle,
337 &SectionHandle,
338 &ViewBase,
339 &FileSize,
340 FALSE);
341 if (!NT_SUCCESS(Status))
342 {
343 DPRINT1("Cannot load %S !\n", UnattendInfPath);
344 NtClose(FileHandle);
345 return;
346 }
347
348 /* Write to the INI file */
349
350 /* "\r\n" */
351 Status = NtWriteFile(FileHandle,
352 NULL,
353 NULL,
354 NULL,
355 &IoStatusBlock,
356 (PVOID)CrLf,
357 2 * sizeof(CHAR), // 2 * sizeof(WCHAR),
358 &FileInfo.EndOfFile,
359 NULL);
360
361 Status = NtWriteFile(FileHandle,
362 NULL,
363 NULL,
364 NULL,
365 &IoStatusBlock,
366 ViewBase,
367 FileSize,
368 NULL,
369 NULL);
370 if (!NT_SUCCESS(Status))
371 {
372 DPRINT("NtWriteFile() failed (Status %lx)\n", Status);
373 }
374
375 /* Finally, unmap and close the file */
376 UnMapFile(SectionHandle, ViewBase);
377 NtClose(UnattendFileHandle);
378
379 NtClose(FileHandle);
380 #endif
381 }
382
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 OBJECT_ATTRIBUTES ObjectAttributes;
393 UNICODE_STRING LinkName = RTL_CONSTANT_STRING(L"\\SystemRoot");
394 UNICODE_STRING SourceName;
395 WCHAR SourceBuffer[MAX_PATH] = L"";
396 HANDLE Handle;
397 ULONG Length;
398 PWCHAR Ptr;
399
400 InitializeObjectAttributes(&ObjectAttributes,
401 &LinkName,
402 OBJ_CASE_INSENSITIVE,
403 NULL,
404 NULL);
405
406 Status = NtOpenSymbolicLinkObject(&Handle,
407 SYMBOLIC_LINK_ALL_ACCESS,
408 &ObjectAttributes);
409 if (!NT_SUCCESS(Status))
410 return Status;
411
412 RtlInitEmptyUnicodeString(&SourceName, SourceBuffer, sizeof(SourceBuffer));
413
414 Status = NtQuerySymbolicLinkObject(Handle,
415 &SourceName,
416 &Length);
417 NtClose(Handle);
418
419 if (!NT_SUCCESS(Status))
420 return Status;
421
422 RtlCreateUnicodeString(SourcePath,
423 SourceName.Buffer);
424
425 /* Strip trailing directory */
426 Ptr = wcsrchr(SourceName.Buffer, OBJ_NAME_PATH_SEPARATOR);
427 if (Ptr)
428 {
429 RtlCreateUnicodeString(SourceRootDir, Ptr);
430 *Ptr = UNICODE_NULL;
431 }
432 else
433 {
434 RtlCreateUnicodeString(SourceRootDir, L"");
435 }
436
437 RtlCreateUnicodeString(SourceRootPath,
438 SourceName.Buffer);
439
440 return STATUS_SUCCESS;
441 }
442
443
444 ERROR_NUMBER
445 LoadSetupInf(
446 OUT HINF* SetupInf,
447 IN OUT PUSETUP_DATA pSetupData)
448 {
449 INFCONTEXT Context;
450 UINT ErrorLine;
451 INT IntValue;
452 PWCHAR Value;
453 WCHAR FileNameBuffer[MAX_PATH];
454
455 CombinePaths(FileNameBuffer, ARRAYSIZE(FileNameBuffer), 2,
456 pSetupData->SourcePath.Buffer, L"txtsetup.sif");
457
458 *SetupInf = SetupOpenInfFileExW(FileNameBuffer,
459 NULL,
460 INF_STYLE_WIN4,
461 pSetupData->LanguageId,
462 &ErrorLine);
463
464 if (*SetupInf == INVALID_HANDLE_VALUE)
465 return ERROR_LOAD_TXTSETUPSIF;
466
467 /* Open 'Version' section */
468 if (!SetupFindFirstLineW(*SetupInf, L"Version", L"Signature", &Context))
469 return ERROR_CORRUPT_TXTSETUPSIF;
470
471 /* Get pointer 'Signature' key */
472 if (!INF_GetData(&Context, NULL, &Value))
473 return ERROR_CORRUPT_TXTSETUPSIF;
474
475 /* Check 'Signature' string */
476 if (_wcsicmp(Value, L"$ReactOS$") != 0)
477 {
478 INF_FreeData(Value);
479 return ERROR_SIGNATURE_TXTSETUPSIF;
480 }
481
482 INF_FreeData(Value);
483
484 /* Open 'DiskSpaceRequirements' section */
485 if (!SetupFindFirstLineW(*SetupInf, L"DiskSpaceRequirements", L"FreeSysPartDiskSpace", &Context))
486 return ERROR_CORRUPT_TXTSETUPSIF;
487
488 pSetupData->RequiredPartitionDiskSpace = ~0;
489
490 /* Get the 'FreeSysPartDiskSpace' value */
491 if (!SetupGetIntField(&Context, 1, &IntValue))
492 return ERROR_CORRUPT_TXTSETUPSIF;
493
494 pSetupData->RequiredPartitionDiskSpace = (ULONG)IntValue;
495
496 //
497 // TODO: Support "SetupSourceDevice" and "SetupSourcePath" in txtsetup.sif
498 // See CORE-9023
499 //
500
501 /* Search for 'DefaultPath' in the 'SetupData' section */
502 if (SetupFindFirstLineW(*SetupInf, L"SetupData", L"DefaultPath", &Context))
503 {
504 /* Get pointer 'DefaultPath' key */
505 if (!INF_GetData(&Context, NULL, &Value))
506 return ERROR_CORRUPT_TXTSETUPSIF;
507
508 wcscpy(pSetupData->InstallationDirectory, Value);
509 INF_FreeData(Value);
510 }
511
512 return ERROR_SUCCESS;
513 }
514
515 /* EOF */