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
);
83 IsValidNTOSInstallation_UStr(
84 IN PUNICODE_STRING SystemRootPath
);
87 IsValidNTOSInstallation(
88 IN PCWSTR SystemRoot
);
90 static PNTOS_INSTALLATION
91 FindExistingNTOSInstall(
92 IN PGENERIC_LIST List
,
93 IN PCWSTR SystemRootArcPath OPTIONAL
,
94 IN PUNICODE_STRING SystemRootNtPath OPTIONAL
// or PCWSTR ?
97 static PNTOS_INSTALLATION
99 IN PGENERIC_LIST List
,
100 IN PCWSTR SystemRootArcPath
,
101 IN PUNICODE_STRING SystemRootNtPath
, // or PCWSTR ?
102 IN PCWSTR PathComponent
, // Pointer inside SystemRootNtPath buffer
104 IN ULONG PartitionNumber
,
105 IN PPARTENTRY PartEntry OPTIONAL
,
106 IN PCWSTR InstallationName
);
108 typedef struct _ENUM_INSTALLS_DATA
110 IN OUT PGENERIC_LIST List
;
111 IN PPARTLIST PartList
;
112 // IN PPARTENTRY PartEntry;
113 } ENUM_INSTALLS_DATA
, *PENUM_INSTALLS_DATA
;
115 // PENUM_BOOT_ENTRIES_ROUTINE
118 EnumerateInstallations(
119 IN NTOS_BOOT_LOADER_TYPE Type
,
120 IN PNTOS_BOOT_ENTRY BootEntry
,
121 IN PVOID Parameter OPTIONAL
)
123 PENUM_INSTALLS_DATA Data
= (PENUM_INSTALLS_DATA
)Parameter
;
125 PNTOS_INSTALLATION NtOsInstall
;
126 UNICODE_STRING SystemRootPath
;
127 WCHAR SystemRoot
[MAX_PATH
];
128 WCHAR InstallNameW
[MAX_PATH
];
130 ULONG DiskNumber
= 0, PartitionNumber
= 0;
131 PCWSTR PathComponent
= NULL
;
132 PDISKENTRY DiskEntry
= NULL
;
133 PPARTENTRY PartEntry
= NULL
;
135 /* We have a boot entry */
137 UNICODE_STRING InstallName
;
138 // /**/RtlInitUnicodeString(&InstallName, BootEntry->FriendlyName);/**/
139 InstallName
= *BootEntry
->FriendlyName
;
144 /* Check for supported boot type "Windows2003" */
146 // TODO: What to do with "Windows" ; "WindowsNT40" ; "ReactOSSetup" ?
147 if ((BootType
== NULL
) ||
148 ( (_wcsicmp(BootType
, L
"Windows2003") != 0) &&
149 (_wcsicmp(BootType
, L
"\"Windows2003\"") != 0) ))
151 /* This is not a ReactOS entry */
152 /* Certainly not a ReactOS installation */
153 return STATUS_SUCCESS
;
158 DPRINT1(" Found a candidate Win2k3 install '%wZ' with ARC path '%S'\n",
159 &InstallName
, BootEntry
->OsLoadPath
);
160 // DPRINT1(" Found a Win2k3 install '%wZ' with ARC path '%S'\n",
161 // &InstallName, BootEntry->OsLoadPath);
163 // TODO: Normalize the ARC path.
166 * Check whether we already have an installation with this ARC path.
167 * If this is the case, stop there.
169 NtOsInstall
= FindExistingNTOSInstall(Data
->List
, BootEntry
->OsLoadPath
, NULL
);
172 DPRINT1(" An NTOS installation with name \"%S\" already exists in SystemRoot '%wZ'\n",
173 NtOsInstall
->InstallationName
, &NtOsInstall
->SystemArcPath
);
174 return STATUS_SUCCESS
;
178 * Convert the ARC path into an NT path, from which we will deduce
179 * the real disk drive & partition on which the candidate installation
180 * resides, as well verifying whether it is indeed an NTOS installation.
182 RtlInitEmptyUnicodeString(&SystemRootPath
, SystemRoot
, sizeof(SystemRoot
));
183 if (!ArcPathToNtPath(&SystemRootPath
, BootEntry
->OsLoadPath
, Data
->PartList
))
185 DPRINT1("ArcPathToNtPath(%S) failed, skip the installation.\n", BootEntry
->OsLoadPath
);
186 return STATUS_SUCCESS
;
189 DPRINT1("ArcPathToNtPath() succeeded: '%S' --> '%wZ'\n",
190 BootEntry
->OsLoadPath
, &SystemRootPath
);
193 * Check whether we already have an installation with this NT path.
194 * If this is the case, stop there.
196 NtOsInstall
= FindExistingNTOSInstall(Data
->List
, NULL
/*BootEntry->OsLoadPath*/, &SystemRootPath
);
199 DPRINT1(" An NTOS installation with name \"%S\" already exists in SystemRoot '%wZ'\n",
200 NtOsInstall
->InstallationName
, &NtOsInstall
->SystemNtPath
);
201 return STATUS_SUCCESS
;
204 DPRINT1("EnumerateInstallations: SystemRootPath: '%wZ'\n", &SystemRootPath
);
206 /* Check if this is a valid NTOS installation; stop there if it isn't one */
207 if (!IsValidNTOSInstallation_UStr(&SystemRootPath
))
208 return STATUS_SUCCESS
;
210 DPRINT1("Found a valid NTOS installation in SystemRoot ARC path '%S', NT path '%wZ'\n",
211 BootEntry
->OsLoadPath
, &SystemRootPath
);
213 /* From the NT path, compute the disk, partition and path components */
214 if (NtPathToDiskPartComponents(SystemRootPath
.Buffer
, &DiskNumber
, &PartitionNumber
, &PathComponent
))
216 DPRINT1("SystemRootPath = '%wZ' points to disk #%d, partition #%d, path '%S'\n",
217 &SystemRootPath
, DiskNumber
, PartitionNumber
, PathComponent
);
219 /* Retrieve the corresponding disk and partition */
220 if (!GetDiskOrPartition(Data
->PartList
, DiskNumber
, PartitionNumber
, &DiskEntry
, &PartEntry
))
222 DPRINT1("GetDiskOrPartition(disk #%d, partition #%d) failed\n",
223 DiskNumber
, PartitionNumber
);
228 DPRINT1("NtPathToDiskPartComponents(%wZ) failed\n", &SystemRootPath
);
231 if (PartEntry
&& PartEntry
->DriveLetter
)
233 /* We have retrieved a partition that is mounted */
234 StringCchPrintfW(InstallNameW
, ARRAYSIZE(InstallNameW
), L
"%C:%s \"%wZ\"",
235 PartEntry
->DriveLetter
, PathComponent
, &InstallName
);
239 /* We failed somewhere, just show the NT path */
240 StringCchPrintfW(InstallNameW
, ARRAYSIZE(InstallNameW
), L
"%wZ \"%wZ\"",
241 &SystemRootPath
, &InstallName
);
243 AddNTOSInstallation(Data
->List
, BootEntry
->OsLoadPath
,
244 &SystemRootPath
, PathComponent
,
245 DiskNumber
, PartitionNumber
, PartEntry
,
248 return STATUS_SUCCESS
;
252 * FindSubStrI(PCWSTR str, PCWSTR strSearch) :
253 * Searches for a sub-string 'strSearch' inside 'str', similarly to what
254 * wcsstr(str, strSearch) does, but ignores the case during the comparisons.
256 PCWSTR
FindSubStrI(PCWSTR str
, PCWSTR strSearch
)
269 while (*s1
&& *s2
&& (towupper(*s1
) == towupper(*s2
)))
282 CheckForValidPEAndVendor(
283 IN HANDLE RootDirectory OPTIONAL
,
284 IN PCWSTR PathNameToFile
,
285 OUT PUNICODE_STRING VendorName
288 BOOLEAN Success
= FALSE
;
290 HANDLE FileHandle
, SectionHandle
;
293 PVOID VersionBuffer
= NULL
; // Read-only
297 if (VendorName
->MaximumLength
< sizeof(UNICODE_NULL
))
300 *VendorName
->Buffer
= UNICODE_NULL
;
301 VendorName
->Length
= 0;
303 Status
= OpenAndMapFile(RootDirectory
, PathNameToFile
,
304 &FileHandle
, &SectionHandle
, &ViewBase
, NULL
);
305 if (!NT_SUCCESS(Status
))
307 DPRINT1("Failed to open and map file '%S', Status 0x%08lx\n", PathNameToFile
, Status
);
308 return FALSE
; // Status;
311 /* Make sure it's a valid PE file */
312 if (!RtlImageNtHeader(ViewBase
))
314 DPRINT1("File '%S' does not seem to be a valid PE, bail out\n", PathNameToFile
);
315 Status
= STATUS_INVALID_IMAGE_FORMAT
;
320 * Search for a valid executable version and vendor.
321 * NOTE: The module is loaded as a data file, it should be marked as such.
323 Status
= NtGetVersionResource((PVOID
)((ULONG_PTR
)ViewBase
| 1), &VersionBuffer
, NULL
);
324 if (!NT_SUCCESS(Status
))
326 DPRINT1("Failed to get version resource for file '%S', Status 0x%08lx\n", PathNameToFile
, Status
);
330 Status
= NtVerQueryValue(VersionBuffer
, L
"\\VarFileInfo\\Translation", &pvData
, &BufLen
);
331 if (NT_SUCCESS(Status
))
333 USHORT wCodePage
= 0, wLangID
= 0;
334 WCHAR FileInfo
[MAX_PATH
];
336 wCodePage
= LOWORD(*(ULONG
*)pvData
);
337 wLangID
= HIWORD(*(ULONG
*)pvData
);
339 StringCchPrintfW(FileInfo
, ARRAYSIZE(FileInfo
),
340 L
"StringFileInfo\\%04X%04X\\CompanyName",
343 Status
= NtVerQueryValue(VersionBuffer
, FileInfo
, &pvData
, &BufLen
);
345 /* Fixup the Status in case pvData is NULL */
346 if (NT_SUCCESS(Status
) && !pvData
)
347 Status
= STATUS_NOT_FOUND
;
349 if (NT_SUCCESS(Status
) /*&& pvData*/)
351 /* BufLen includes the NULL terminator count */
352 DPRINT1("Found version vendor: \"%S\" for file '%S'\n", pvData
, PathNameToFile
);
354 StringCbCopyNW(VendorName
->Buffer
, VendorName
->MaximumLength
,
355 pvData
, BufLen
* sizeof(WCHAR
));
356 VendorName
->Length
= wcslen(VendorName
->Buffer
) * sizeof(WCHAR
);
362 if (!NT_SUCCESS(Status
))
363 DPRINT1("No version vendor found for file '%S'\n", PathNameToFile
);
366 /* Finally, unmap and close the file */
367 UnMapFile(SectionHandle
, ViewBase
);
374 // TODO: Instead of returning TRUE/FALSE, it would be nice to return
375 // a flag indicating:
376 // - whether the installation is actually valid;
377 // - if it's broken or not (aka. needs for repair, or just upgrading).
380 IsValidNTOSInstallationByHandle(
381 IN HANDLE SystemRootDirectory
)
383 BOOLEAN Success
= FALSE
;
386 UNICODE_STRING VendorName
;
387 WCHAR VendorNameBuffer
[MAX_PATH
];
389 /* Check for the existence of \SystemRoot\System32 */
390 PathName
= L
"System32\\";
391 if (!DoesPathExist(SystemRootDirectory
, PathName
))
393 // DPRINT1("Failed to open directory '%S', Status 0x%08lx\n", PathName, Status);
397 /* Check for the existence of \SystemRoot\System32\drivers */
398 PathName
= L
"System32\\drivers\\";
399 if (!DoesPathExist(SystemRootDirectory
, PathName
))
401 // DPRINT1("Failed to open directory '%S', Status 0x%08lx\n", PathName, Status);
405 /* Check for the existence of \SystemRoot\System32\config */
406 PathName
= L
"System32\\config\\";
407 if (!DoesPathExist(SystemRootDirectory
, PathName
))
409 // DPRINT1("Failed to open directory '%S', Status 0x%08lx\n", PathName, Status);
415 * Check for the existence of SYSTEM and SOFTWARE hives in \SystemRoot\System32\config
416 * (but we don't check here whether they are actually valid).
418 PathName
= L
"System32\\config\\SYSTEM";
419 if (!DoesFileExist(SystemRootDirectory
, PathName
))
421 // DPRINT1("Failed to open file '%S', Status 0x%08lx\n", PathName, Status);
424 PathName
= L
"System32\\config\\SOFTWARE";
425 if (!DoesFileExist(SystemRootDirectory
, PathName
))
427 // DPRINT1("Failed to open file '%S', Status 0x%08lx\n", PathName, Status);
432 RtlInitEmptyUnicodeString(&VendorName
, VendorNameBuffer
, sizeof(VendorNameBuffer
));
434 /* Check for the existence of \SystemRoot\System32\ntoskrnl.exe and retrieves its vendor name */
435 PathName
= L
"System32\\ntoskrnl.exe";
436 Success
= CheckForValidPEAndVendor(SystemRootDirectory
, PathName
, &VendorName
);
438 DPRINT1("Kernel executable '%S' is either not a PE file, or does not have any vendor?\n", PathName
);
440 /* The kernel gives the OS its flavour */
443 for (i
= 0; i
< ARRAYSIZE(KnownVendors
); ++i
)
445 Success
= !!FindSubStrI(VendorName
.Buffer
, KnownVendors
[i
]);
448 /* We have found a correct vendor combination */
449 DPRINT1("IsValidNTOSInstallation: We've got an NTOS installation from %S !\n", KnownVendors
[i
]);
455 /* OPTIONAL: Check for the existence of \SystemRoot\System32\ntkrnlpa.exe */
457 /* Check for the existence of \SystemRoot\System32\ntdll.dll and retrieves its vendor name */
458 PathName
= L
"System32\\ntdll.dll";
459 Success
= CheckForValidPEAndVendor(SystemRootDirectory
, PathName
, &VendorName
);
461 DPRINT1("User-mode DLL '%S' is either not a PE file, or does not have any vendor?\n", PathName
);
464 for (i
= 0; i
< ARRAYSIZE(KnownVendors
); ++i
)
466 if (!!FindSubStrI(VendorName
.Buffer
, KnownVendors
[i
]))
468 /* We have found a correct vendor combination */
469 DPRINT1("IsValidNTOSInstallation: The user-mode DLL '%S' is from %S\n", PathName
, KnownVendors
[i
]);
479 IsValidNTOSInstallation_UStr(
480 IN PUNICODE_STRING SystemRootPath
)
483 OBJECT_ATTRIBUTES ObjectAttributes
;
484 IO_STATUS_BLOCK IoStatusBlock
;
485 HANDLE SystemRootDirectory
;
488 /* Open SystemRootPath */
489 InitializeObjectAttributes(&ObjectAttributes
,
491 OBJ_CASE_INSENSITIVE
,
494 Status
= NtOpenFile(&SystemRootDirectory
,
495 FILE_LIST_DIRECTORY
| SYNCHRONIZE
,
498 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
499 FILE_SYNCHRONOUS_IO_NONALERT
| FILE_DIRECTORY_FILE
);
500 if (!NT_SUCCESS(Status
))
502 DPRINT1("Failed to open SystemRoot '%wZ', Status 0x%08lx\n", SystemRootPath
, Status
);
506 Success
= IsValidNTOSInstallationByHandle(SystemRootDirectory
);
509 NtClose(SystemRootDirectory
);
514 IsValidNTOSInstallation(
515 IN PCWSTR SystemRoot
)
517 UNICODE_STRING SystemRootPath
;
518 RtlInitUnicodeString(&SystemRootPath
, SystemRoot
);
519 return IsValidNTOSInstallationByHandle(&SystemRootPath
);
524 IN PGENERIC_LIST List
)
526 PGENERIC_LIST_ENTRY Entry
;
527 PNTOS_INSTALLATION NtOsInstall
;
528 ULONG NtOsInstallsCount
= GetNumberOfListEntries(List
);
530 DPRINT1("There %s %d installation%s detected:\n",
531 NtOsInstallsCount
>= 2 ? "are" : "is",
533 NtOsInstallsCount
>= 2 ? "s" : "");
535 Entry
= GetFirstListEntry(List
);
538 NtOsInstall
= (PNTOS_INSTALLATION
)GetListEntryUserData(Entry
);
539 Entry
= GetNextListEntry(Entry
);
541 DPRINT1(" On disk #%d, partition #%d: Installation \"%S\" in SystemRoot '%wZ'\n",
542 NtOsInstall
->DiskNumber
, NtOsInstall
->PartitionNumber
,
543 NtOsInstall
->InstallationName
, &NtOsInstall
->SystemNtPath
);
549 static PNTOS_INSTALLATION
550 FindExistingNTOSInstall(
551 IN PGENERIC_LIST List
,
552 IN PCWSTR SystemRootArcPath OPTIONAL
,
553 IN PUNICODE_STRING SystemRootNtPath OPTIONAL
// or PCWSTR ?
556 PGENERIC_LIST_ENTRY Entry
;
557 PNTOS_INSTALLATION NtOsInstall
;
558 UNICODE_STRING SystemArcPath
;
561 * We search either via ARC path or NT path.
562 * If both pointers are NULL then we fail straight away.
564 if (!SystemRootArcPath
&& !SystemRootNtPath
)
567 RtlInitUnicodeString(&SystemArcPath
, SystemRootArcPath
);
569 Entry
= GetFirstListEntry(List
);
572 NtOsInstall
= (PNTOS_INSTALLATION
)GetListEntryUserData(Entry
);
573 Entry
= GetNextListEntry(Entry
);
576 * Note that if both ARC paths are equal, then the corresponding
577 * NT paths must be the same. However, two ARC paths may be different
578 * but resolve into the same NT path.
580 if ( (SystemRootArcPath
&&
581 RtlEqualUnicodeString(&NtOsInstall
->SystemArcPath
,
582 &SystemArcPath
, TRUE
)) ||
584 RtlEqualUnicodeString(&NtOsInstall
->SystemNtPath
,
585 SystemRootNtPath
, TRUE
)) )
595 static PNTOS_INSTALLATION
597 IN PGENERIC_LIST List
,
598 IN PCWSTR SystemRootArcPath
,
599 IN PUNICODE_STRING SystemRootNtPath
, // or PCWSTR ?
600 IN PCWSTR PathComponent
, // Pointer inside SystemRootNtPath buffer
602 IN ULONG PartitionNumber
,
603 IN PPARTENTRY PartEntry OPTIONAL
,
604 IN PCWSTR InstallationName
)
606 PNTOS_INSTALLATION NtOsInstall
;
607 SIZE_T ArcPathLength
, NtPathLength
;
608 CHAR InstallNameA
[MAX_PATH
];
610 /* Is there already any installation with these settings? */
611 NtOsInstall
= FindExistingNTOSInstall(List
, SystemRootArcPath
, SystemRootNtPath
);
614 DPRINT1("An NTOS installation with name \"%S\" already exists on disk #%d, partition #%d, in SystemRoot '%wZ'\n",
615 NtOsInstall
->InstallationName
, NtOsInstall
->DiskNumber
, NtOsInstall
->PartitionNumber
, &NtOsInstall
->SystemNtPath
);
617 // NOTE: We may use its "IsDefault" attribute, and only keep the entries that have IsDefault == TRUE...
618 // Setting IsDefault to TRUE would imply searching for the "Default" entry in the loader configuration file.
623 ArcPathLength
= (wcslen(SystemRootArcPath
) + 1) * sizeof(WCHAR
);
624 // NtPathLength = ROUND_UP(SystemRootNtPath->Length + sizeof(UNICODE_NULL), sizeof(WCHAR));
625 NtPathLength
= SystemRootNtPath
->Length
+ sizeof(UNICODE_NULL
);
627 /* None was found, so add a new one */
628 NtOsInstall
= RtlAllocateHeap(ProcessHeap
, HEAP_ZERO_MEMORY
,
629 sizeof(*NtOsInstall
) +
630 ArcPathLength
+ NtPathLength
);
634 NtOsInstall
->DiskNumber
= DiskNumber
;
635 NtOsInstall
->PartitionNumber
= PartitionNumber
;
636 NtOsInstall
->PartEntry
= PartEntry
;
638 RtlInitEmptyUnicodeString(&NtOsInstall
->SystemArcPath
,
639 (PWCHAR
)(NtOsInstall
+ 1),
641 RtlCopyMemory(NtOsInstall
->SystemArcPath
.Buffer
, SystemRootArcPath
, ArcPathLength
);
642 NtOsInstall
->SystemArcPath
.Length
= ArcPathLength
- sizeof(UNICODE_NULL
);
644 RtlInitEmptyUnicodeString(&NtOsInstall
->SystemNtPath
,
645 (PWCHAR
)((ULONG_PTR
)(NtOsInstall
+ 1) + ArcPathLength
),
647 RtlCopyUnicodeString(&NtOsInstall
->SystemNtPath
, SystemRootNtPath
);
648 NtOsInstall
->PathComponent
= NtOsInstall
->SystemNtPath
.Buffer
+
649 (PathComponent
- SystemRootNtPath
->Buffer
);
651 StringCchCopyW(NtOsInstall
->InstallationName
, ARRAYSIZE(NtOsInstall
->InstallationName
), InstallationName
);
653 // Having the GENERIC_LIST storing the display item string plainly sucks...
654 StringCchPrintfA(InstallNameA
, ARRAYSIZE(InstallNameA
), "%S", InstallationName
);
655 AppendGenericListEntry(List
, InstallNameA
, NtOsInstall
, FALSE
);
661 FindNTOSInstallations(
662 IN OUT PGENERIC_LIST List
,
663 IN PPARTLIST PartList
,
664 IN PPARTENTRY PartEntry
)
667 ULONG DiskNumber
= PartEntry
->DiskEntry
->DiskNumber
;
668 ULONG PartitionNumber
= PartEntry
->PartitionNumber
;
669 HANDLE PartitionHandle
;
670 OBJECT_ATTRIBUTES ObjectAttributes
;
671 IO_STATUS_BLOCK IoStatusBlock
;
672 UNICODE_STRING PartitionRootPath
;
673 NTOS_BOOT_LOADER_TYPE Type
;
674 ENUM_INSTALLS_DATA Data
;
676 WCHAR PathBuffer
[MAX_PATH
];
678 /* Set PartitionRootPath */
679 StringCchPrintfW(PathBuffer
, ARRAYSIZE(PathBuffer
),
680 L
"\\Device\\Harddisk%lu\\Partition%lu\\",
681 DiskNumber
, PartitionNumber
);
682 RtlInitUnicodeString(&PartitionRootPath
, PathBuffer
);
683 DPRINT1("FindNTOSInstallations: PartitionRootPath: '%wZ'\n", &PartitionRootPath
);
685 /* Open the partition */
686 InitializeObjectAttributes(&ObjectAttributes
,
688 OBJ_CASE_INSENSITIVE
,
691 Status
= NtOpenFile(&PartitionHandle
,
692 FILE_LIST_DIRECTORY
| SYNCHRONIZE
,
695 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
696 FILE_SYNCHRONOUS_IO_NONALERT
| FILE_DIRECTORY_FILE
);
697 if (!NT_SUCCESS(Status
))
699 DPRINT1("Failed to open partition '%wZ', Status 0x%08lx\n", &PartitionRootPath
, Status
);
704 Data
.PartList
= PartList
;
706 /* Try to see whether we recognize some NT boot loaders */
707 for (Type
= FreeLdr
; Type
< BldrTypeMax
; ++Type
)
709 Status
= FindNTOSBootLoader(PartitionHandle
, Type
, &Version
);
710 if (!NT_SUCCESS(Status
))
712 /* The loader does not exist, continue with another one */
713 DPRINT1("Loader type '%d' does not exist, or an error happened (Status 0x%08lx), continue with another one...\n",
718 /* The loader exists, try to enumerate its boot entries */
719 DPRINT1("Analyse the OS installations for loader type '%d' in disk #%d, partition #%d\n",
720 Type
, DiskNumber
, PartitionNumber
);
722 EnumerateNTOSBootEntries(PartitionHandle
, Type
, EnumerateInstallations
, &Data
);
725 /* Close the partition */
726 NtClose(PartitionHandle
);
731 ShouldICheckThisPartition(
732 IN PPARTENTRY PartEntry
)
737 return PartEntry
->IsPartitioned
&&
738 !IsContainerPartition(PartEntry
->PartitionType
) /* alternatively: PartEntry->PartitionNumber != 0 */ &&
740 (PartEntry
->FormatState
== Preformatted
/* || PartEntry->FormatState == Formatted */);
743 // EnumerateNTOSInstallations
745 CreateNTOSInstallationsList(
746 IN PPARTLIST PartList
)
749 PLIST_ENTRY Entry
, Entry2
;
750 PDISKENTRY DiskEntry
;
751 PPARTENTRY PartEntry
;
753 List
= CreateGenericList();
757 /* Loop each available disk ... */
758 Entry
= PartList
->DiskListHead
.Flink
;
759 while (Entry
!= &PartList
->DiskListHead
)
761 DiskEntry
= CONTAINING_RECORD(Entry
, DISKENTRY
, ListEntry
);
762 Entry
= Entry
->Flink
;
764 DPRINT1("Disk #%d\n", DiskEntry
->DiskNumber
);
766 /* ... and for each disk, loop each available partition */
768 /* First, the primary partitions */
769 Entry2
= DiskEntry
->PrimaryPartListHead
.Flink
;
770 while (Entry2
!= &DiskEntry
->PrimaryPartListHead
)
772 PartEntry
= CONTAINING_RECORD(Entry2
, PARTENTRY
, ListEntry
);
773 Entry2
= Entry2
->Flink
;
775 ASSERT(PartEntry
->DiskEntry
== DiskEntry
);
777 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",
778 PartEntry
->PartitionNumber
, PartEntry
->PartitionIndex
,
779 PartEntry
->PartitionType
, PartEntry
->LogicalPartition
? "TRUE" : "FALSE",
780 PartEntry
->IsPartitioned
? "TRUE" : "FALSE",
781 PartEntry
->New
? "Yes" : "No",
782 PartEntry
->AutoCreate
? "Yes" : "No",
783 PartEntry
->FormatState
,
784 ShouldICheckThisPartition(PartEntry
) ? "YES!" : "NO!");
786 if (ShouldICheckThisPartition(PartEntry
))
787 FindNTOSInstallations(List
, PartList
, PartEntry
);
790 /* Then, the logical partitions (present in the extended partition) */
791 Entry2
= DiskEntry
->LogicalPartListHead
.Flink
;
792 while (Entry2
!= &DiskEntry
->LogicalPartListHead
)
794 PartEntry
= CONTAINING_RECORD(Entry2
, PARTENTRY
, ListEntry
);
795 Entry2
= Entry2
->Flink
;
797 ASSERT(PartEntry
->DiskEntry
== DiskEntry
);
799 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",
800 PartEntry
->PartitionNumber
, PartEntry
->PartitionIndex
,
801 PartEntry
->PartitionType
, PartEntry
->LogicalPartition
? "TRUE" : "FALSE",
802 PartEntry
->IsPartitioned
? "TRUE" : "FALSE",
803 PartEntry
->New
? "Yes" : "No",
804 PartEntry
->AutoCreate
? "Yes" : "No",
805 PartEntry
->FormatState
,
806 ShouldICheckThisPartition(PartEntry
) ? "YES!" : "NO!");
808 if (ShouldICheckThisPartition(PartEntry
))
809 FindNTOSInstallations(List
, PartList
, PartEntry
);
813 /**** Debugging: List all the collected installations ****/
814 DumpNTOSInstalls(List
);