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)
10 /* INCLUDES *****************************************************************/
26 /* GLOBALS ******************************************************************/
28 /* FUNCTIONS ****************************************************************/
32 IN OUT PUSETUP_DATA pSetupData
)
39 WCHAR UnattendInfPath
[MAX_PATH
];
41 CombinePaths(UnattendInfPath
, ARRAYSIZE(UnattendInfPath
), 2,
42 pSetupData
->SourcePath
.Buffer
, L
"unattend.inf");
44 DPRINT("UnattendInf path: '%S'\n", UnattendInfPath
);
46 if (DoesFileExist(NULL
, UnattendInfPath
) == FALSE
)
48 DPRINT("Does not exist: %S\n", UnattendInfPath
);
52 /* Load 'unattend.inf' from installation media */
53 UnattendInf
= SetupOpenInfFileExW(UnattendInfPath
,
56 pSetupData
->LanguageId
,
59 if (UnattendInf
== INVALID_HANDLE_VALUE
)
61 DPRINT("SetupOpenInfFileExW() failed\n");
65 /* Open 'Unattend' section */
66 if (!SetupFindFirstLineW(UnattendInf
, L
"Unattend", L
"Signature", &Context
))
68 DPRINT("SetupFindFirstLineW() failed for section 'Unattend'\n");
72 /* Get pointer 'Signature' key */
73 if (!INF_GetData(&Context
, NULL
, &Value
))
75 DPRINT("INF_GetData() failed for key 'Signature'\n");
79 /* Check 'Signature' string */
80 if (_wcsicmp(Value
, L
"$ReactOS$") != 0)
82 DPRINT("Signature not $ReactOS$\n");
89 /* Check if Unattend setup is enabled */
90 if (!SetupFindFirstLineW(UnattendInf
, L
"Unattend", L
"UnattendSetupEnabled", &Context
))
92 DPRINT("Can't find key 'UnattendSetupEnabled'\n");
96 if (!INF_GetData(&Context
, NULL
, &Value
))
98 DPRINT("Can't read key 'UnattendSetupEnabled'\n");
102 if (_wcsicmp(Value
, L
"yes") != 0)
104 DPRINT("Unattend setup is disabled by 'UnattendSetupEnabled' key!\n");
111 /* Search for 'DestinationDiskNumber' in the 'Unattend' section */
112 if (!SetupFindFirstLineW(UnattendInf
, L
"Unattend", L
"DestinationDiskNumber", &Context
))
114 DPRINT("SetupFindFirstLine() failed for key 'DestinationDiskNumber'\n");
118 if (!SetupGetIntField(&Context
, 1, &IntValue
))
120 DPRINT("SetupGetIntField() failed for key 'DestinationDiskNumber'\n");
124 pSetupData
->DestinationDiskNumber
= (LONG
)IntValue
;
126 /* Search for 'DestinationPartitionNumber' in the 'Unattend' section */
127 if (!SetupFindFirstLineW(UnattendInf
, L
"Unattend", L
"DestinationPartitionNumber", &Context
))
129 DPRINT("SetupFindFirstLine() failed for key 'DestinationPartitionNumber'\n");
133 if (!SetupGetIntField(&Context
, 1, &IntValue
))
135 DPRINT("SetupGetIntField() failed for key 'DestinationPartitionNumber'\n");
139 pSetupData
->DestinationPartitionNumber
= (LONG
)IntValue
;
141 /* Search for 'InstallationDirectory' in the 'Unattend' section (optional) */
142 if (SetupFindFirstLineW(UnattendInf
, L
"Unattend", L
"InstallationDirectory", &Context
))
144 /* Get pointer 'InstallationDirectory' key */
145 if (!INF_GetData(&Context
, NULL
, &Value
))
147 DPRINT("INF_GetData() failed for key 'InstallationDirectory'\n");
150 wcscpy(pSetupData
->InstallationDirectory
, Value
);
154 IsUnattendedSetup
= TRUE
;
155 DPRINT("Running unattended setup\n");
157 /* Search for 'MBRInstallType' in the 'Unattend' section */
158 pSetupData
->MBRInstallType
= -1;
159 if (SetupFindFirstLineW(UnattendInf
, L
"Unattend", L
"MBRInstallType", &Context
))
161 if (SetupGetIntField(&Context
, 1, &IntValue
))
163 pSetupData
->MBRInstallType
= IntValue
;
167 /* Search for 'FormatPartition' in the 'Unattend' section */
168 pSetupData
->FormatPartition
= 0;
169 if (SetupFindFirstLineW(UnattendInf
, L
"Unattend", L
"FormatPartition", &Context
))
171 if (SetupGetIntField(&Context
, 1, &IntValue
))
173 pSetupData
->FormatPartition
= IntValue
;
177 pSetupData
->AutoPartition
= 0;
178 if (SetupFindFirstLineW(UnattendInf
, L
"Unattend", L
"AutoPartition", &Context
))
180 if (SetupGetIntField(&Context
, 1, &IntValue
))
182 pSetupData
->AutoPartition
= IntValue
;
186 /* Search for LocaleID in the 'Unattend' section */
187 if (SetupFindFirstLineW(UnattendInf
, L
"Unattend", L
"LocaleID", &Context
))
189 if (INF_GetData(&Context
, NULL
, &Value
))
191 LONG Id
= wcstol(Value
, NULL
, 16);
192 swprintf(pSetupData
->LocaleID
, L
"%08lx", Id
);
198 SetupCloseInfFile(UnattendInf
);
203 IN OUT PUSETUP_DATA pSetupData
)
209 PINICACHE UnattendCache
;
210 PINICACHEITERATOR Iterator
;
212 // PCWSTR CrLf = L"\r\n";
214 HANDLE FileHandle
, UnattendFileHandle
, SectionHandle
;
215 FILE_STANDARD_INFORMATION FileInfo
;
218 UNICODE_STRING FileName
;
219 OBJECT_ATTRIBUTES ObjectAttributes
;
220 IO_STATUS_BLOCK IoStatusBlock
;
223 PINICACHESECTION IniSection
;
224 WCHAR PathBuffer
[MAX_PATH
];
225 WCHAR UnattendInfPath
[MAX_PATH
];
227 /* Create a $winnt$.inf file with default entries */
228 IniCache
= IniCacheCreate();
232 IniSection
= IniCacheAppendSection(IniCache
, L
"SetupParams");
235 /* Key "skipmissingfiles" */
236 // StringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
237 // L"\"%s\"", L"WinNt5.2");
238 // IniCacheInsertKey(IniSection, NULL, INSERT_LAST,
239 // L"Version", PathBuffer);
242 IniSection
= IniCacheAppendSection(IniCache
, L
"Data");
245 StringCchPrintfW(PathBuffer
, ARRAYSIZE(PathBuffer
),
246 L
"\"%s\"", IsUnattendedSetup
? L
"yes" : L
"no");
247 IniCacheInsertKey(IniSection
, NULL
, INSERT_LAST
,
248 L
"UnattendedInstall", PathBuffer
);
250 // "floppylessbootpath" (yes/no)
252 StringCchPrintfW(PathBuffer
, ARRAYSIZE(PathBuffer
),
253 L
"\"%s\"", L
"winnt");
254 IniCacheInsertKey(IniSection
, NULL
, INSERT_LAST
,
255 L
"ProductType", PathBuffer
);
257 StringCchPrintfW(PathBuffer
, ARRAYSIZE(PathBuffer
),
258 L
"\"%s\\\"", pSetupData
->SourceRootPath
.Buffer
);
259 IniCacheInsertKey(IniSection
, NULL
, INSERT_LAST
,
260 L
"SourcePath", PathBuffer
);
262 // "floppyless" ("0")
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
)
272 DPRINT("Does not exist: %S\n", UnattendInfPath
);
276 Status
= IniCacheLoad(&UnattendCache
, UnattendInfPath
, FALSE
);
277 if (!NT_SUCCESS(Status
))
279 DPRINT1("Cannot load %S as an INI file!\n", UnattendInfPath
);
283 IniCacheDestroy(UnattendCache
);
286 CombinePaths(PathBuffer
, ARRAYSIZE(PathBuffer
), 2,
287 pSetupData
->DestinationPath
.Buffer
, L
"System32\\$winnt$.inf");
288 IniCacheSave(IniCache
, PathBuffer
);
289 IniCacheDestroy(IniCache
);
293 CombinePaths(PathBuffer
, ARRAYSIZE(PathBuffer
), 2,
294 pSetupData
->DestinationPath
.Buffer
, L
"System32\\$winnt$.inf");
295 IniCacheSave(IniCache
, PathBuffer
);
296 IniCacheDestroy(IniCache
);
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
)
303 DPRINT("Does not exist: %S\n", UnattendInfPath
);
307 RtlInitUnicodeString(&FileName
, PathBuffer
);
308 InitializeObjectAttributes(&ObjectAttributes
,
310 OBJ_CASE_INSENSITIVE
| OBJ_OPENIF
,
313 Status
= NtOpenFile(&FileHandle
,
314 FILE_APPEND_DATA
| SYNCHRONIZE
,
318 FILE_SYNCHRONOUS_IO_NONALERT
| FILE_NON_DIRECTORY_FILE
);
319 if (!NT_SUCCESS(Status
))
321 DPRINT1("Cannot load %S as an INI file!\n", PathBuffer
);
325 /* Query the file size */
326 Status
= NtQueryInformationFile(FileHandle
,
330 FileStandardInformation
);
331 if (!NT_SUCCESS(Status
))
333 DPRINT("NtQueryInformationFile() failed (Status %lx)\n", Status
);
334 FileInfo
.EndOfFile
.QuadPart
= 0ULL;
337 Status
= OpenAndMapFile(NULL
,
344 if (!NT_SUCCESS(Status
))
346 DPRINT1("Cannot load %S !\n", UnattendInfPath
);
351 /* Write to the INI file */
354 Status
= NtWriteFile(FileHandle
,
360 2 * sizeof(CHAR
), // 2 * sizeof(WCHAR),
364 Status
= NtWriteFile(FileHandle
,
373 if (!NT_SUCCESS(Status
))
375 DPRINT("NtWriteFile() failed (Status %lx)\n", Status
);
378 /* Finally, unmap and close the file */
379 UnMapAndCloseFile(UnattendFileHandle
, SectionHandle
, ViewBase
);
387 OUT PUNICODE_STRING SourcePath
,
388 OUT PUNICODE_STRING SourceRootPath
,
389 OUT PUNICODE_STRING SourceRootDir
)
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");
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
,
412 // STATUS_INFO_LENGTH_MISMATCH or STATUS_BUFFER_TOO_SMALL ?
413 if (!NT_SUCCESS(Status
))
416 /* Manually NULL-terminate */
417 InstallSourcePath
->Buffer
[InstallSourcePath
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
419 /* Strip the trailing file name */
420 Ptr
= wcsrchr(InstallSourcePath
->Buffer
, OBJ_NAME_PATH_SEPARATOR
);
423 InstallSourcePath
->Length
= wcslen(InstallSourcePath
->Buffer
) * sizeof(WCHAR
);
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.
434 InitializeObjectAttributes(&ObjectAttributes
,
436 OBJ_CASE_INSENSITIVE
,
440 Status
= NtOpenSymbolicLinkObject(&Handle
,
443 if (!NT_SUCCESS(Status
))
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.
450 DPRINT1("NtOpenSymbolicLinkObject(%wZ) failed with Status 0x%08lx\n",
451 &SystemRootPath
, Status
);
455 RtlInitEmptyUnicodeString(&SystemRootPath
,
457 sizeof(SystemRootBuffer
));
459 Status
= NtQuerySymbolicLinkObject(Handle
,
464 if (!NT_SUCCESS(Status
))
465 return Status
; // Unexpected error
467 /* Check whether the resolved \SystemRoot is a prefix of the image file path */
468 if (RtlPrefixUnicodeString(&SystemRootPath
, InstallSourcePath
, TRUE
))
470 /* Yes it is, so we use instead SystemRoot as the installation source path */
471 InstallSourcePath
= &SystemRootPath
;
477 * Retrieve the different source path components
479 RtlCreateUnicodeString(SourcePath
, InstallSourcePath
->Buffer
);
481 /* Strip trailing directory */
482 Ptr
= wcsrchr(InstallSourcePath
->Buffer
, OBJ_NAME_PATH_SEPARATOR
);
485 RtlCreateUnicodeString(SourceRootDir
, Ptr
);
490 RtlCreateUnicodeString(SourceRootDir
, L
"");
493 RtlCreateUnicodeString(SourceRootPath
, InstallSourcePath
->Buffer
);
495 return STATUS_SUCCESS
;
501 IN OUT PUSETUP_DATA pSetupData
)
507 WCHAR FileNameBuffer
[MAX_PATH
];
509 CombinePaths(FileNameBuffer
, ARRAYSIZE(FileNameBuffer
), 2,
510 pSetupData
->SourcePath
.Buffer
, L
"txtsetup.sif");
512 DPRINT("SetupInf path: '%S'\n", FileNameBuffer
);
514 *SetupInf
= SetupOpenInfFileExW(FileNameBuffer
,
516 INF_STYLE_WIN4
| INF_STYLE_OLDNT
,
517 pSetupData
->LanguageId
,
520 if (*SetupInf
== INVALID_HANDLE_VALUE
)
521 return ERROR_LOAD_TXTSETUPSIF
;
523 /* Open 'Version' section */
524 if (!SetupFindFirstLineW(*SetupInf
, L
"Version", L
"Signature", &Context
))
525 return ERROR_CORRUPT_TXTSETUPSIF
;
527 /* Get pointer 'Signature' key */
528 if (!INF_GetData(&Context
, NULL
, &Value
))
529 return ERROR_CORRUPT_TXTSETUPSIF
;
531 /* Check 'Signature' string */
532 if (_wcsicmp(Value
, L
"$ReactOS$") != 0)
535 return ERROR_SIGNATURE_TXTSETUPSIF
;
540 /* Open 'DiskSpaceRequirements' section */
541 if (!SetupFindFirstLineW(*SetupInf
, L
"DiskSpaceRequirements", L
"FreeSysPartDiskSpace", &Context
))
542 return ERROR_CORRUPT_TXTSETUPSIF
;
544 pSetupData
->RequiredPartitionDiskSpace
= ~0;
546 /* Get the 'FreeSysPartDiskSpace' value */
547 if (!SetupGetIntField(&Context
, 1, &IntValue
))
548 return ERROR_CORRUPT_TXTSETUPSIF
;
550 pSetupData
->RequiredPartitionDiskSpace
= (ULONG
)IntValue
;
553 // TODO: Support "SetupSourceDevice" and "SetupSourcePath" in txtsetup.sif
557 /* Search for 'DefaultPath' in the 'SetupData' section */
558 if (SetupFindFirstLineW(*SetupInf
, L
"SetupData", L
"DefaultPath", &Context
))
560 /* Get pointer 'DefaultPath' key */
561 if (!INF_GetData(&Context
, NULL
, &Value
))
562 return ERROR_CORRUPT_TXTSETUPSIF
;
564 wcscpy(pSetupData
->InstallationDirectory
, Value
);
568 return ERROR_SUCCESS
;