2 * PROJECT: ReactOS Setup Library
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: NT 5.x family (MS Windows <= 2003, and ReactOS)
5 * operating systems detection code.
6 * COPYRIGHT: Copyright 2017-2018 Hermes Belusca-Maito
9 /* INCLUDES *****************************************************************/
13 #include "ntverrsrc.h"
14 // #include "arcname.h"
29 /* GLOBALS ******************************************************************/
31 /* Language-independent Vendor strings */
32 static const PCWSTR KnownVendors
[] = { L
"ReactOS", L
"Microsoft" };
35 /* FUNCTIONS ****************************************************************/
39 BOOL
IsWindowsOS(VOID
)
42 // Load the "SystemRoot\System32\Config\SOFTWARE" hive and mount it,
43 // then go to (SOFTWARE\\)Microsoft\\Windows NT\\CurrentVersion,
44 // check the REG_SZ value "ProductName" and see whether it's "Windows"
45 // or "ReactOS". One may also check the REG_SZ "CurrentVersion" value,
46 // the REG_SZ "SystemRoot" and "PathName" values (what are the differences??).
48 // Optionally, looking at the SYSTEM hive, CurrentControlSet\\Control,
49 // REG_SZ values "SystemBootDevice" (and "FirmwareBootDevice" ??)...
52 /* ReactOS reports as Windows NT 5.2 */
55 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE
,
56 L
"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
57 0, KEY_QUERY_VALUE
, &hKey
) == ERROR_SUCCESS
)
60 DWORD dwType
= 0, dwBufSize
= 0;
62 ret
= RegQueryValueExW(hKey
, L
"ProductName", NULL
, &dwType
, NULL
, &dwBufSize
);
63 if (ret
== ERROR_SUCCESS
&& dwType
== REG_SZ
)
65 LPTSTR lpszProductName
= (LPTSTR
)MemAlloc(0, dwBufSize
);
66 RegQueryValueExW(hKey
, L
"ProductName", NULL
, &dwType
, (LPBYTE
)lpszProductName
, &dwBufSize
);
68 bIsWindowsOS
= (FindSubStrI(lpszProductName
, _T("Windows")) != NULL
);
70 MemFree(lpszProductName
);
81 typedef enum _NTOS_BOOT_LOADER_TYPE
83 FreeLdr
, // ReactOS' FreeLDR
84 NtLdr
, // Windows <= 2k3 NTLDR
85 // BootMgr, // Vista+ BCD-oriented BOOTMGR
86 } NTOS_BOOT_LOADER_TYPE
;
88 typedef struct _NTOS_BOOT_LOADER_FILES
90 NTOS_BOOT_LOADER_TYPE Type
;
91 PCWSTR LoaderExecutable
;
92 PCWSTR LoaderConfigurationFile
;
93 // EnumerateInstallations;
94 } NTOS_BOOT_LOADER_FILES
, *PNTOS_BOOT_LOADER_FILES
;
96 // Question 1: What if config file is optional?
97 // Question 2: What if many config files are possible?
98 NTOS_BOOT_LOADER_FILES NtosBootLoaders
[] =
100 {FreeLdr
, L
"freeldr.sys", L
"freeldr.ini"},
101 {NtLdr
, L
"ntldr" , L
"boot.ini"},
102 {NtLdr
, L
"setupldr" , L
"txtsetup.sif"},
103 // {BootMgr, L"bootmgr" , ???}
108 IsValidNTOSInstallation_UStr(
109 IN PUNICODE_STRING SystemRootPath
);
112 IsValidNTOSInstallation(
113 IN PCWSTR SystemRoot
);
115 static PNTOS_INSTALLATION
116 FindExistingNTOSInstall(
117 IN PGENERIC_LIST List
,
118 IN PCWSTR SystemRootArcPath OPTIONAL
,
119 IN PUNICODE_STRING SystemRootNtPath OPTIONAL
// or PCWSTR ?
122 static PNTOS_INSTALLATION
124 IN PGENERIC_LIST List
,
125 IN PCWSTR SystemRootArcPath
,
126 IN PUNICODE_STRING SystemRootNtPath
, // or PCWSTR ?
127 IN PCWSTR PathComponent
, // Pointer inside SystemRootNtPath buffer
129 IN ULONG PartitionNumber
,
130 IN PPARTENTRY PartEntry OPTIONAL
,
131 IN PCWSTR InstallationName
);
134 FreeLdrEnumerateInstallations(
135 IN OUT PGENERIC_LIST List
,
136 IN PPARTLIST PartList
,
137 // IN PPARTENTRY PartEntry,
143 PINICACHEITERATOR Iterator
;
144 PINICACHESECTION IniSection
, OsIniSection
;
145 PWCHAR SectionName
, KeyData
;
146 UNICODE_STRING InstallName
;
148 PNTOS_INSTALLATION NtOsInstall
;
149 UNICODE_STRING SystemRootPath
;
150 WCHAR SystemRoot
[MAX_PATH
];
151 WCHAR InstallNameW
[MAX_PATH
];
153 /* Open an *existing* FreeLdr.ini configuration file */
154 Status
= IniCacheLoadFromMemory(&IniCache
, FileBuffer
, FileLength
, FALSE
);
155 if (!NT_SUCCESS(Status
))
158 /* Get the "Operating Systems" section */
159 IniSection
= IniCacheGetSection(IniCache
, L
"Operating Systems");
160 if (IniSection
== NULL
)
162 IniCacheDestroy(IniCache
);
163 return STATUS_UNSUCCESSFUL
;
166 /* Enumerate all the valid installations */
167 Iterator
= IniCacheFindFirstValue(IniSection
, &SectionName
, &KeyData
);
168 if (!Iterator
) goto Quit
;
171 // FIXME: Poor-man quotes removal (improvement over bootsup.c:UpdateFreeLoaderIni).
172 if (KeyData
[0] == L
'"')
174 /* Quoted name, copy up to the closing quote */
175 PWCHAR Begin
= &KeyData
[1];
176 PWCHAR End
= wcschr(Begin
, L
'"');
178 End
= Begin
+ wcslen(Begin
);
179 RtlInitEmptyUnicodeString(&InstallName
, Begin
, (ULONG_PTR
)End
- (ULONG_PTR
)Begin
);
180 InstallName
.Length
= InstallName
.MaximumLength
;
184 /* Non-quoted name, copy everything */
185 RtlInitUnicodeString(&InstallName
, KeyData
);
188 DPRINT1("Possible installation '%wZ' in OS section '%S'\n", &InstallName
, SectionName
);
190 /* Search for an existing ReactOS entry */
191 OsIniSection
= IniCacheGetSection(IniCache
, SectionName
);
195 /* Check for supported boot type "Windows2003" */
196 Status
= IniCacheGetKey(OsIniSection
, L
"BootType", &KeyData
);
197 if (NT_SUCCESS(Status
))
199 // TODO: What to do with "Windows" ; "WindowsNT40" ; "ReactOSSetup" ?
200 if ((KeyData
== NULL
) ||
201 ( (_wcsicmp(KeyData
, L
"Windows2003") != 0) &&
202 (_wcsicmp(KeyData
, L
"\"Windows2003\"") != 0) ))
204 /* This is not a ReactOS entry */
210 /* Certainly not a ReactOS installation */
214 /* BootType is Windows2003. Now check SystemPath. */
215 Status
= IniCacheGetKey(OsIniSection
, L
"SystemPath", &KeyData
);
216 if (!NT_SUCCESS(Status
))
218 DPRINT1(" A Win2k3 install '%wZ' without an ARC path?!\n", &InstallName
);
222 DPRINT1(" Found a candidate Win2k3 install '%wZ' with ARC path '%S'\n", &InstallName
, KeyData
);
224 // TODO: Normalize the ARC path.
227 * Check whether we already have an installation with this ARC path.
228 * If this is the case, stop there.
230 NtOsInstall
= FindExistingNTOSInstall(List
, KeyData
, NULL
);
233 DPRINT1(" An NTOS installation with name \"%S\" already exists in SystemRoot '%wZ'\n",
234 NtOsInstall
->InstallationName
, &NtOsInstall
->SystemArcPath
);
239 * Convert the ARC path into an NT path, from which we will deduce
240 * the real disk drive & partition on which the candidate installation
241 * resides, as well verifying whether it is indeed an NTOS installation.
243 RtlInitEmptyUnicodeString(&SystemRootPath
, SystemRoot
, sizeof(SystemRoot
));
244 if (!ArcPathToNtPath(&SystemRootPath
, KeyData
, PartList
))
246 DPRINT1("ArcPathToNtPath(%S) failed, skip the installation.\n", KeyData
);
250 DPRINT1("ArcPathToNtPath() succeeded: '%S' --> '%wZ'\n", KeyData
, &SystemRootPath
);
253 * Check whether we already have an installation with this NT path.
254 * If this is the case, stop there.
256 NtOsInstall
= FindExistingNTOSInstall(List
, NULL
/*KeyData*/, &SystemRootPath
);
259 DPRINT1(" An NTOS installation with name \"%S\" already exists in SystemRoot '%wZ'\n",
260 NtOsInstall
->InstallationName
, &NtOsInstall
->SystemNtPath
);
264 /* Set SystemRootPath */
265 DPRINT1("FreeLdrEnumerateInstallations: SystemRootPath: '%wZ'\n", &SystemRootPath
);
267 if (IsValidNTOSInstallation_UStr(&SystemRootPath
))
269 ULONG DiskNumber
= 0, PartitionNumber
= 0;
270 PCWSTR PathComponent
= NULL
;
271 PDISKENTRY DiskEntry
= NULL
;
272 PPARTENTRY PartEntry
= NULL
;
274 DPRINT1("Found a valid NTOS installation in SystemRoot ARC path '%S', NT path '%wZ'\n", KeyData
, &SystemRootPath
);
276 /* From the NT path, compute the disk, partition and path components */
277 if (NtPathToDiskPartComponents(SystemRootPath
.Buffer
, &DiskNumber
, &PartitionNumber
, &PathComponent
))
279 DPRINT1("SystemRootPath = '%wZ' points to disk #%d, partition #%d, path '%S'\n",
280 &SystemRootPath
, DiskNumber
, PartitionNumber
, PathComponent
);
282 /* Retrieve the corresponding disk and partition */
283 if (!GetDiskOrPartition(PartList
, DiskNumber
, PartitionNumber
, &DiskEntry
, &PartEntry
))
284 DPRINT1("GetDiskOrPartition(disk #%d, partition #%d) failed\n", DiskNumber
, PartitionNumber
);
288 DPRINT1("NtPathToDiskPartComponents(%wZ) failed\n", &SystemRootPath
);
291 if (PartEntry
&& PartEntry
->DriveLetter
)
293 /* We have retrieved a partition that is mounted */
294 StringCchPrintfW(InstallNameW
, ARRAYSIZE(InstallNameW
), L
"%C:%s \"%wZ\"",
295 PartEntry
->DriveLetter
, PathComponent
, &InstallName
);
299 /* We failed somewhere, just show the NT path */
300 StringCchPrintfW(InstallNameW
, ARRAYSIZE(InstallNameW
), L
"%wZ \"%wZ\"",
301 &SystemRootPath
, &InstallName
);
303 AddNTOSInstallation(List
, KeyData
, &SystemRootPath
, PathComponent
,
304 DiskNumber
, PartitionNumber
, PartEntry
,
308 while (IniCacheFindNextValue(Iterator
, &SectionName
, &KeyData
));
310 IniCacheFindClose(Iterator
);
313 IniCacheDestroy(IniCache
);
314 return STATUS_SUCCESS
;
318 NtLdrEnumerateInstallations(
319 IN OUT PGENERIC_LIST List
,
320 IN PPARTLIST PartList
,
321 // IN PPARTENTRY PartEntry,
327 PINICACHEITERATOR Iterator
;
328 PINICACHESECTION IniSection
;
329 PWCHAR SectionName
, KeyData
;
330 UNICODE_STRING InstallName
;
332 PNTOS_INSTALLATION NtOsInstall
;
333 UNICODE_STRING SystemRootPath
;
334 WCHAR SystemRoot
[MAX_PATH
];
335 WCHAR InstallNameW
[MAX_PATH
];
337 /* Open an *existing* FreeLdr.ini configuration file */
338 Status
= IniCacheLoadFromMemory(&IniCache
, FileBuffer
, FileLength
, FALSE
);
339 if (!NT_SUCCESS(Status
))
342 /* Get the "Operating Systems" section */
343 IniSection
= IniCacheGetSection(IniCache
, L
"operating systems");
344 if (IniSection
== NULL
)
346 IniCacheDestroy(IniCache
);
347 return STATUS_UNSUCCESSFUL
;
350 /* Enumerate all the valid installations */
351 Iterator
= IniCacheFindFirstValue(IniSection
, &SectionName
, &KeyData
);
352 if (!Iterator
) goto Quit
;
355 // FIXME: Poor-man quotes removal (improvement over bootsup.c:UpdateFreeLoaderIni).
356 if (KeyData
[0] == L
'"')
358 /* Quoted name, copy up to the closing quote */
359 PWCHAR Begin
= &KeyData
[1];
360 PWCHAR End
= wcschr(Begin
, L
'"');
362 End
= Begin
+ wcslen(Begin
);
363 RtlInitEmptyUnicodeString(&InstallName
, Begin
, (ULONG_PTR
)End
- (ULONG_PTR
)Begin
);
364 InstallName
.Length
= InstallName
.MaximumLength
;
368 /* Non-quoted name, copy everything */
369 RtlInitUnicodeString(&InstallName
, KeyData
);
372 DPRINT1("Possible installation '%wZ' with ARC path '%S'\n", &InstallName
, SectionName
);
374 DPRINT1(" Found a Win2k3 install '%wZ' with ARC path '%S'\n", &InstallName
, SectionName
);
376 // TODO: Normalize the ARC path.
379 * Check whether we already have an installation with this ARC path.
380 * If this is the case, stop there.
382 NtOsInstall
= FindExistingNTOSInstall(List
, SectionName
, NULL
);
385 DPRINT1(" An NTOS installation with name \"%S\" already exists in SystemRoot '%wZ'\n",
386 NtOsInstall
->InstallationName
, &NtOsInstall
->SystemArcPath
);
391 * Convert the ARC path into an NT path, from which we will deduce
392 * the real disk drive & partition on which the candidate installation
393 * resides, as well verifying whether it is indeed an NTOS installation.
395 RtlInitEmptyUnicodeString(&SystemRootPath
, SystemRoot
, sizeof(SystemRoot
));
396 if (!ArcPathToNtPath(&SystemRootPath
, SectionName
, PartList
))
398 DPRINT1("ArcPathToNtPath(%S) failed, skip the installation.\n", SectionName
);
402 DPRINT1("ArcPathToNtPath() succeeded: '%S' --> '%wZ'\n", SectionName
, &SystemRootPath
);
405 * Check whether we already have an installation with this NT path.
406 * If this is the case, stop there.
408 NtOsInstall
= FindExistingNTOSInstall(List
, NULL
/*SectionName*/, &SystemRootPath
);
411 DPRINT1(" An NTOS installation with name \"%S\" already exists in SystemRoot '%wZ'\n",
412 NtOsInstall
->InstallationName
, &NtOsInstall
->SystemNtPath
);
416 /* Set SystemRootPath */
417 DPRINT1("NtLdrEnumerateInstallations: SystemRootPath: '%wZ'\n", &SystemRootPath
);
419 if (IsValidNTOSInstallation_UStr(&SystemRootPath
))
421 ULONG DiskNumber
= 0, PartitionNumber
= 0;
422 PCWSTR PathComponent
= NULL
;
423 PDISKENTRY DiskEntry
= NULL
;
424 PPARTENTRY PartEntry
= NULL
;
426 DPRINT1("Found a valid NTOS installation in SystemRoot ARC path '%S', NT path '%wZ'\n", SectionName
, &SystemRootPath
);
428 /* From the NT path, compute the disk, partition and path components */
429 if (NtPathToDiskPartComponents(SystemRootPath
.Buffer
, &DiskNumber
, &PartitionNumber
, &PathComponent
))
431 DPRINT1("SystemRootPath = '%wZ' points to disk #%d, partition #%d, path '%S'\n",
432 &SystemRootPath
, DiskNumber
, PartitionNumber
, PathComponent
);
434 /* Retrieve the corresponding disk and partition */
435 if (!GetDiskOrPartition(PartList
, DiskNumber
, PartitionNumber
, &DiskEntry
, &PartEntry
))
436 DPRINT1("GetDiskOrPartition(disk #%d, partition #%d) failed\n", DiskNumber
, PartitionNumber
);
440 DPRINT1("NtPathToDiskPartComponents(%wZ) failed\n", &SystemRootPath
);
443 if (PartEntry
&& PartEntry
->DriveLetter
)
445 /* We have retrieved a partition that is mounted */
446 StringCchPrintfW(InstallNameW
, ARRAYSIZE(InstallNameW
), L
"%C:%s \"%wZ\"",
447 PartEntry
->DriveLetter
, PathComponent
, &InstallName
);
451 /* We failed somewhere, just show the NT path */
452 StringCchPrintfW(InstallNameW
, ARRAYSIZE(InstallNameW
), L
"%wZ \"%wZ\"",
453 &SystemRootPath
, &InstallName
);
455 AddNTOSInstallation(List
, SectionName
, &SystemRootPath
, PathComponent
,
456 DiskNumber
, PartitionNumber
, PartEntry
,
460 while (IniCacheFindNextValue(Iterator
, &SectionName
, &KeyData
));
462 IniCacheFindClose(Iterator
);
465 IniCacheDestroy(IniCache
);
466 return STATUS_SUCCESS
;
470 * FindSubStrI(PCWSTR str, PCWSTR strSearch) :
471 * Searches for a sub-string 'strSearch' inside 'str', similarly to what
472 * wcsstr(str, strSearch) does, but ignores the case during the comparisons.
474 PCWSTR
FindSubStrI(PCWSTR str
, PCWSTR strSearch
)
487 while (*s1
&& *s2
&& (towupper(*s1
) == towupper(*s2
)))
500 CheckForValidPEAndVendor(
501 IN HANDLE RootDirectory OPTIONAL
,
502 IN PCWSTR PathNameToFile
,
503 OUT PUNICODE_STRING VendorName
506 BOOLEAN Success
= FALSE
;
508 HANDLE FileHandle
, SectionHandle
;
511 PVOID VersionBuffer
= NULL
; // Read-only
515 if (VendorName
->MaximumLength
< sizeof(UNICODE_NULL
))
518 *VendorName
->Buffer
= UNICODE_NULL
;
519 VendorName
->Length
= 0;
521 Status
= OpenAndMapFile(RootDirectory
, PathNameToFile
,
522 &FileHandle
, &SectionHandle
, &ViewBase
, NULL
);
523 if (!NT_SUCCESS(Status
))
525 DPRINT1("Failed to open and map file '%S', Status 0x%08lx\n", PathNameToFile
, Status
);
526 return FALSE
; // Status;
529 /* Make sure it's a valid PE file */
530 if (!RtlImageNtHeader(ViewBase
))
532 DPRINT1("File '%S' does not seem to be a valid PE, bail out\n", PathNameToFile
);
533 Status
= STATUS_INVALID_IMAGE_FORMAT
;
538 * Search for a valid executable version and vendor.
539 * NOTE: The module is loaded as a data file, it should be marked as such.
541 Status
= NtGetVersionResource((PVOID
)((ULONG_PTR
)ViewBase
| 1), &VersionBuffer
, NULL
);
542 if (!NT_SUCCESS(Status
))
544 DPRINT1("Failed to get version resource for file '%S', Status 0x%08lx\n", PathNameToFile
, Status
);
548 Status
= NtVerQueryValue(VersionBuffer
, L
"\\VarFileInfo\\Translation", &pvData
, &BufLen
);
549 if (NT_SUCCESS(Status
))
551 USHORT wCodePage
= 0, wLangID
= 0;
552 WCHAR FileInfo
[MAX_PATH
];
554 wCodePage
= LOWORD(*(ULONG
*)pvData
);
555 wLangID
= HIWORD(*(ULONG
*)pvData
);
557 StringCchPrintfW(FileInfo
, ARRAYSIZE(FileInfo
),
558 L
"StringFileInfo\\%04X%04X\\CompanyName",
561 Status
= NtVerQueryValue(VersionBuffer
, FileInfo
, &pvData
, &BufLen
);
563 /* Fixup the Status in case pvData is NULL */
564 if (NT_SUCCESS(Status
) && !pvData
)
565 Status
= STATUS_NOT_FOUND
;
567 if (NT_SUCCESS(Status
) /*&& pvData*/)
569 /* BufLen includes the NULL terminator count */
570 DPRINT1("Found version vendor: \"%S\" for file '%S'\n", pvData
, PathNameToFile
);
572 StringCbCopyNW(VendorName
->Buffer
, VendorName
->MaximumLength
,
573 pvData
, BufLen
* sizeof(WCHAR
));
574 VendorName
->Length
= wcslen(VendorName
->Buffer
) * sizeof(WCHAR
);
580 if (!NT_SUCCESS(Status
))
581 DPRINT1("No version vendor found for file '%S'\n", PathNameToFile
);
584 /* Finally, unmap and close the file */
585 UnMapFile(SectionHandle
, ViewBase
);
592 // TODO: Instead of returning TRUE/FALSE, it would be nice to return
593 // a flag indicating:
594 // - whether the installation is actually valid;
595 // - if it's broken or not (aka. needs for repair, or just upgrading).
598 IsValidNTOSInstallationByHandle(
599 IN HANDLE SystemRootDirectory
)
601 BOOLEAN Success
= FALSE
;
603 UNICODE_STRING VendorName
;
604 WCHAR VendorNameBuffer
[MAX_PATH
];
606 /* Check for the existence of \SystemRoot\System32 */
607 if (!DoesPathExist(SystemRootDirectory
, L
"System32\\"))
609 // DPRINT1("Failed to open directory '%wZ', Status 0x%08lx\n", &FileName, Status);
613 /* Check for the existence of \SystemRoot\System32\drivers */
614 if (!DoesPathExist(SystemRootDirectory
, L
"System32\\drivers\\"))
616 // DPRINT1("Failed to open directory '%wZ', Status 0x%08lx\n", &FileName, Status);
620 /* Check for the existence of \SystemRoot\System32\config */
621 if (!DoesPathExist(SystemRootDirectory
, L
"System32\\config\\"))
623 // DPRINT1("Failed to open directory '%wZ', Status 0x%08lx\n", &FileName, Status);
629 * Check for the existence of SYSTEM and SOFTWARE hives in \SystemRoot\System32\config
630 * (but we don't check here whether they are actually valid).
632 if (!DoesFileExist(SystemRootDirectory
, L
"System32\\config\\SYSTEM"))
634 // DPRINT1("Failed to open file '%wZ', Status 0x%08lx\n", &FileName, Status);
637 if (!DoesFileExist(SystemRootDirectory
, L
"System32\\config\\SOFTWARE"))
639 // DPRINT1("Failed to open file '%wZ', Status 0x%08lx\n", &FileName, Status);
644 RtlInitEmptyUnicodeString(&VendorName
, VendorNameBuffer
, sizeof(VendorNameBuffer
));
646 /* Check for the existence of \SystemRoot\System32\ntoskrnl.exe and retrieves its vendor name */
647 Success
= CheckForValidPEAndVendor(SystemRootDirectory
, L
"System32\\ntoskrnl.exe", &VendorName
);
649 DPRINT1("Kernel file ntoskrnl.exe is either not a PE file, or does not have any vendor?\n");
651 /* The kernel gives the OS its flavour */
654 for (i
= 0; i
< ARRAYSIZE(KnownVendors
); ++i
)
656 Success
= !!FindSubStrI(VendorName
.Buffer
, KnownVendors
[i
]);
659 /* We have found a correct vendor combination */
660 DPRINT1("IsValidNTOSInstallation: We've got an NTOS installation from %S !\n", KnownVendors
[i
]);
666 /* OPTIONAL: Check for the existence of \SystemRoot\System32\ntkrnlpa.exe */
668 /* Check for the existence of \SystemRoot\System32\ntdll.dll and retrieves its vendor name */
669 Success
= CheckForValidPEAndVendor(SystemRootDirectory
, L
"System32\\ntdll.dll", &VendorName
);
671 DPRINT1("User-mode file ntdll.dll is either not a PE file, or does not have any vendor?\n");
674 for (i
= 0; i
< ARRAYSIZE(KnownVendors
); ++i
)
676 if (!!FindSubStrI(VendorName
.Buffer
, KnownVendors
[i
]))
678 /* We have found a correct vendor combination */
679 DPRINT1("IsValidNTOSInstallation: The user-mode file ntdll.dll is from %S\n", KnownVendors
[i
]);
689 IsValidNTOSInstallation_UStr(
690 IN PUNICODE_STRING SystemRootPath
)
693 OBJECT_ATTRIBUTES ObjectAttributes
;
694 IO_STATUS_BLOCK IoStatusBlock
;
695 HANDLE SystemRootDirectory
;
698 /* Open SystemRootPath */
699 InitializeObjectAttributes(&ObjectAttributes
,
701 OBJ_CASE_INSENSITIVE
,
704 Status
= NtOpenFile(&SystemRootDirectory
,
705 FILE_LIST_DIRECTORY
| SYNCHRONIZE
,
708 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
709 FILE_SYNCHRONOUS_IO_NONALERT
| FILE_DIRECTORY_FILE
);
710 if (!NT_SUCCESS(Status
))
712 DPRINT1("Failed to open SystemRoot '%wZ', Status 0x%08lx\n", SystemRootPath
, Status
);
716 Success
= IsValidNTOSInstallationByHandle(SystemRootDirectory
);
719 NtClose(SystemRootDirectory
);
724 IsValidNTOSInstallation(
725 IN PCWSTR SystemRoot
)
727 UNICODE_STRING SystemRootPath
;
728 RtlInitUnicodeString(&SystemRootPath
, SystemRoot
);
729 return IsValidNTOSInstallationByHandle(&SystemRootPath
);
734 IN PGENERIC_LIST List
)
736 PGENERIC_LIST_ENTRY Entry
;
737 PNTOS_INSTALLATION NtOsInstall
;
738 ULONG NtOsInstallsCount
= GetNumberOfListEntries(List
);
740 DPRINT1("There %s %d installation%s detected:\n",
741 NtOsInstallsCount
>= 2 ? "are" : "is",
743 NtOsInstallsCount
>= 2 ? "s" : "");
745 Entry
= GetFirstListEntry(List
);
748 NtOsInstall
= (PNTOS_INSTALLATION
)GetListEntryUserData(Entry
);
749 Entry
= GetNextListEntry(Entry
);
751 DPRINT1(" On disk #%d, partition #%d: Installation \"%S\" in SystemRoot '%wZ'\n",
752 NtOsInstall
->DiskNumber
, NtOsInstall
->PartitionNumber
,
753 NtOsInstall
->InstallationName
, &NtOsInstall
->SystemNtPath
);
759 static PNTOS_INSTALLATION
760 FindExistingNTOSInstall(
761 IN PGENERIC_LIST List
,
762 IN PCWSTR SystemRootArcPath OPTIONAL
,
763 IN PUNICODE_STRING SystemRootNtPath OPTIONAL
// or PCWSTR ?
766 PGENERIC_LIST_ENTRY Entry
;
767 PNTOS_INSTALLATION NtOsInstall
;
768 UNICODE_STRING SystemArcPath
;
771 * We search either via ARC path or NT path.
772 * If both pointers are NULL then we fail straight away.
774 if (!SystemRootArcPath
&& !SystemRootNtPath
)
777 RtlInitUnicodeString(&SystemArcPath
, SystemRootArcPath
);
779 Entry
= GetFirstListEntry(List
);
782 NtOsInstall
= (PNTOS_INSTALLATION
)GetListEntryUserData(Entry
);
783 Entry
= GetNextListEntry(Entry
);
786 * Note that if both ARC paths are equal, then the corresponding
787 * NT paths must be the same. However, two ARC paths may be different
788 * but resolve into the same NT path.
790 if ( (SystemRootArcPath
&&
791 RtlEqualUnicodeString(&NtOsInstall
->SystemArcPath
,
792 &SystemArcPath
, TRUE
)) ||
794 RtlEqualUnicodeString(&NtOsInstall
->SystemNtPath
,
795 SystemRootNtPath
, TRUE
)) )
805 static PNTOS_INSTALLATION
807 IN PGENERIC_LIST List
,
808 IN PCWSTR SystemRootArcPath
,
809 IN PUNICODE_STRING SystemRootNtPath
, // or PCWSTR ?
810 IN PCWSTR PathComponent
, // Pointer inside SystemRootNtPath buffer
812 IN ULONG PartitionNumber
,
813 IN PPARTENTRY PartEntry OPTIONAL
,
814 IN PCWSTR InstallationName
)
816 PNTOS_INSTALLATION NtOsInstall
;
817 SIZE_T ArcPathLength
, NtPathLength
;
818 CHAR InstallNameA
[MAX_PATH
];
820 /* Is there already any installation with these settings? */
821 NtOsInstall
= FindExistingNTOSInstall(List
, SystemRootArcPath
, SystemRootNtPath
);
824 DPRINT1("An NTOS installation with name \"%S\" already exists on disk #%d, partition #%d, in SystemRoot '%wZ'\n",
825 NtOsInstall
->InstallationName
, NtOsInstall
->DiskNumber
, NtOsInstall
->PartitionNumber
, &NtOsInstall
->SystemNtPath
);
827 // NOTE: We may use its "IsDefault" attribute, and only keep the entries that have IsDefault == TRUE...
828 // Setting IsDefault to TRUE would imply searching for the "Default" entry in the loader configuration file.
833 ArcPathLength
= (wcslen(SystemRootArcPath
) + 1) * sizeof(WCHAR
);
834 // NtPathLength = ROUND_UP(SystemRootNtPath->Length + sizeof(UNICODE_NULL), sizeof(WCHAR));
835 NtPathLength
= SystemRootNtPath
->Length
+ sizeof(UNICODE_NULL
);
837 /* None was found, so add a new one */
838 NtOsInstall
= RtlAllocateHeap(ProcessHeap
, HEAP_ZERO_MEMORY
,
839 sizeof(*NtOsInstall
) +
840 ArcPathLength
+ NtPathLength
);
844 NtOsInstall
->DiskNumber
= DiskNumber
;
845 NtOsInstall
->PartitionNumber
= PartitionNumber
;
846 NtOsInstall
->PartEntry
= PartEntry
;
848 RtlInitEmptyUnicodeString(&NtOsInstall
->SystemArcPath
,
849 (PWCHAR
)(NtOsInstall
+ 1),
851 RtlCopyMemory(NtOsInstall
->SystemArcPath
.Buffer
, SystemRootArcPath
, ArcPathLength
);
852 NtOsInstall
->SystemArcPath
.Length
= ArcPathLength
- sizeof(UNICODE_NULL
);
854 RtlInitEmptyUnicodeString(&NtOsInstall
->SystemNtPath
,
855 (PWCHAR
)((ULONG_PTR
)(NtOsInstall
+ 1) + ArcPathLength
),
857 RtlCopyUnicodeString(&NtOsInstall
->SystemNtPath
, SystemRootNtPath
);
858 NtOsInstall
->PathComponent
= NtOsInstall
->SystemNtPath
.Buffer
+
859 (PathComponent
- SystemRootNtPath
->Buffer
);
861 StringCchCopyW(NtOsInstall
->InstallationName
, ARRAYSIZE(NtOsInstall
->InstallationName
), InstallationName
);
863 // Having the GENERIC_LIST storing the display item string plainly sucks...
864 StringCchPrintfA(InstallNameA
, ARRAYSIZE(InstallNameA
), "%S", InstallationName
);
865 AppendGenericListEntry(List
, InstallNameA
, NtOsInstall
, FALSE
);
871 FindNTOSInstallations(
872 IN OUT PGENERIC_LIST List
,
873 IN PPARTLIST PartList
,
874 IN PPARTENTRY PartEntry
)
877 ULONG DiskNumber
= PartEntry
->DiskEntry
->DiskNumber
;
878 ULONG PartitionNumber
= PartEntry
->PartitionNumber
;
879 HANDLE PartitionHandle
, FileHandle
;
880 OBJECT_ATTRIBUTES ObjectAttributes
;
881 IO_STATUS_BLOCK IoStatusBlock
;
882 UNICODE_STRING PartitionRootPath
;
884 HANDLE SectionHandle
;
888 WCHAR PathBuffer
[MAX_PATH
];
890 /* Set PartitionRootPath */
891 StringCchPrintfW(PathBuffer
, ARRAYSIZE(PathBuffer
),
892 L
"\\Device\\Harddisk%lu\\Partition%lu\\",
893 DiskNumber
, PartitionNumber
);
894 RtlInitUnicodeString(&PartitionRootPath
, PathBuffer
);
895 DPRINT1("FindNTOSInstallations: PartitionRootPath: '%wZ'\n", &PartitionRootPath
);
897 /* Open the partition */
898 InitializeObjectAttributes(&ObjectAttributes
,
900 OBJ_CASE_INSENSITIVE
,
903 Status
= NtOpenFile(&PartitionHandle
,
904 FILE_LIST_DIRECTORY
| SYNCHRONIZE
,
907 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
908 FILE_SYNCHRONOUS_IO_NONALERT
| FILE_DIRECTORY_FILE
);
909 if (!NT_SUCCESS(Status
))
911 DPRINT1("Failed to open partition '%wZ', Status 0x%08lx\n", &PartitionRootPath
, Status
);
915 /* Try to see whether we recognize some NT boot loaders */
916 for (i
= 0; i
< ARRAYSIZE(NtosBootLoaders
); ++i
)
918 /* Check whether the loader executable exists */
919 if (!DoesFileExist(PartitionHandle
, NtosBootLoaders
[i
].LoaderExecutable
))
921 /* The loader does not exist, continue with another one */
922 DPRINT1("Loader executable '%S' does not exist, continue with another one...\n", NtosBootLoaders
[i
].LoaderExecutable
);
926 /* Check whether the loader configuration file exists */
927 Status
= OpenAndMapFile(PartitionHandle
, NtosBootLoaders
[i
].LoaderConfigurationFile
,
928 &FileHandle
, &SectionHandle
, &ViewBase
, &FileSize
);
929 if (!NT_SUCCESS(Status
))
931 /* The loader does not exist, continue with another one */
932 // FIXME: Consider it might be optional??
933 DPRINT1("Loader configuration file '%S' does not exist, continue with another one...\n", NtosBootLoaders
[i
].LoaderConfigurationFile
);
937 /* The loader configuration file exists, interpret it to find valid installations */
938 DPRINT1("Analyse the OS installations inside '%S' in disk #%d, partition #%d\n",
939 NtosBootLoaders
[i
].LoaderConfigurationFile
, DiskNumber
, PartitionNumber
);
940 switch (NtosBootLoaders
[i
].Type
)
943 Status
= FreeLdrEnumerateInstallations(List
, PartList
, ViewBase
, FileSize
);
947 Status
= NtLdrEnumerateInstallations(List
, PartList
, ViewBase
, FileSize
);
951 DPRINT1("Loader type %d is currently unsupported!\n", NtosBootLoaders
[i
].Type
);
952 Status
= STATUS_SUCCESS
;
955 /* Finally, unmap and close the file */
956 UnMapFile(SectionHandle
, ViewBase
);
960 /* Close the partition */
961 NtClose(PartitionHandle
);
966 ShouldICheckThisPartition(
967 IN PPARTENTRY PartEntry
)
972 return PartEntry
->IsPartitioned
&&
973 !IsContainerPartition(PartEntry
->PartitionType
) /* alternatively: PartEntry->PartitionNumber != 0 */ &&
975 (PartEntry
->FormatState
== Preformatted
/* || PartEntry->FormatState == Formatted */);
978 // EnumerateNTOSInstallations
980 CreateNTOSInstallationsList(
981 IN PPARTLIST PartList
)
984 PLIST_ENTRY Entry
, Entry2
;
985 PDISKENTRY DiskEntry
;
986 PPARTENTRY PartEntry
;
988 List
= CreateGenericList();
992 /* Loop each available disk ... */
993 Entry
= PartList
->DiskListHead
.Flink
;
994 while (Entry
!= &PartList
->DiskListHead
)
996 DiskEntry
= CONTAINING_RECORD(Entry
, DISKENTRY
, ListEntry
);
997 Entry
= Entry
->Flink
;
999 DPRINT1("Disk #%d\n", DiskEntry
->DiskNumber
);
1001 /* ... and for each disk, loop each available partition */
1003 /* First, the primary partitions */
1004 Entry2
= DiskEntry
->PrimaryPartListHead
.Flink
;
1005 while (Entry2
!= &DiskEntry
->PrimaryPartListHead
)
1007 PartEntry
= CONTAINING_RECORD(Entry2
, PARTENTRY
, ListEntry
);
1008 Entry2
= Entry2
->Flink
;
1010 ASSERT(PartEntry
->DiskEntry
== DiskEntry
);
1012 DPRINT1(" Primary Partition #%d, index %d - Type 0x%02x, IsLogical = %s, IsPartitioned = %s, IsNew = %s, AutoCreate = %s, FormatState = %lu -- Should I check it? %s\n",
1013 PartEntry
->PartitionNumber
, PartEntry
->PartitionIndex
,
1014 PartEntry
->PartitionType
, PartEntry
->LogicalPartition
? "TRUE" : "FALSE",
1015 PartEntry
->IsPartitioned
? "TRUE" : "FALSE",
1016 PartEntry
->New
? "Yes" : "No",
1017 PartEntry
->AutoCreate
? "Yes" : "No",
1018 PartEntry
->FormatState
,
1019 ShouldICheckThisPartition(PartEntry
) ? "YES!" : "NO!");
1021 if (ShouldICheckThisPartition(PartEntry
))
1022 FindNTOSInstallations(List
, PartList
, PartEntry
);
1025 /* Then, the logical partitions (present in the extended partition) */
1026 Entry2
= DiskEntry
->LogicalPartListHead
.Flink
;
1027 while (Entry2
!= &DiskEntry
->LogicalPartListHead
)
1029 PartEntry
= CONTAINING_RECORD(Entry2
, PARTENTRY
, ListEntry
);
1030 Entry2
= Entry2
->Flink
;
1032 ASSERT(PartEntry
->DiskEntry
== DiskEntry
);
1034 DPRINT1(" Logical Partition #%d, index %d - Type 0x%02x, IsLogical = %s, IsPartitioned = %s, IsNew = %s, AutoCreate = %s, FormatState = %lu -- Should I check it? %s\n",
1035 PartEntry
->PartitionNumber
, PartEntry
->PartitionIndex
,
1036 PartEntry
->PartitionType
, PartEntry
->LogicalPartition
? "TRUE" : "FALSE",
1037 PartEntry
->IsPartitioned
? "TRUE" : "FALSE",
1038 PartEntry
->New
? "Yes" : "No",
1039 PartEntry
->AutoCreate
? "Yes" : "No",
1040 PartEntry
->FormatState
,
1041 ShouldICheckThisPartition(PartEntry
) ? "YES!" : "NO!");
1043 if (ShouldICheckThisPartition(PartEntry
))
1044 FindNTOSInstallations(List
, PartList
, PartEntry
);
1048 /**** Debugging: List all the collected installations ****/
1049 DumpNTOSInstalls(List
);