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(
109 IN HANDLE SystemRootDirectory OPTIONAL
,
110 IN PCWSTR SystemRoot OPTIONAL
);
112 static PNTOS_INSTALLATION
113 FindExistingNTOSInstall(
114 IN PGENERIC_LIST List
,
115 IN PCWSTR SystemRootArcPath OPTIONAL
,
116 IN PUNICODE_STRING SystemRootNtPath OPTIONAL
// or PCWSTR ?
119 static PNTOS_INSTALLATION
121 IN PGENERIC_LIST List
,
122 IN PCWSTR SystemRootArcPath
,
123 IN PUNICODE_STRING SystemRootNtPath
, // or PCWSTR ?
124 IN PCWSTR PathComponent
, // Pointer inside SystemRootNtPath buffer
126 IN ULONG PartitionNumber
,
127 IN PPARTENTRY PartEntry OPTIONAL
,
128 IN PCWSTR InstallationName
);
131 FreeLdrEnumerateInstallations(
132 IN OUT PGENERIC_LIST List
,
133 IN PPARTLIST PartList
,
134 // IN PPARTENTRY PartEntry,
140 PINICACHEITERATOR Iterator
;
141 PINICACHESECTION IniSection
, OsIniSection
;
142 PWCHAR SectionName
, KeyData
;
143 UNICODE_STRING InstallName
;
145 HANDLE SystemRootDirectory
;
146 OBJECT_ATTRIBUTES ObjectAttributes
;
147 IO_STATUS_BLOCK IoStatusBlock
;
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 /* Open SystemRootPath */
268 InitializeObjectAttributes(&ObjectAttributes
,
270 OBJ_CASE_INSENSITIVE
,
273 Status
= NtOpenFile(&SystemRootDirectory
,
274 FILE_LIST_DIRECTORY
| SYNCHRONIZE
,
277 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
278 FILE_SYNCHRONOUS_IO_NONALERT
| FILE_DIRECTORY_FILE
);
279 if (!NT_SUCCESS(Status
))
281 DPRINT1("Failed to open SystemRoot '%wZ', Status 0x%08lx\n", &SystemRootPath
, Status
);
285 if (IsValidNTOSInstallation(SystemRootDirectory
, NULL
))
287 ULONG DiskNumber
= 0, PartitionNumber
= 0;
288 PCWSTR PathComponent
= NULL
;
289 PDISKENTRY DiskEntry
= NULL
;
290 PPARTENTRY PartEntry
= NULL
;
292 DPRINT1("Found a valid NTOS installation in SystemRoot ARC path '%S', NT path '%wZ'\n", KeyData
, &SystemRootPath
);
294 /* From the NT path, compute the disk, partition and path components */
295 if (NtPathToDiskPartComponents(SystemRootPath
.Buffer
, &DiskNumber
, &PartitionNumber
, &PathComponent
))
297 DPRINT1("SystemRootPath = '%wZ' points to disk #%d, partition #%d, path '%S'\n",
298 &SystemRootPath
, DiskNumber
, PartitionNumber
, PathComponent
);
300 /* Retrieve the corresponding disk and partition */
301 if (!GetDiskOrPartition(PartList
, DiskNumber
, PartitionNumber
, &DiskEntry
, &PartEntry
))
302 DPRINT1("GetDiskOrPartition(disk #%d, partition #%d) failed\n", DiskNumber
, PartitionNumber
);
306 DPRINT1("NtPathToDiskPartComponents(%wZ) failed\n", &SystemRootPath
);
309 if (PartEntry
&& PartEntry
->DriveLetter
)
311 /* We have retrieved a partition that is mounted */
312 StringCchPrintfW(InstallNameW
, ARRAYSIZE(InstallNameW
), L
"%C:%s \"%wZ\"",
313 PartEntry
->DriveLetter
, PathComponent
, &InstallName
);
317 /* We failed somewhere, just show the NT path */
318 StringCchPrintfW(InstallNameW
, ARRAYSIZE(InstallNameW
), L
"%wZ \"%wZ\"",
319 &SystemRootPath
, &InstallName
);
321 AddNTOSInstallation(List
, KeyData
, &SystemRootPath
, PathComponent
,
322 DiskNumber
, PartitionNumber
, PartEntry
,
326 NtClose(SystemRootDirectory
);
328 while (IniCacheFindNextValue(Iterator
, &SectionName
, &KeyData
));
330 IniCacheFindClose(Iterator
);
333 IniCacheDestroy(IniCache
);
334 return STATUS_SUCCESS
;
338 NtLdrEnumerateInstallations(
339 IN OUT PGENERIC_LIST List
,
340 IN PPARTLIST PartList
,
341 // IN PPARTENTRY PartEntry,
347 PINICACHEITERATOR Iterator
;
348 PINICACHESECTION IniSection
;
349 PWCHAR SectionName
, KeyData
;
350 UNICODE_STRING InstallName
;
352 HANDLE SystemRootDirectory
;
353 OBJECT_ATTRIBUTES ObjectAttributes
;
354 IO_STATUS_BLOCK IoStatusBlock
;
355 PNTOS_INSTALLATION NtOsInstall
;
356 UNICODE_STRING SystemRootPath
;
357 WCHAR SystemRoot
[MAX_PATH
];
358 WCHAR InstallNameW
[MAX_PATH
];
360 /* Open an *existing* FreeLdr.ini configuration file */
361 Status
= IniCacheLoadFromMemory(&IniCache
, FileBuffer
, FileLength
, FALSE
);
362 if (!NT_SUCCESS(Status
))
365 /* Get the "Operating Systems" section */
366 IniSection
= IniCacheGetSection(IniCache
, L
"operating systems");
367 if (IniSection
== NULL
)
369 IniCacheDestroy(IniCache
);
370 return STATUS_UNSUCCESSFUL
;
373 /* Enumerate all the valid installations */
374 Iterator
= IniCacheFindFirstValue(IniSection
, &SectionName
, &KeyData
);
375 if (!Iterator
) goto Quit
;
378 // FIXME: Poor-man quotes removal (improvement over bootsup.c:UpdateFreeLoaderIni).
379 if (KeyData
[0] == L
'"')
381 /* Quoted name, copy up to the closing quote */
382 PWCHAR Begin
= &KeyData
[1];
383 PWCHAR End
= wcschr(Begin
, L
'"');
385 End
= Begin
+ wcslen(Begin
);
386 RtlInitEmptyUnicodeString(&InstallName
, Begin
, (ULONG_PTR
)End
- (ULONG_PTR
)Begin
);
387 InstallName
.Length
= InstallName
.MaximumLength
;
391 /* Non-quoted name, copy everything */
392 RtlInitUnicodeString(&InstallName
, KeyData
);
395 DPRINT1("Possible installation '%wZ' with ARC path '%S'\n", &InstallName
, SectionName
);
397 DPRINT1(" Found a Win2k3 install '%wZ' with ARC path '%S'\n", &InstallName
, SectionName
);
399 // TODO: Normalize the ARC path.
402 * Check whether we already have an installation with this ARC path.
403 * If this is the case, stop there.
405 NtOsInstall
= FindExistingNTOSInstall(List
, SectionName
, NULL
);
408 DPRINT1(" An NTOS installation with name \"%S\" already exists in SystemRoot '%wZ'\n",
409 NtOsInstall
->InstallationName
, &NtOsInstall
->SystemArcPath
);
414 * Convert the ARC path into an NT path, from which we will deduce
415 * the real disk drive & partition on which the candidate installation
416 * resides, as well verifying whether it is indeed an NTOS installation.
418 RtlInitEmptyUnicodeString(&SystemRootPath
, SystemRoot
, sizeof(SystemRoot
));
419 if (!ArcPathToNtPath(&SystemRootPath
, SectionName
, PartList
))
421 DPRINT1("ArcPathToNtPath(%S) failed, skip the installation.\n", SectionName
);
425 DPRINT1("ArcPathToNtPath() succeeded: '%S' --> '%wZ'\n", SectionName
, &SystemRootPath
);
428 * Check whether we already have an installation with this NT path.
429 * If this is the case, stop there.
431 NtOsInstall
= FindExistingNTOSInstall(List
, NULL
/*SectionName*/, &SystemRootPath
);
434 DPRINT1(" An NTOS installation with name \"%S\" already exists in SystemRoot '%wZ'\n",
435 NtOsInstall
->InstallationName
, &NtOsInstall
->SystemNtPath
);
439 /* Set SystemRootPath */
440 DPRINT1("NtLdrEnumerateInstallations: SystemRootPath: '%wZ'\n", &SystemRootPath
);
442 /* Open SystemRootPath */
443 InitializeObjectAttributes(&ObjectAttributes
,
445 OBJ_CASE_INSENSITIVE
,
448 Status
= NtOpenFile(&SystemRootDirectory
,
449 FILE_LIST_DIRECTORY
| SYNCHRONIZE
,
452 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
453 FILE_SYNCHRONOUS_IO_NONALERT
| FILE_DIRECTORY_FILE
);
454 if (!NT_SUCCESS(Status
))
456 DPRINT1("Failed to open SystemRoot '%wZ', Status 0x%08lx\n", &SystemRootPath
, Status
);
460 if (IsValidNTOSInstallation(SystemRootDirectory
, NULL
))
462 ULONG DiskNumber
= 0, PartitionNumber
= 0;
463 PCWSTR PathComponent
= NULL
;
464 PDISKENTRY DiskEntry
= NULL
;
465 PPARTENTRY PartEntry
= NULL
;
467 DPRINT1("Found a valid NTOS installation in SystemRoot ARC path '%S', NT path '%wZ'\n", SectionName
, &SystemRootPath
);
469 /* From the NT path, compute the disk, partition and path components */
470 if (NtPathToDiskPartComponents(SystemRootPath
.Buffer
, &DiskNumber
, &PartitionNumber
, &PathComponent
))
472 DPRINT1("SystemRootPath = '%wZ' points to disk #%d, partition #%d, path '%S'\n",
473 &SystemRootPath
, DiskNumber
, PartitionNumber
, PathComponent
);
475 /* Retrieve the corresponding disk and partition */
476 if (!GetDiskOrPartition(PartList
, DiskNumber
, PartitionNumber
, &DiskEntry
, &PartEntry
))
477 DPRINT1("GetDiskOrPartition(disk #%d, partition #%d) failed\n", DiskNumber
, PartitionNumber
);
481 DPRINT1("NtPathToDiskPartComponents(%wZ) failed\n", &SystemRootPath
);
484 if (PartEntry
&& PartEntry
->DriveLetter
)
486 /* We have retrieved a partition that is mounted */
487 StringCchPrintfW(InstallNameW
, ARRAYSIZE(InstallNameW
), L
"%C:%s \"%wZ\"",
488 PartEntry
->DriveLetter
, PathComponent
, &InstallName
);
492 /* We failed somewhere, just show the NT path */
493 StringCchPrintfW(InstallNameW
, ARRAYSIZE(InstallNameW
), L
"%wZ \"%wZ\"",
494 &SystemRootPath
, &InstallName
);
496 AddNTOSInstallation(List
, SectionName
, &SystemRootPath
, PathComponent
,
497 DiskNumber
, PartitionNumber
, PartEntry
,
501 NtClose(SystemRootDirectory
);
503 while (IniCacheFindNextValue(Iterator
, &SectionName
, &KeyData
));
505 IniCacheFindClose(Iterator
);
508 IniCacheDestroy(IniCache
);
509 return STATUS_SUCCESS
;
513 * FindSubStrI(PCWSTR str, PCWSTR strSearch) :
514 * Searches for a sub-string 'strSearch' inside 'str', similarly to what
515 * wcsstr(str, strSearch) does, but ignores the case during the comparisons.
517 PCWSTR
FindSubStrI(PCWSTR str
, PCWSTR strSearch
)
530 while (*s1
&& *s2
&& (towupper(*s1
) == towupper(*s2
)))
543 CheckForValidPEAndVendor(
544 IN HANDLE RootDirectory OPTIONAL
,
545 IN PCWSTR PathName OPTIONAL
,
546 IN PCWSTR FileName
, // OPTIONAL
547 OUT PUNICODE_STRING VendorName
550 BOOLEAN Success
= FALSE
;
552 HANDLE FileHandle
, SectionHandle
;
555 PVOID VersionBuffer
= NULL
; // Read-only
559 if (VendorName
->MaximumLength
< sizeof(UNICODE_NULL
))
562 *VendorName
->Buffer
= UNICODE_NULL
;
563 VendorName
->Length
= 0;
565 Status
= OpenAndMapFile(RootDirectory
, PathName
, FileName
,
566 &FileHandle
, &SectionHandle
, &ViewBase
, NULL
);
567 if (!NT_SUCCESS(Status
))
569 DPRINT1("Failed to open and map file '%S', Status 0x%08lx\n", FileName
, Status
);
570 return FALSE
; // Status;
573 /* Make sure it's a valid PE file */
574 if (!RtlImageNtHeader(ViewBase
))
576 DPRINT1("File '%S' does not seem to be a valid PE, bail out\n", FileName
);
577 Status
= STATUS_INVALID_IMAGE_FORMAT
;
582 * Search for a valid executable version and vendor.
583 * NOTE: The module is loaded as a data file, it should be marked as such.
585 Status
= NtGetVersionResource((PVOID
)((ULONG_PTR
)ViewBase
| 1), &VersionBuffer
, NULL
);
586 if (!NT_SUCCESS(Status
))
588 DPRINT1("Failed to get version resource for file '%S', Status 0x%08lx\n", FileName
, Status
);
592 Status
= NtVerQueryValue(VersionBuffer
, L
"\\VarFileInfo\\Translation", &pvData
, &BufLen
);
593 if (NT_SUCCESS(Status
))
595 USHORT wCodePage
= 0, wLangID
= 0;
596 WCHAR FileInfo
[MAX_PATH
];
598 wCodePage
= LOWORD(*(ULONG
*)pvData
);
599 wLangID
= HIWORD(*(ULONG
*)pvData
);
601 StringCchPrintfW(FileInfo
, ARRAYSIZE(FileInfo
),
602 L
"StringFileInfo\\%04X%04X\\CompanyName",
605 Status
= NtVerQueryValue(VersionBuffer
, FileInfo
, &pvData
, &BufLen
);
607 /* Fixup the Status in case pvData is NULL */
608 if (NT_SUCCESS(Status
) && !pvData
)
609 Status
= STATUS_NOT_FOUND
;
611 if (NT_SUCCESS(Status
) /*&& pvData*/)
613 /* BufLen includes the NULL terminator count */
614 DPRINT1("Found version vendor: \"%S\" for file '%S'\n", pvData
, FileName
);
616 StringCbCopyNW(VendorName
->Buffer
, VendorName
->MaximumLength
,
617 pvData
, BufLen
* sizeof(WCHAR
));
618 VendorName
->Length
= wcslen(VendorName
->Buffer
) * sizeof(WCHAR
);
624 if (!NT_SUCCESS(Status
))
625 DPRINT1("No version vendor found for file '%S'\n", FileName
);
628 /* Finally, unmap and close the file */
629 UnMapFile(SectionHandle
, ViewBase
);
636 // TODO: Instead of returning TRUE/FALSE, it would be nice to return
637 // a flag indicating:
638 // - whether the installation is actually valid;
639 // - if it's broken or not (aka. needs for repair, or just upgrading).
642 IsValidNTOSInstallation(
643 IN HANDLE SystemRootDirectory OPTIONAL
,
644 IN PCWSTR SystemRoot OPTIONAL
)
646 BOOLEAN Success
= FALSE
;
648 UNICODE_STRING VendorName
;
649 WCHAR PathBuffer
[MAX_PATH
];
652 * Use either the 'SystemRootDirectory' handle or the 'SystemRoot' string,
653 * depending on what the user gave to us in entry.
655 if (SystemRootDirectory
)
657 // else SystemRootDirectory == NULL and SystemRoot is what it is.
659 /* If both the parameters are NULL we cannot do anything else more */
660 if (!SystemRootDirectory
&& !SystemRoot
)
663 // DoesPathExist(SystemRootDirectory, SystemRoot, L"System32\\"); etc...
665 /* Check for the existence of \SystemRoot\System32 */
666 StringCchPrintfW(PathBuffer
, ARRAYSIZE(PathBuffer
), L
"%s%s", SystemRoot
? SystemRoot
: L
"", L
"System32\\");
667 if (!DoesPathExist(SystemRootDirectory
, PathBuffer
))
669 // DPRINT1("Failed to open directory '%wZ', Status 0x%08lx\n", &FileName, Status);
673 /* Check for the existence of \SystemRoot\System32\drivers */
674 StringCchPrintfW(PathBuffer
, ARRAYSIZE(PathBuffer
), L
"%s%s", SystemRoot
? SystemRoot
: L
"", L
"System32\\drivers\\");
675 if (!DoesPathExist(SystemRootDirectory
, PathBuffer
))
677 // DPRINT1("Failed to open directory '%wZ', Status 0x%08lx\n", &FileName, Status);
681 /* Check for the existence of \SystemRoot\System32\config */
682 StringCchPrintfW(PathBuffer
, ARRAYSIZE(PathBuffer
), L
"%s%s", SystemRoot
? SystemRoot
: L
"", L
"System32\\config\\");
683 if (!DoesPathExist(SystemRootDirectory
, PathBuffer
))
685 // DPRINT1("Failed to open directory '%wZ', Status 0x%08lx\n", &FileName, Status);
691 * Check for the existence of SYSTEM and SOFTWARE hives in \SystemRoot\System32\config
692 * (but we don't check here whether they are actually valid).
694 if (!DoesFileExist(SystemRootDirectory
, SystemRoot
, L
"System32\\config\\SYSTEM"))
696 // DPRINT1("Failed to open file '%wZ', Status 0x%08lx\n", &FileName, Status);
699 if (!DoesFileExist(SystemRootDirectory
, SystemRoot
, L
"System32\\config\\SOFTWARE"))
701 // DPRINT1("Failed to open file '%wZ', Status 0x%08lx\n", &FileName, Status);
706 RtlInitEmptyUnicodeString(&VendorName
, PathBuffer
, sizeof(PathBuffer
));
708 /* Check for the existence of \SystemRoot\System32\ntoskrnl.exe and retrieves its vendor name */
709 Success
= CheckForValidPEAndVendor(SystemRootDirectory
, SystemRoot
, L
"System32\\ntoskrnl.exe", &VendorName
);
711 DPRINT1("Kernel file ntoskrnl.exe is either not a PE file, or does not have any vendor?\n");
713 /* The kernel gives the OS its flavour */
716 for (i
= 0; i
< ARRAYSIZE(KnownVendors
); ++i
)
718 Success
= !!FindSubStrI(VendorName
.Buffer
, KnownVendors
[i
]);
721 /* We have found a correct vendor combination */
722 DPRINT1("IsValidNTOSInstallation: We've got an NTOS installation from %S !\n", KnownVendors
[i
]);
728 /* OPTIONAL: Check for the existence of \SystemRoot\System32\ntkrnlpa.exe */
730 /* Check for the existence of \SystemRoot\System32\ntdll.dll and retrieves its vendor name */
731 Success
= CheckForValidPEAndVendor(SystemRootDirectory
, SystemRoot
, L
"System32\\ntdll.dll", &VendorName
);
733 DPRINT1("User-mode file ntdll.dll is either not a PE file, or does not have any vendor?\n");
736 for (i
= 0; i
< ARRAYSIZE(KnownVendors
); ++i
)
738 if (!!FindSubStrI(VendorName
.Buffer
, KnownVendors
[i
]))
740 /* We have found a correct vendor combination */
741 DPRINT1("IsValidNTOSInstallation: The user-mode file ntdll.dll is from %S\n", KnownVendors
[i
]);
752 IN PGENERIC_LIST List
)
754 PGENERIC_LIST_ENTRY Entry
;
755 PNTOS_INSTALLATION NtOsInstall
;
756 ULONG NtOsInstallsCount
= GetNumberOfListEntries(List
);
758 DPRINT1("There %s %d installation%s detected:\n",
759 NtOsInstallsCount
>= 2 ? "are" : "is",
761 NtOsInstallsCount
>= 2 ? "s" : "");
763 Entry
= GetFirstListEntry(List
);
766 NtOsInstall
= (PNTOS_INSTALLATION
)GetListEntryUserData(Entry
);
767 Entry
= GetNextListEntry(Entry
);
769 DPRINT1(" On disk #%d, partition #%d: Installation \"%S\" in SystemRoot '%wZ'\n",
770 NtOsInstall
->DiskNumber
, NtOsInstall
->PartitionNumber
,
771 NtOsInstall
->InstallationName
, &NtOsInstall
->SystemNtPath
);
777 static PNTOS_INSTALLATION
778 FindExistingNTOSInstall(
779 IN PGENERIC_LIST List
,
780 IN PCWSTR SystemRootArcPath OPTIONAL
,
781 IN PUNICODE_STRING SystemRootNtPath OPTIONAL
// or PCWSTR ?
784 PGENERIC_LIST_ENTRY Entry
;
785 PNTOS_INSTALLATION NtOsInstall
;
786 UNICODE_STRING SystemArcPath
;
789 * We search either via ARC path or NT path.
790 * If both pointers are NULL then we fail straight away.
792 if (!SystemRootArcPath
&& !SystemRootNtPath
)
795 RtlInitUnicodeString(&SystemArcPath
, SystemRootArcPath
);
797 Entry
= GetFirstListEntry(List
);
800 NtOsInstall
= (PNTOS_INSTALLATION
)GetListEntryUserData(Entry
);
801 Entry
= GetNextListEntry(Entry
);
804 * Note that if both ARC paths are equal, then the corresponding
805 * NT paths must be the same. However, two ARC paths may be different
806 * but resolve into the same NT path.
808 if ( (SystemRootArcPath
&&
809 RtlEqualUnicodeString(&NtOsInstall
->SystemArcPath
,
810 &SystemArcPath
, TRUE
)) ||
812 RtlEqualUnicodeString(&NtOsInstall
->SystemNtPath
,
813 SystemRootNtPath
, TRUE
)) )
823 static PNTOS_INSTALLATION
825 IN PGENERIC_LIST List
,
826 IN PCWSTR SystemRootArcPath
,
827 IN PUNICODE_STRING SystemRootNtPath
, // or PCWSTR ?
828 IN PCWSTR PathComponent
, // Pointer inside SystemRootNtPath buffer
830 IN ULONG PartitionNumber
,
831 IN PPARTENTRY PartEntry OPTIONAL
,
832 IN PCWSTR InstallationName
)
834 PNTOS_INSTALLATION NtOsInstall
;
835 SIZE_T ArcPathLength
, NtPathLength
;
836 CHAR InstallNameA
[MAX_PATH
];
838 /* Is there already any installation with these settings? */
839 NtOsInstall
= FindExistingNTOSInstall(List
, SystemRootArcPath
, SystemRootNtPath
);
842 DPRINT1("An NTOS installation with name \"%S\" already exists on disk #%d, partition #%d, in SystemRoot '%wZ'\n",
843 NtOsInstall
->InstallationName
, NtOsInstall
->DiskNumber
, NtOsInstall
->PartitionNumber
, &NtOsInstall
->SystemNtPath
);
845 // NOTE: We may use its "IsDefault" attribute, and only keep the entries that have IsDefault == TRUE...
846 // Setting IsDefault to TRUE would imply searching for the "Default" entry in the loader configuration file.
851 ArcPathLength
= (wcslen(SystemRootArcPath
) + 1) * sizeof(WCHAR
);
852 // NtPathLength = ROUND_UP(SystemRootNtPath->Length + sizeof(UNICODE_NULL), sizeof(WCHAR));
853 NtPathLength
= SystemRootNtPath
->Length
+ sizeof(UNICODE_NULL
);
855 /* None was found, so add a new one */
856 NtOsInstall
= RtlAllocateHeap(ProcessHeap
, HEAP_ZERO_MEMORY
,
857 sizeof(*NtOsInstall
) +
858 ArcPathLength
+ NtPathLength
);
862 NtOsInstall
->DiskNumber
= DiskNumber
;
863 NtOsInstall
->PartitionNumber
= PartitionNumber
;
864 NtOsInstall
->PartEntry
= PartEntry
;
866 RtlInitEmptyUnicodeString(&NtOsInstall
->SystemArcPath
,
867 (PWCHAR
)(NtOsInstall
+ 1),
869 RtlCopyMemory(NtOsInstall
->SystemArcPath
.Buffer
, SystemRootArcPath
, ArcPathLength
);
870 NtOsInstall
->SystemArcPath
.Length
= ArcPathLength
- sizeof(UNICODE_NULL
);
872 RtlInitEmptyUnicodeString(&NtOsInstall
->SystemNtPath
,
873 (PWCHAR
)((ULONG_PTR
)(NtOsInstall
+ 1) + ArcPathLength
),
875 RtlCopyUnicodeString(&NtOsInstall
->SystemNtPath
, SystemRootNtPath
);
876 NtOsInstall
->PathComponent
= NtOsInstall
->SystemNtPath
.Buffer
+
877 (PathComponent
- SystemRootNtPath
->Buffer
);
879 StringCchCopyW(NtOsInstall
->InstallationName
, ARRAYSIZE(NtOsInstall
->InstallationName
), InstallationName
);
881 // Having the GENERIC_LIST storing the display item string plainly sucks...
882 StringCchPrintfA(InstallNameA
, ARRAYSIZE(InstallNameA
), "%S", InstallationName
);
883 AppendGenericListEntry(List
, InstallNameA
, NtOsInstall
, FALSE
);
889 FindNTOSInstallations(
890 IN OUT PGENERIC_LIST List
,
891 IN PPARTLIST PartList
,
892 IN PPARTENTRY PartEntry
)
895 ULONG DiskNumber
= PartEntry
->DiskEntry
->DiskNumber
;
896 ULONG PartitionNumber
= PartEntry
->PartitionNumber
;
897 HANDLE PartitionHandle
, FileHandle
;
898 OBJECT_ATTRIBUTES ObjectAttributes
;
899 IO_STATUS_BLOCK IoStatusBlock
;
900 UNICODE_STRING PartitionRootPath
;
902 HANDLE SectionHandle
;
906 WCHAR PathBuffer
[MAX_PATH
];
908 /* Set PartitionRootPath */
909 StringCchPrintfW(PathBuffer
, ARRAYSIZE(PathBuffer
),
910 L
"\\Device\\Harddisk%lu\\Partition%lu\\",
911 DiskNumber
, PartitionNumber
);
912 RtlInitUnicodeString(&PartitionRootPath
, PathBuffer
);
913 DPRINT1("FindNTOSInstallations: PartitionRootPath: '%wZ'\n", &PartitionRootPath
);
915 /* Open the partition */
916 InitializeObjectAttributes(&ObjectAttributes
,
918 OBJ_CASE_INSENSITIVE
,
921 Status
= NtOpenFile(&PartitionHandle
,
922 FILE_LIST_DIRECTORY
| SYNCHRONIZE
,
925 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
926 FILE_SYNCHRONOUS_IO_NONALERT
| FILE_DIRECTORY_FILE
);
927 if (!NT_SUCCESS(Status
))
929 DPRINT1("Failed to open partition '%wZ', Status 0x%08lx\n", &PartitionRootPath
, Status
);
933 /* Try to see whether we recognize some NT boot loaders */
934 for (i
= 0; i
< ARRAYSIZE(NtosBootLoaders
); ++i
)
936 /* Check whether the loader executable exists */
937 if (!DoesFileExist(PartitionHandle
, NULL
, NtosBootLoaders
[i
].LoaderExecutable
))
939 /* The loader does not exist, continue with another one */
940 DPRINT1("Loader executable '%S' does not exist, continue with another one...\n", NtosBootLoaders
[i
].LoaderExecutable
);
944 /* Check whether the loader configuration file exists */
945 Status
= OpenAndMapFile(PartitionHandle
, NULL
, NtosBootLoaders
[i
].LoaderConfigurationFile
,
946 &FileHandle
, &SectionHandle
, &ViewBase
, &FileSize
);
947 if (!NT_SUCCESS(Status
))
949 /* The loader does not exist, continue with another one */
950 // FIXME: Consider it might be optional??
951 DPRINT1("Loader configuration file '%S' does not exist, continue with another one...\n", NtosBootLoaders
[i
].LoaderConfigurationFile
);
955 /* The loader configuration file exists, interpret it to find valid installations */
956 DPRINT1("Analyse the OS installations inside '%S' in disk #%d, partition #%d\n",
957 NtosBootLoaders
[i
].LoaderConfigurationFile
, DiskNumber
, PartitionNumber
);
958 switch (NtosBootLoaders
[i
].Type
)
961 Status
= FreeLdrEnumerateInstallations(List
, PartList
, ViewBase
, FileSize
);
965 Status
= NtLdrEnumerateInstallations(List
, PartList
, ViewBase
, FileSize
);
969 DPRINT1("Loader type %d is currently unsupported!\n", NtosBootLoaders
[i
].Type
);
970 Status
= STATUS_SUCCESS
;
973 /* Finally, unmap and close the file */
974 UnMapFile(SectionHandle
, ViewBase
);
978 /* Close the partition */
979 NtClose(PartitionHandle
);
984 ShouldICheckThisPartition(
985 IN PPARTENTRY PartEntry
)
990 return PartEntry
->IsPartitioned
&&
991 !IsContainerPartition(PartEntry
->PartitionType
) /* alternatively: PartEntry->PartitionNumber != 0 */ &&
993 (PartEntry
->FormatState
== Preformatted
/* || PartEntry->FormatState == Formatted */);
996 // EnumerateNTOSInstallations
998 CreateNTOSInstallationsList(
999 IN PPARTLIST PartList
)
1002 PLIST_ENTRY Entry
, Entry2
;
1003 PDISKENTRY DiskEntry
;
1004 PPARTENTRY PartEntry
;
1006 List
= CreateGenericList();
1010 /* Loop each available disk ... */
1011 Entry
= PartList
->DiskListHead
.Flink
;
1012 while (Entry
!= &PartList
->DiskListHead
)
1014 DiskEntry
= CONTAINING_RECORD(Entry
, DISKENTRY
, ListEntry
);
1015 Entry
= Entry
->Flink
;
1017 DPRINT1("Disk #%d\n", DiskEntry
->DiskNumber
);
1019 /* ... and for each disk, loop each available partition */
1021 /* First, the primary partitions */
1022 Entry2
= DiskEntry
->PrimaryPartListHead
.Flink
;
1023 while (Entry2
!= &DiskEntry
->PrimaryPartListHead
)
1025 PartEntry
= CONTAINING_RECORD(Entry2
, PARTENTRY
, ListEntry
);
1026 Entry2
= Entry2
->Flink
;
1028 ASSERT(PartEntry
->DiskEntry
== DiskEntry
);
1030 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",
1031 PartEntry
->PartitionNumber
, PartEntry
->PartitionIndex
,
1032 PartEntry
->PartitionType
, PartEntry
->LogicalPartition
? "TRUE" : "FALSE",
1033 PartEntry
->IsPartitioned
? "TRUE" : "FALSE",
1034 PartEntry
->New
? "Yes" : "No",
1035 PartEntry
->AutoCreate
? "Yes" : "No",
1036 PartEntry
->FormatState
,
1037 ShouldICheckThisPartition(PartEntry
) ? "YES!" : "NO!");
1039 if (ShouldICheckThisPartition(PartEntry
))
1040 FindNTOSInstallations(List
, PartList
, PartEntry
);
1043 /* Then, the logical partitions (present in the extended partition) */
1044 Entry2
= DiskEntry
->LogicalPartListHead
.Flink
;
1045 while (Entry2
!= &DiskEntry
->LogicalPartListHead
)
1047 PartEntry
= CONTAINING_RECORD(Entry2
, PARTENTRY
, ListEntry
);
1048 Entry2
= Entry2
->Flink
;
1050 ASSERT(PartEntry
->DiskEntry
== DiskEntry
);
1052 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",
1053 PartEntry
->PartitionNumber
, PartEntry
->PartitionIndex
,
1054 PartEntry
->PartitionType
, PartEntry
->LogicalPartition
? "TRUE" : "FALSE",
1055 PartEntry
->IsPartitioned
? "TRUE" : "FALSE",
1056 PartEntry
->New
? "Yes" : "No",
1057 PartEntry
->AutoCreate
? "Yes" : "No",
1058 PartEntry
->FormatState
,
1059 ShouldICheckThisPartition(PartEntry
) ? "YES!" : "NO!");
1061 if (ShouldICheckThisPartition(PartEntry
))
1062 FindNTOSInstallations(List
, PartList
, PartEntry
);
1066 /**** Debugging: List all the collected installations ****/
1067 DumpNTOSInstalls(List
);