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"
26 /* GLOBALS ******************************************************************/
28 /* Language-independent Vendor strings */
29 static const PCWSTR KnownVendors
[] = { VENDOR_REACTOS
, VENDOR_MICROSOFT
};
32 /* FUNCTIONS ****************************************************************/
35 IsValidNTOSInstallation(
36 IN PUNICODE_STRING SystemRootPath
,
37 OUT PUNICODE_STRING VendorName OPTIONAL
);
39 static PNTOS_INSTALLATION
40 FindExistingNTOSInstall(
41 IN PGENERIC_LIST List
,
42 IN PCWSTR SystemRootArcPath OPTIONAL
,
43 IN PUNICODE_STRING SystemRootNtPath OPTIONAL
// or PCWSTR ?
46 static PNTOS_INSTALLATION
48 IN PGENERIC_LIST List
,
49 IN PCWSTR InstallationName
,
51 IN PCWSTR SystemRootArcPath
,
52 IN PUNICODE_STRING SystemRootNtPath
, // or PCWSTR ?
53 IN PCWSTR PathComponent
, // Pointer inside SystemRootNtPath buffer
55 IN ULONG PartitionNumber
,
56 IN PPARTENTRY PartEntry OPTIONAL
);
58 typedef struct _ENUM_INSTALLS_DATA
60 IN OUT PGENERIC_LIST List
;
61 IN PPARTLIST PartList
;
62 // IN PPARTENTRY PartEntry;
63 } ENUM_INSTALLS_DATA
, *PENUM_INSTALLS_DATA
;
65 // PENUM_BOOT_ENTRIES_ROUTINE
68 EnumerateInstallations(
69 IN BOOT_STORE_TYPE Type
,
70 IN PBOOT_STORE_ENTRY BootEntry
,
71 IN PVOID Parameter OPTIONAL
)
73 PENUM_INSTALLS_DATA Data
= (PENUM_INSTALLS_DATA
)Parameter
;
74 PNTOS_OPTIONS Options
= (PNTOS_OPTIONS
)&BootEntry
->OsOptions
;
75 PNTOS_INSTALLATION NtOsInstall
;
77 ULONG DiskNumber
= 0, PartitionNumber
= 0;
78 PCWSTR PathComponent
= NULL
;
79 PDISKENTRY DiskEntry
= NULL
;
80 PPARTENTRY PartEntry
= NULL
;
82 UNICODE_STRING SystemRootPath
;
83 WCHAR SystemRoot
[MAX_PATH
];
85 UNICODE_STRING VendorName
;
86 WCHAR VendorNameBuffer
[MAX_PATH
];
89 /* We have a boot entry */
91 /* Check for supported boot type "Windows2003" */
92 if (BootEntry
->OsOptionsLength
< sizeof(NTOS_OPTIONS
) ||
93 RtlCompareMemory(&BootEntry
->OsOptions
/* Signature */,
94 NTOS_OPTIONS_SIGNATURE
,
95 RTL_FIELD_SIZE(NTOS_OPTIONS
, Signature
)) !=
96 RTL_FIELD_SIZE(NTOS_OPTIONS
, Signature
))
98 /* This is not a ReactOS entry */
99 // DPRINT1(" An installation '%S' of unsupported type '%S'\n",
100 // BootEntry->FriendlyName, BootEntry->Version ? BootEntry->Version : L"n/a");
101 DPRINT1(" An installation '%S' of unsupported type %lu\n",
102 BootEntry
->FriendlyName
, BootEntry
->OsOptionsLength
);
103 /* Continue the enumeration */
104 return STATUS_SUCCESS
;
107 /* BootType is Windows2003, now check OsLoadPath */
108 if (!Options
->OsLoadPath
|| !*Options
->OsLoadPath
)
110 /* Certainly not a ReactOS installation */
111 DPRINT1(" A Win2k3 install '%S' without an ARC path?!\n", BootEntry
->FriendlyName
);
112 /* Continue the enumeration */
113 return STATUS_SUCCESS
;
116 DPRINT1(" Found a candidate Win2k3 install '%S' with ARC path '%S'\n",
117 BootEntry
->FriendlyName
, Options
->OsLoadPath
);
118 // DPRINT1(" Found a Win2k3 install '%S' with ARC path '%S'\n",
119 // BootEntry->FriendlyName, Options->OsLoadPath);
121 // TODO: Normalize the ARC path.
124 * Check whether we already have an installation with this ARC path.
125 * If this is the case, stop there.
127 NtOsInstall
= FindExistingNTOSInstall(Data
->List
, Options
->OsLoadPath
, NULL
);
130 DPRINT1(" An NTOS installation with name \"%S\" from vendor \"%S\" already exists in SystemRoot '%wZ'\n",
131 NtOsInstall
->InstallationName
, NtOsInstall
->VendorName
, &NtOsInstall
->SystemArcPath
);
132 /* Continue the enumeration */
133 return STATUS_SUCCESS
;
137 * Convert the ARC path into an NT path, from which we will deduce
138 * the real disk drive & partition on which the candidate installation
139 * resides, as well verifying whether it is indeed an NTOS installation.
141 RtlInitEmptyUnicodeString(&SystemRootPath
, SystemRoot
, sizeof(SystemRoot
));
142 if (!ArcPathToNtPath(&SystemRootPath
, Options
->OsLoadPath
, Data
->PartList
))
144 DPRINT1("ArcPathToNtPath(%S) failed, skip the installation.\n", Options
->OsLoadPath
);
145 /* Continue the enumeration */
146 return STATUS_SUCCESS
;
149 DPRINT1("ArcPathToNtPath() succeeded: '%S' --> '%wZ'\n",
150 Options
->OsLoadPath
, &SystemRootPath
);
153 * Check whether we already have an installation with this NT path.
154 * If this is the case, stop there.
156 NtOsInstall
= FindExistingNTOSInstall(Data
->List
, NULL
/*Options->OsLoadPath*/, &SystemRootPath
);
159 DPRINT1(" An NTOS installation with name \"%S\" from vendor \"%S\" already exists in SystemRoot '%wZ'\n",
160 NtOsInstall
->InstallationName
, NtOsInstall
->VendorName
, &NtOsInstall
->SystemNtPath
);
161 /* Continue the enumeration */
162 return STATUS_SUCCESS
;
165 DPRINT1("EnumerateInstallations: SystemRootPath: '%wZ'\n", &SystemRootPath
);
167 /* Check if this is a valid NTOS installation; stop there if it isn't one */
168 RtlInitEmptyUnicodeString(&VendorName
, VendorNameBuffer
, sizeof(VendorNameBuffer
));
169 if (!IsValidNTOSInstallation(&SystemRootPath
, &VendorName
))
171 /* Continue the enumeration */
172 return STATUS_SUCCESS
;
175 DPRINT1("Found a valid NTOS installation in SystemRoot ARC path '%S', NT path '%wZ'\n",
176 Options
->OsLoadPath
, &SystemRootPath
);
178 /* From the NT path, compute the disk, partition and path components */
179 if (NtPathToDiskPartComponents(SystemRootPath
.Buffer
, &DiskNumber
, &PartitionNumber
, &PathComponent
))
181 DPRINT1("SystemRootPath = '%wZ' points to disk #%d, partition #%d, path '%S'\n",
182 &SystemRootPath
, DiskNumber
, PartitionNumber
, PathComponent
);
184 /* Retrieve the corresponding disk and partition */
185 if (!GetDiskOrPartition(Data
->PartList
, DiskNumber
, PartitionNumber
, &DiskEntry
, &PartEntry
))
187 DPRINT1("GetDiskOrPartition(disk #%d, partition #%d) failed\n",
188 DiskNumber
, PartitionNumber
);
193 DPRINT1("NtPathToDiskPartComponents(%wZ) failed\n", &SystemRootPath
);
196 /* Add the discovered NTOS installation into the list */
197 AddNTOSInstallation(Data
->List
,
198 BootEntry
->FriendlyName
,
199 VendorName
.Buffer
, // FIXME: What if it's not NULL-terminated?
201 &SystemRootPath
, PathComponent
,
202 DiskNumber
, PartitionNumber
, PartEntry
);
204 /* Continue the enumeration */
205 return STATUS_SUCCESS
;
209 * FindSubStrI(PCWSTR str, PCWSTR strSearch) :
210 * Searches for a sub-string 'strSearch' inside 'str', similarly to what
211 * wcsstr(str, strSearch) does, but ignores the case during the comparisons.
213 PCWSTR
FindSubStrI(PCWSTR str
, PCWSTR strSearch
)
226 while (*s1
&& *s2
&& (towupper(*s1
) == towupper(*s2
)))
239 CheckForValidPEAndVendor(
240 IN HANDLE RootDirectory OPTIONAL
,
241 IN PCWSTR PathNameToFile
,
242 OUT PUNICODE_STRING VendorName
245 BOOLEAN Success
= FALSE
;
247 HANDLE FileHandle
, SectionHandle
;
250 PVOID VersionBuffer
= NULL
; // Read-only
254 if (VendorName
->MaximumLength
< sizeof(UNICODE_NULL
))
257 *VendorName
->Buffer
= UNICODE_NULL
;
258 VendorName
->Length
= 0;
260 Status
= OpenAndMapFile(RootDirectory
, PathNameToFile
,
261 &FileHandle
, &SectionHandle
, &ViewBase
,
263 if (!NT_SUCCESS(Status
))
265 DPRINT1("Failed to open and map file '%S', Status 0x%08lx\n", PathNameToFile
, Status
);
266 return FALSE
; // Status;
269 /* Make sure it's a valid PE file */
270 if (!RtlImageNtHeader(ViewBase
))
272 DPRINT1("File '%S' does not seem to be a valid PE, bail out\n", PathNameToFile
);
273 Status
= STATUS_INVALID_IMAGE_FORMAT
;
278 * Search for a valid executable version and vendor.
279 * NOTE: The module is loaded as a data file, it should be marked as such.
281 Status
= NtGetVersionResource((PVOID
)((ULONG_PTR
)ViewBase
| 1), &VersionBuffer
, NULL
);
282 if (!NT_SUCCESS(Status
))
284 DPRINT1("Failed to get version resource for file '%S', Status 0x%08lx\n", PathNameToFile
, Status
);
288 Status
= NtVerQueryValue(VersionBuffer
, L
"\\VarFileInfo\\Translation", &pvData
, &BufLen
);
289 if (NT_SUCCESS(Status
))
291 USHORT wCodePage
= 0, wLangID
= 0;
292 WCHAR FileInfo
[MAX_PATH
];
294 wCodePage
= LOWORD(*(ULONG
*)pvData
);
295 wLangID
= HIWORD(*(ULONG
*)pvData
);
297 RtlStringCchPrintfW(FileInfo
, ARRAYSIZE(FileInfo
),
298 L
"StringFileInfo\\%04X%04X\\CompanyName",
301 Status
= NtVerQueryValue(VersionBuffer
, FileInfo
, &pvData
, &BufLen
);
303 /* Fixup the Status in case pvData is NULL */
304 if (NT_SUCCESS(Status
) && !pvData
)
305 Status
= STATUS_NOT_FOUND
;
307 if (NT_SUCCESS(Status
) /*&& pvData*/)
309 /* BufLen includes the NULL terminator count */
310 DPRINT1("Found version vendor: \"%S\" for file '%S'\n", pvData
, PathNameToFile
);
312 RtlStringCbCopyNW(VendorName
->Buffer
, VendorName
->MaximumLength
,
313 pvData
, BufLen
* sizeof(WCHAR
));
314 VendorName
->Length
= wcslen(VendorName
->Buffer
) * sizeof(WCHAR
);
320 if (!NT_SUCCESS(Status
))
321 DPRINT1("No version vendor found for file '%S'\n", PathNameToFile
);
324 /* Finally, unmap and close the file */
325 UnMapAndCloseFile(FileHandle
, SectionHandle
, ViewBase
);
331 // TODO: Instead of returning TRUE/FALSE, it would be nice to return
332 // a flag indicating:
333 // - whether the installation is actually valid;
334 // - if it's broken or not (aka. needs for repair, or just upgrading).
337 IsValidNTOSInstallationByHandle(
338 IN HANDLE SystemRootDirectory
,
339 OUT PUNICODE_STRING VendorName OPTIONAL
)
341 BOOLEAN Success
= FALSE
;
344 UNICODE_STRING LocalVendorName
;
345 WCHAR VendorNameBuffer
[MAX_PATH
];
347 /* Check for VendorName validity */
348 if (VendorName
->MaximumLength
< sizeof(UNICODE_NULL
))
350 /* Don't use it, invalidate the pointer */
356 *VendorName
->Buffer
= UNICODE_NULL
;
357 VendorName
->Length
= 0;
360 /* Check for the existence of \SystemRoot\System32 */
361 PathName
= L
"System32\\";
362 if (!DoesDirExist(SystemRootDirectory
, PathName
))
364 // DPRINT1("Failed to open directory '%S', Status 0x%08lx\n", PathName, Status);
368 /* Check for the existence of \SystemRoot\System32\drivers */
369 PathName
= L
"System32\\drivers\\";
370 if (!DoesDirExist(SystemRootDirectory
, PathName
))
372 // DPRINT1("Failed to open directory '%S', Status 0x%08lx\n", PathName, Status);
376 /* Check for the existence of \SystemRoot\System32\config */
377 PathName
= L
"System32\\config\\";
378 if (!DoesDirExist(SystemRootDirectory
, PathName
))
380 // DPRINT1("Failed to open directory '%S', Status 0x%08lx\n", PathName, Status);
386 * Check for the existence of SYSTEM and SOFTWARE hives in \SystemRoot\System32\config
387 * (but we don't check here whether they are actually valid).
389 PathName
= L
"System32\\config\\SYSTEM";
390 if (!DoesFileExist(SystemRootDirectory
, PathName
))
392 // DPRINT1("Failed to open file '%S', Status 0x%08lx\n", PathName, Status);
395 PathName
= L
"System32\\config\\SOFTWARE";
396 if (!DoesFileExist(SystemRootDirectory
, PathName
))
398 // DPRINT1("Failed to open file '%S', Status 0x%08lx\n", PathName, Status);
403 RtlInitEmptyUnicodeString(&LocalVendorName
, VendorNameBuffer
, sizeof(VendorNameBuffer
));
405 /* Check for the existence of \SystemRoot\System32\ntoskrnl.exe and retrieves its vendor name */
406 PathName
= L
"System32\\ntoskrnl.exe";
407 Success
= CheckForValidPEAndVendor(SystemRootDirectory
, PathName
, &LocalVendorName
);
409 DPRINT1("Kernel executable '%S' is either not a PE file, or does not have any vendor?\n", PathName
);
411 /* The kernel gives the OS its flavour */
414 for (i
= 0; i
< ARRAYSIZE(KnownVendors
); ++i
)
416 Success
= !!FindSubStrI(LocalVendorName
.Buffer
, KnownVendors
[i
]);
419 /* We have found a correct vendor combination */
420 DPRINT1("IsValidNTOSInstallation: We've got an NTOS installation from %S !\n", KnownVendors
[i
]);
426 /* Return the vendor name */
429 /* Copy the string and invalidate the pointer */
430 RtlCopyUnicodeString(VendorName
, &LocalVendorName
);
434 /* OPTIONAL: Check for the existence of \SystemRoot\System32\ntkrnlpa.exe */
436 /* Check for the existence of \SystemRoot\System32\ntdll.dll and retrieves its vendor name */
437 PathName
= L
"System32\\ntdll.dll";
438 Success
= CheckForValidPEAndVendor(SystemRootDirectory
, PathName
, &LocalVendorName
);
440 DPRINT1("User-mode DLL '%S' is either not a PE file, or does not have any vendor?\n", PathName
);
443 for (i
= 0; i
< ARRAYSIZE(KnownVendors
); ++i
)
445 if (!!FindSubStrI(LocalVendorName
.Buffer
, KnownVendors
[i
]))
447 /* We have found a correct vendor combination */
448 DPRINT1("IsValidNTOSInstallation: The user-mode DLL '%S' is from %S\n", PathName
, KnownVendors
[i
]);
454 /* Return the vendor name if not already obtained */
457 /* Copy the string and invalidate the pointer */
458 RtlCopyUnicodeString(VendorName
, &LocalVendorName
);
466 IsValidNTOSInstallation(
467 IN PUNICODE_STRING SystemRootPath
,
468 OUT PUNICODE_STRING VendorName OPTIONAL
)
471 OBJECT_ATTRIBUTES ObjectAttributes
;
472 IO_STATUS_BLOCK IoStatusBlock
;
473 HANDLE SystemRootDirectory
;
476 /* Open SystemRootPath */
477 InitializeObjectAttributes(&ObjectAttributes
,
479 OBJ_CASE_INSENSITIVE
,
482 Status
= NtOpenFile(&SystemRootDirectory
,
483 FILE_LIST_DIRECTORY
| FILE_TRAVERSE
| SYNCHRONIZE
,
486 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
487 FILE_SYNCHRONOUS_IO_NONALERT
| FILE_DIRECTORY_FILE
);
488 if (!NT_SUCCESS(Status
))
490 DPRINT1("Failed to open SystemRoot '%wZ', Status 0x%08lx\n", SystemRootPath
, Status
);
494 Success
= IsValidNTOSInstallationByHandle(SystemRootDirectory
, VendorName
);
497 NtClose(SystemRootDirectory
);
503 IN PGENERIC_LIST List
)
505 PGENERIC_LIST_ENTRY Entry
;
506 PNTOS_INSTALLATION NtOsInstall
;
507 ULONG NtOsInstallsCount
= GetNumberOfListEntries(List
);
509 DPRINT1("There %s %d installation%s detected:\n",
510 NtOsInstallsCount
>= 2 ? "are" : "is",
512 NtOsInstallsCount
>= 2 ? "s" : "");
514 for (Entry
= GetFirstListEntry(List
); Entry
; Entry
= GetNextListEntry(Entry
))
516 NtOsInstall
= (PNTOS_INSTALLATION
)GetListEntryData(Entry
);
518 DPRINT1(" On disk #%d, partition #%d: Installation \"%S\" in SystemRoot '%wZ'\n",
519 NtOsInstall
->DiskNumber
, NtOsInstall
->PartitionNumber
,
520 NtOsInstall
->InstallationName
, &NtOsInstall
->SystemNtPath
);
526 static PNTOS_INSTALLATION
527 FindExistingNTOSInstall(
528 IN PGENERIC_LIST List
,
529 IN PCWSTR SystemRootArcPath OPTIONAL
,
530 IN PUNICODE_STRING SystemRootNtPath OPTIONAL
// or PCWSTR ?
533 PGENERIC_LIST_ENTRY Entry
;
534 PNTOS_INSTALLATION NtOsInstall
;
535 UNICODE_STRING SystemArcPath
;
538 * We search either via ARC path or NT path.
539 * If both pointers are NULL then we fail straight away.
541 if (!SystemRootArcPath
&& !SystemRootNtPath
)
544 RtlInitUnicodeString(&SystemArcPath
, SystemRootArcPath
);
546 for (Entry
= GetFirstListEntry(List
); Entry
; Entry
= GetNextListEntry(Entry
))
548 NtOsInstall
= (PNTOS_INSTALLATION
)GetListEntryData(Entry
);
551 * Note that if both ARC paths are equal, then the corresponding
552 * NT paths must be the same. However, two ARC paths may be different
553 * but resolve into the same NT path.
555 if ( (SystemRootArcPath
&&
556 RtlEqualUnicodeString(&NtOsInstall
->SystemArcPath
,
557 &SystemArcPath
, TRUE
)) ||
559 RtlEqualUnicodeString(&NtOsInstall
->SystemNtPath
,
560 SystemRootNtPath
, TRUE
)) )
570 static PNTOS_INSTALLATION
572 IN PGENERIC_LIST List
,
573 IN PCWSTR InstallationName
,
574 IN PCWSTR VendorName
,
575 IN PCWSTR SystemRootArcPath
,
576 IN PUNICODE_STRING SystemRootNtPath
, // or PCWSTR ?
577 IN PCWSTR PathComponent
, // Pointer inside SystemRootNtPath buffer
579 IN ULONG PartitionNumber
,
580 IN PPARTENTRY PartEntry OPTIONAL
)
582 PNTOS_INSTALLATION NtOsInstall
;
583 SIZE_T ArcPathLength
, NtPathLength
;
585 /* Is there already any installation with these settings? */
586 NtOsInstall
= FindExistingNTOSInstall(List
, SystemRootArcPath
, SystemRootNtPath
);
589 DPRINT1("An NTOS installation with name \"%S\" from vendor \"%S\" already exists on disk #%d, partition #%d, in SystemRoot '%wZ'\n",
590 NtOsInstall
->InstallationName
, NtOsInstall
->VendorName
,
591 NtOsInstall
->DiskNumber
, NtOsInstall
->PartitionNumber
, &NtOsInstall
->SystemNtPath
);
593 // NOTE: We may use its "IsDefault" attribute, and only keep the entries that have IsDefault == TRUE...
594 // Setting IsDefault to TRUE would imply searching for the "Default" entry in the loader configuration file.
599 ArcPathLength
= (wcslen(SystemRootArcPath
) + 1) * sizeof(WCHAR
);
600 // NtPathLength = ROUND_UP(SystemRootNtPath->Length + sizeof(UNICODE_NULL), sizeof(WCHAR));
601 NtPathLength
= SystemRootNtPath
->Length
+ sizeof(UNICODE_NULL
);
603 /* None was found, so add a new one */
604 NtOsInstall
= RtlAllocateHeap(ProcessHeap
, HEAP_ZERO_MEMORY
,
605 sizeof(*NtOsInstall
) +
606 ArcPathLength
+ NtPathLength
);
610 NtOsInstall
->DiskNumber
= DiskNumber
;
611 NtOsInstall
->PartitionNumber
= PartitionNumber
;
612 NtOsInstall
->PartEntry
= PartEntry
;
614 RtlInitEmptyUnicodeString(&NtOsInstall
->SystemArcPath
,
615 (PWCHAR
)(NtOsInstall
+ 1),
617 RtlCopyMemory(NtOsInstall
->SystemArcPath
.Buffer
, SystemRootArcPath
, ArcPathLength
);
618 NtOsInstall
->SystemArcPath
.Length
= ArcPathLength
- sizeof(UNICODE_NULL
);
620 RtlInitEmptyUnicodeString(&NtOsInstall
->SystemNtPath
,
621 (PWCHAR
)((ULONG_PTR
)(NtOsInstall
+ 1) + ArcPathLength
),
623 RtlCopyUnicodeString(&NtOsInstall
->SystemNtPath
, SystemRootNtPath
);
624 NtOsInstall
->PathComponent
= NtOsInstall
->SystemNtPath
.Buffer
+
625 (PathComponent
- SystemRootNtPath
->Buffer
);
627 RtlStringCchCopyW(NtOsInstall
->InstallationName
,
628 ARRAYSIZE(NtOsInstall
->InstallationName
),
631 RtlStringCchCopyW(NtOsInstall
->VendorName
,
632 ARRAYSIZE(NtOsInstall
->VendorName
),
635 AppendGenericListEntry(List
, NtOsInstall
, FALSE
);
641 FindNTOSInstallations(
642 IN OUT PGENERIC_LIST List
,
643 IN PPARTLIST PartList
,
644 IN PPARTENTRY PartEntry
)
647 ULONG DiskNumber
= PartEntry
->DiskEntry
->DiskNumber
;
648 ULONG PartitionNumber
= PartEntry
->PartitionNumber
;
649 HANDLE PartitionDirectoryHandle
;
650 OBJECT_ATTRIBUTES ObjectAttributes
;
651 IO_STATUS_BLOCK IoStatusBlock
;
652 UNICODE_STRING PartitionRootPath
;
653 BOOT_STORE_TYPE Type
;
654 PVOID BootStoreHandle
;
655 ENUM_INSTALLS_DATA Data
;
657 WCHAR PathBuffer
[MAX_PATH
];
659 /* Set PartitionRootPath */
660 RtlStringCchPrintfW(PathBuffer
, ARRAYSIZE(PathBuffer
),
661 L
"\\Device\\Harddisk%lu\\Partition%lu\\",
662 DiskNumber
, PartitionNumber
);
663 RtlInitUnicodeString(&PartitionRootPath
, PathBuffer
);
664 DPRINT1("FindNTOSInstallations: PartitionRootPath: '%wZ'\n", &PartitionRootPath
);
666 /* Open the partition */
667 InitializeObjectAttributes(&ObjectAttributes
,
669 OBJ_CASE_INSENSITIVE
,
672 Status
= NtOpenFile(&PartitionDirectoryHandle
,
673 FILE_LIST_DIRECTORY
| FILE_TRAVERSE
| SYNCHRONIZE
,
676 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
677 FILE_SYNCHRONOUS_IO_NONALERT
| FILE_DIRECTORY_FILE
);
678 if (!NT_SUCCESS(Status
))
680 DPRINT1("Failed to open partition '%wZ', Status 0x%08lx\n", &PartitionRootPath
, Status
);
685 Data
.PartList
= PartList
;
687 /* Try to see whether we recognize some NT boot loaders */
688 for (Type
= FreeLdr
; Type
< BldrTypeMax
; ++Type
)
690 Status
= FindBootStore(PartitionDirectoryHandle
, Type
, &Version
);
691 if (!NT_SUCCESS(Status
))
693 /* The loader does not exist, continue with another one */
694 DPRINT1("Loader type '%d' does not exist, or an error happened (Status 0x%08lx), continue with another one...\n",
699 /* The loader exists, try to enumerate its boot entries */
700 DPRINT1("Analyse the OS installations for loader type '%d' in disk #%d, partition #%d\n",
701 Type
, DiskNumber
, PartitionNumber
);
703 Status
= OpenBootStoreByHandle(&BootStoreHandle
, PartitionDirectoryHandle
, Type
, FALSE
);
704 if (!NT_SUCCESS(Status
))
706 DPRINT1("Could not open the NTOS boot store of type '%d' (Status 0x%08lx), continue with another one...\n",
710 EnumerateBootStoreEntries(BootStoreHandle
, EnumerateInstallations
, &Data
);
711 CloseBootStore(BootStoreHandle
);
714 /* Close the partition */
715 NtClose(PartitionDirectoryHandle
);
720 ShouldICheckThisPartition(
721 IN PPARTENTRY PartEntry
)
726 return PartEntry
->IsPartitioned
&&
727 !IsContainerPartition(PartEntry
->PartitionType
) /* alternatively: PartEntry->PartitionNumber != 0 */ &&
729 (PartEntry
->FormatState
== Preformatted
/* || PartEntry->FormatState == Formatted */);
732 // EnumerateNTOSInstallations
734 CreateNTOSInstallationsList(
735 IN PPARTLIST PartList
)
738 PLIST_ENTRY Entry
, Entry2
;
739 PDISKENTRY DiskEntry
;
740 PPARTENTRY PartEntry
;
742 List
= CreateGenericList();
746 /* Loop each available disk ... */
747 Entry
= PartList
->DiskListHead
.Flink
;
748 while (Entry
!= &PartList
->DiskListHead
)
750 DiskEntry
= CONTAINING_RECORD(Entry
, DISKENTRY
, ListEntry
);
751 Entry
= Entry
->Flink
;
753 DPRINT1("Disk #%d\n", DiskEntry
->DiskNumber
);
755 /* ... and for each disk, loop each available partition */
757 /* First, the primary partitions */
758 Entry2
= DiskEntry
->PrimaryPartListHead
.Flink
;
759 while (Entry2
!= &DiskEntry
->PrimaryPartListHead
)
761 PartEntry
= CONTAINING_RECORD(Entry2
, PARTENTRY
, ListEntry
);
762 Entry2
= Entry2
->Flink
;
764 ASSERT(PartEntry
->DiskEntry
== DiskEntry
);
766 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",
767 PartEntry
->PartitionNumber
, PartEntry
->PartitionIndex
,
768 PartEntry
->PartitionType
, PartEntry
->LogicalPartition
? "TRUE" : "FALSE",
769 PartEntry
->IsPartitioned
? "TRUE" : "FALSE",
770 PartEntry
->New
? "Yes" : "No",
771 PartEntry
->AutoCreate
? "Yes" : "No",
772 PartEntry
->FormatState
,
773 ShouldICheckThisPartition(PartEntry
) ? "YES!" : "NO!");
775 if (ShouldICheckThisPartition(PartEntry
))
776 FindNTOSInstallations(List
, PartList
, PartEntry
);
779 /* Then, the logical partitions (present in the extended partition) */
780 Entry2
= DiskEntry
->LogicalPartListHead
.Flink
;
781 while (Entry2
!= &DiskEntry
->LogicalPartListHead
)
783 PartEntry
= CONTAINING_RECORD(Entry2
, PARTENTRY
, ListEntry
);
784 Entry2
= Entry2
->Flink
;
786 ASSERT(PartEntry
->DiskEntry
== DiskEntry
);
788 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",
789 PartEntry
->PartitionNumber
, PartEntry
->PartitionIndex
,
790 PartEntry
->PartitionType
, PartEntry
->LogicalPartition
? "TRUE" : "FALSE",
791 PartEntry
->IsPartitioned
? "TRUE" : "FALSE",
792 PartEntry
->New
? "Yes" : "No",
793 PartEntry
->AutoCreate
? "Yes" : "No",
794 PartEntry
->FormatState
,
795 ShouldICheckThisPartition(PartEntry
) ? "YES!" : "NO!");
797 if (ShouldICheckThisPartition(PartEntry
))
798 FindNTOSInstallations(List
, PartList
, PartEntry
);
802 /**** Debugging: List all the collected installations ****/
803 DumpNTOSInstalls(List
);