From 5a6050902bd78e831d525fc56a1714f155bd86f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herm=C3=A8s=20B=C3=A9lusca-Ma=C3=AFto?= Date: Mon, 22 May 2017 01:19:08 +0000 Subject: [PATCH] [USETUP] Improve the NTOS installations detector. In all the disks/partitions available, it searches for the presence of freeldr.ini / boot.ini, open & parse them, and enumerates the available boot entries (as candidates for installations). For each of them, it maps their ARC paths into the NT namespace (hence the ARC 2 NT path resolver committed in r74621), then attempts to detect in these paths the existence of NTOS installations. svn path=/branches/setup_improvements/; revision=74622 --- base/setup/usetup/osdetect.c | 593 +++++++++++++++++++++++++++-------- base/setup/usetup/osdetect.h | 8 +- 2 files changed, 470 insertions(+), 131 deletions(-) diff --git a/base/setup/usetup/osdetect.c b/base/setup/usetup/osdetect.c index 23f1dee7a91..34c18e7b110 100644 --- a/base/setup/usetup/osdetect.c +++ b/base/setup/usetup/osdetect.c @@ -94,28 +94,41 @@ NTOS_BOOT_LOADER_FILES NtosBootLoaders[] = // {BootMgr, L"bootmgr" , ???} }; -#if 0 -static VOID -EnumerateInstallationsFreeLdr() +static BOOLEAN +IsValidNTOSInstallation( + IN HANDLE SystemRootDirectory OPTIONAL, + IN PCWSTR SystemRoot OPTIONAL); + +static PNTOS_INSTALLATION +AddNTOSInstallation( + IN PGENERIC_LIST List, + IN ULONG DiskNumber, + IN ULONG PartitionNumber, + IN PCWSTR SystemRoot, + IN PCWSTR InstallationName); + +static NTSTATUS +FreeLdrEnumerateInstallations( + IN OUT PGENERIC_LIST List, + IN PPARTLIST PartList, + // IN PPARTENTRY PartEntry, + IN PCHAR FileBuffer, + IN ULONG FileLength) { NTSTATUS Status; PINICACHE IniCache; - PINICACHESECTION IniSection; - PINICACHESECTION OsIniSection; - WCHAR SectionName[80]; - WCHAR OsName[80]; - WCHAR SystemPath[200]; - WCHAR SectionName2[200]; - PWCHAR KeyData; - ULONG i,j; + PINICACHEITERATOR Iterator; + PINICACHESECTION IniSection, OsIniSection; + PWCHAR SectionName, KeyData; + WCHAR InstallName[MAX_PATH]; /* Open an *existing* FreeLdr.ini configuration file */ - Status = IniCacheLoad(&IniCache, IniPath, FALSE); + Status = IniCacheLoadFromMemory(&IniCache, FileBuffer, FileLength, FALSE); if (!NT_SUCCESS(Status)) return Status; - /* Get "Operating Systems" section */ + /* Get the "Operating Systems" section */ IniSection = IniCacheGetSection(IniCache, L"Operating Systems"); if (IniSection == NULL) { @@ -123,69 +136,335 @@ EnumerateInstallationsFreeLdr() return STATUS_UNSUCCESSFUL; } - /* NOTE that we enumerate all the valid installations, not just the default one */ - while (TRUE) + /* Enumerate all the valid installations */ + Iterator = IniCacheFindFirstValue(IniSection, &SectionName, &KeyData); + if (!Iterator) goto Quit; + do { - Status = IniCacheGetKey(IniSection, SectionName, &KeyData); - if (!NT_SUCCESS(Status)) - break; + // FIXME: Poor-man quotes removal (improvement over bootsup.c:UpdateFreeLoaderIni). + if (KeyData[0] == L'"') + { + /* Quoted name, copy up to the closing quote */ + PWCHAR Begin = &KeyData[1]; + PWCHAR End = wcschr(Begin, L'"'); + if (!End) + End = Begin + wcslen(Begin); + StringCchCopyNW(InstallName, ARRAYSIZE(InstallName), + Begin, End - Begin); + } + else + { + /* Non-quoted name, copy everything */ + StringCchCopyW(InstallName, ARRAYSIZE(InstallName), KeyData); + } - // TODO some foobaring... - - // TODO 2 : Remind the entry name so that we may display it as available installation... + DPRINT1("Possible installation '%S' in OS section '%S'\n", InstallName, SectionName); /* Search for an existing ReactOS entry */ - OsIniSection = IniCacheGetSection(IniCache, SectionName2); - if (OsIniSection != NULL) - { - BOOLEAN UseExistingEntry = TRUE; + OsIniSection = IniCacheGetSection(IniCache, SectionName); + if (!OsIniSection) + continue; - /* Check for supported boot type "Windows2003" */ - Status = IniCacheGetKey(OsIniSection, L"BootType", &KeyData); - if (NT_SUCCESS(Status)) + /* Check for supported boot type "Windows2003" */ + Status = IniCacheGetKey(OsIniSection, L"BootType", &KeyData); + if (NT_SUCCESS(Status)) + { + // TODO: What to do with "Windows" ; "WindowsNT40" ; "ReactOSSetup" ? + if ((KeyData == NULL) || + ( (_wcsicmp(KeyData, L"Windows2003") != 0) && + (_wcsicmp(KeyData, L"\"Windows2003\"") != 0) )) { - if ((KeyData == NULL) || - ( (_wcsicmp(KeyData, L"Windows2003") != 0) && - (_wcsicmp(KeyData, L"\"Windows2003\"") != 0) )) - { - /* This is not a ReactOS entry */ - UseExistingEntry = FALSE; - } + /* This is not a ReactOS entry */ + continue; + } + } + else + { + /* Certainly not a ReactOS installation */ + continue; + } + + /* BootType is Windows2003. Now check SystemPath. */ + Status = IniCacheGetKey(OsIniSection, L"SystemPath", &KeyData); + if (!NT_SUCCESS(Status)) + { + DPRINT1(" A Win2k3 install '%S' without an ARC path?!\n", InstallName); + continue; + } + + { + HANDLE SystemRootDirectory; + OBJECT_ATTRIBUTES ObjectAttributes; + IO_STATUS_BLOCK IoStatusBlock; + UNICODE_STRING SystemRootPath; + WCHAR SystemRoot[MAX_PATH]; + WCHAR InstallNameW[MAX_PATH]; + + DPRINT1(" Found a candidate Win2k3 install '%S' with ARC path '%S'\n", InstallName, KeyData); + + // Note that in ARC path, the disk number is the BIOS disk number, so a conversion + // should be done. + + // TODO 1: Normalize the ARC path. + + // TODO 2: Check whether we already have an installation with this ARC path. + // If that's the case, stop there. If not, continue... + + /* + * Convert the ARC path into an NT path, from which we will deduce + * the real disk drive & partition on which the candidate installation + * resides, as well verifying whether it is indeed an NTOS installation. + */ + RtlInitEmptyUnicodeString(&SystemRootPath, SystemRoot, sizeof(SystemRoot)); + if (!ArcPathToNtPath(&SystemRootPath, KeyData, PartList)) + { + DPRINT1("ArcPathToNtPath(%S) failed, installation skipped.\n", KeyData); + // FIXME: Do not continue! + continue; + } + + DPRINT1("ArcPathToNtPath() succeeded: %S --> %wZ\n", KeyData, &SystemRootPath); + + // TODO 3: Check whether we already have an installation with this NT path. + // If that's the case, stop there. If not, continue... + + /* Set SystemRootPath */ + DPRINT1("FreeLdrEnumerateInstallations: SystemRootPath: %wZ\n", &SystemRootPath); + + /* Open SystemRootPath */ + InitializeObjectAttributes(&ObjectAttributes, + &SystemRootPath, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + Status = NtOpenFile(&SystemRootDirectory, + FILE_LIST_DIRECTORY | SYNCHRONIZE, + &ObjectAttributes, + &IoStatusBlock, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to open SystemRoot %wZ, Status 0x%08lx\n", &SystemRootPath, Status); + continue; + } + + if (IsValidNTOSInstallation(SystemRootDirectory, NULL)) + { + ULONG DiskNumber, PartitionNumber; + PCWSTR PathComponent; + PDISKENTRY DiskEntry = NULL; + PPARTENTRY PartEntry = NULL; + + DPRINT1("Found a valid NTOS installation in SystemRoot ARC path %S, NT path %wZ\n", KeyData, &SystemRootPath); + + /* From the NT path, compute the disk, partition and path components */ + if (NtPathToDiskPartComponents(SystemRootPath.Buffer, &DiskNumber, &PartitionNumber, &PathComponent)) + { + DPRINT1("SystemRootPath = '%wZ' points to disk #%d, partition #%d, path '%S'\n", + &SystemRootPath, DiskNumber, PartitionNumber, PathComponent); + + /* Retrieve the corresponding disk and partition */ + if (!GetDiskOrPartition(PartList, DiskNumber, PartitionNumber, &DiskEntry, &PartEntry)) + DPRINT1("GetDiskOrPartition(disk #%d, partition #%d) failed\n", DiskNumber, PartitionNumber); } else { - UseExistingEntry = FALSE; + DPRINT1("NtPathToDiskPartComponents(%wZ) failed\n", &SystemRootPath); } - if (UseExistingEntry) + if (PartEntry && PartEntry->DriveLetter) { - /* BootType is Windows2003. Now check SystemPath. */ - Status = IniCacheGetKey(OsIniSection, L"SystemPath", &KeyData); - if (NT_SUCCESS(Status)) - { - swprintf(SystemPath, L"\"%s\"", ArcPath); - if ((KeyData == NULL) || - ( (_wcsicmp(KeyData, ArcPath) != 0) && - (_wcsicmp(KeyData, SystemPath) != 0) )) - { - /* This entry is a ReactOS entry, but the SystemRoot - does not match the one we are looking for. */ - UseExistingEntry = FALSE; - } - } - else - { - UseExistingEntry = FALSE; - } + /* We have retrieved a partition that is mounted */ + StringCchPrintfW(InstallNameW, ARRAYSIZE(InstallNameW), L"%C:%s \"%s\"", + PartEntry->DriveLetter, PathComponent, InstallName); } + else + { + /* We failed somewhere, just show the NT path */ + StringCchPrintfW(InstallNameW, ARRAYSIZE(InstallNameW), L"%wZ \"%s\"", + &SystemRootPath, InstallName); + } + AddNTOSInstallation(List, 0, 0 /*DiskNumber, PartitionNumber*/, KeyData, InstallNameW); + } + + NtClose(SystemRootDirectory); } } + while (IniCacheFindNextValue(Iterator, &SectionName, &KeyData)); + IniCacheFindClose(Iterator); + +Quit: IniCacheDestroy(IniCache); + return STATUS_SUCCESS; } -#endif +static NTSTATUS +NtLdrEnumerateInstallations( + IN OUT PGENERIC_LIST List, + IN PPARTLIST PartList, + // IN PPARTENTRY PartEntry, + IN PCHAR FileBuffer, + IN ULONG FileLength) +{ + NTSTATUS Status; + PINICACHE IniCache; + PINICACHEITERATOR Iterator; + PINICACHESECTION IniSection; + PWCHAR SectionName, KeyData; + WCHAR InstallName[MAX_PATH]; + /* Open an *existing* FreeLdr.ini configuration file */ + Status = IniCacheLoadFromMemory(&IniCache, FileBuffer, FileLength, FALSE); + if (!NT_SUCCESS(Status)) + return Status; + + /* Get the "Operating Systems" section */ + IniSection = IniCacheGetSection(IniCache, L"operating systems"); + if (IniSection == NULL) + { + IniCacheDestroy(IniCache); + return STATUS_UNSUCCESSFUL; + } + + /* Enumerate all the valid installations */ + Iterator = IniCacheFindFirstValue(IniSection, &SectionName, &KeyData); + if (!Iterator) goto Quit; + do + { + // FIXME: Poor-man quotes removal (improvement over bootsup.c:UpdateFreeLoaderIni). + if (KeyData[0] == L'"') + { + /* Quoted name, copy up to the closing quote */ + PWCHAR Begin = &KeyData[1]; + PWCHAR End = wcschr(Begin, L'"'); + if (!End) + End = Begin + wcslen(Begin); + StringCchCopyNW(InstallName, ARRAYSIZE(InstallName), + Begin, End - Begin); + } + else + { + /* Non-quoted name, copy everything */ + StringCchCopyW(InstallName, ARRAYSIZE(InstallName), KeyData); + } + + DPRINT1("Possible installation '%S' with ARC path '%S'\n", InstallName, SectionName); + + // FIXME TODO: Determine whether we indeed have an ARC path, in which case + // this is an NT installation, or, whether we have something else like a DOS + // path, which means that we are booting a boot sector... + + DPRINT1(" Found a Win2k3 install '%S' with ARC path '%S'\n", InstallName, SectionName); + // TODO: Dissect it in order to retrieve the real disk drive & partition numbers. + // Note that in ARC path, the disk number is the BIOS disk number, so a conversion + // should be done. + { + HANDLE SystemRootDirectory; + OBJECT_ATTRIBUTES ObjectAttributes; + IO_STATUS_BLOCK IoStatusBlock; + UNICODE_STRING SystemRootPath; + WCHAR SystemRoot[MAX_PATH]; + WCHAR InstallNameW[MAX_PATH]; + + // Note that in ARC path, the disk number is the BIOS disk number, so a conversion + // should be done. + + // TODO 1: Normalize the ARC path. + + // TODO 2: Check whether we already have an installation with this ARC path. + // If that's the case, stop there. If not, continue... + + /* + * Convert the ARC path into an NT path, from which we will deduce + * the real disk drive & partition on which the candidate installation + * resides, as well verifying whether it is indeed an NTOS installation. + */ + RtlInitEmptyUnicodeString(&SystemRootPath, SystemRoot, sizeof(SystemRoot)); + if (!ArcPathToNtPath(&SystemRootPath, SectionName, PartList)) + { + DPRINT1("ArcPathToNtPath(%S) failed, installation skipped.\n", SectionName); + // FIXME: Do not continue! + continue; + } + + DPRINT1("ArcPathToNtPath() succeeded: %S --> %wZ\n", SectionName, &SystemRootPath); + + // TODO 3: Check whether we already have an installation with this NT path. + // If that's the case, stop there. If not, continue... + + /* Set SystemRootPath */ + DPRINT1("NtLdrEnumerateInstallations: SystemRootPath: %wZ\n", &SystemRootPath); + + /* Open SystemRootPath */ + InitializeObjectAttributes(&ObjectAttributes, + &SystemRootPath, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + Status = NtOpenFile(&SystemRootDirectory, + FILE_LIST_DIRECTORY | SYNCHRONIZE, + &ObjectAttributes, + &IoStatusBlock, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to open SystemRoot %wZ, Status 0x%08lx\n", &SystemRootPath, Status); + continue; + } + + if (IsValidNTOSInstallation(SystemRootDirectory, NULL)) + { + ULONG DiskNumber, PartitionNumber; + PCWSTR PathComponent; + PDISKENTRY DiskEntry = NULL; + PPARTENTRY PartEntry = NULL; + + DPRINT1("Found a valid NTOS installation in SystemRoot ARC path %S, NT path %wZ\n", SectionName, &SystemRootPath); + + /* From the NT path, compute the disk, partition and path components */ + if (NtPathToDiskPartComponents(SystemRootPath.Buffer, &DiskNumber, &PartitionNumber, &PathComponent)) + { + DPRINT1("SystemRootPath = '%wZ' points to disk #%d, partition #%d, path '%S'\n", + &SystemRootPath, DiskNumber, PartitionNumber, PathComponent); + + /* Retrieve the corresponding disk and partition */ + if (!GetDiskOrPartition(PartList, DiskNumber, PartitionNumber, &DiskEntry, &PartEntry)) + DPRINT1("GetDiskOrPartition(disk #%d, partition #%d) failed\n", DiskNumber, PartitionNumber); + } + else + { + DPRINT1("NtPathToDiskPartComponents(%wZ) failed\n", &SystemRootPath); + } + + if (PartEntry && PartEntry->DriveLetter) + { + /* We have retrieved a partition that is mounted */ + StringCchPrintfW(InstallNameW, ARRAYSIZE(InstallNameW), L"%C:%s \"%s\"", + PartEntry->DriveLetter, PathComponent, InstallName); + } + else + { + /* We failed somewhere, just show the NT path */ + StringCchPrintfW(InstallNameW, ARRAYSIZE(InstallNameW), L"%wZ \"%s\"", + &SystemRootPath, InstallName); + } + AddNTOSInstallation(List, 0, 0 /*DiskNumber, PartitionNumber*/, SectionName, InstallNameW); + } + + NtClose(SystemRootDirectory); + } + } + while (IniCacheFindNextValue(Iterator, &SectionName, &KeyData)); + + IniCacheFindClose(Iterator); + +Quit: + IniCacheDestroy(IniCache); + return STATUS_SUCCESS; +} /* * FindSubStrI(PCWSTR str, PCWSTR strSearch) : @@ -221,8 +500,8 @@ static BOOLEAN CheckForValidPEAndVendor( IN HANDLE RootDirectory OPTIONAL, IN PCWSTR PathName OPTIONAL, - IN PCWSTR FileName, // OPTIONAL - IN PCWSTR VendorName // Better would be OUT PCWSTR*, and the function returning NTSTATUS ? + IN PCWSTR FileName, // OPTIONAL + OUT PUNICODE_STRING VendorName ) { BOOLEAN Success = FALSE; @@ -234,18 +513,24 @@ CheckForValidPEAndVendor( PVOID pvData = NULL; UINT BufLen = 0; + if (VendorName->MaximumLength < sizeof(UNICODE_NULL)) + return FALSE; + + *VendorName->Buffer = UNICODE_NULL; + VendorName->Length = 0; + Status = OpenAndMapFile(RootDirectory, PathName, FileName, - &FileHandle, &SectionHandle, &ViewBase); + &FileHandle, &SectionHandle, &ViewBase, NULL); if (!NT_SUCCESS(Status)) { - DPRINT1("Failed to open and map file %wZ, Status 0x%08lx\n", &FileName, Status); + DPRINT1("Failed to open and map file %S, Status 0x%08lx\n", FileName, Status); return FALSE; // Status; } /* Make sure it's a valid PE file */ if (!RtlImageNtHeader(ViewBase)) { - DPRINT1("File %wZ does not seem to be a valid PE, bail out\n", &FileName); + DPRINT1("File %S does not seem to be a valid PE, bail out\n", FileName); Status = STATUS_INVALID_IMAGE_FORMAT; goto UnmapFile; } @@ -257,7 +542,7 @@ CheckForValidPEAndVendor( Status = NtGetVersionResource((PVOID)((ULONG_PTR)ViewBase | 1), &VersionBuffer, NULL); if (!NT_SUCCESS(Status)) { - DPRINT1("Failed to get version resource for file %wZ, Status 0x%08lx\n", &FileName, Status); + DPRINT1("Failed to get version resource for file %S, Status 0x%08lx\n", FileName, Status); goto UnmapFile; } @@ -266,7 +551,6 @@ CheckForValidPEAndVendor( { USHORT wCodePage = 0, wLangID = 0; WCHAR FileInfo[MAX_PATH]; - UNICODE_STRING Vendor; wCodePage = LOWORD(*(ULONG*)pvData); wLangID = HIWORD(*(ULONG*)pvData); @@ -276,22 +560,27 @@ CheckForValidPEAndVendor( wCodePage, wLangID); Status = NtVerQueryValue(VersionBuffer, FileInfo, &pvData, &BufLen); - if (NT_SUCCESS(Status) && pvData) + + /* Fixup the Status in case pvData is NULL */ + if (NT_SUCCESS(Status) && !pvData) + Status = STATUS_NOT_FOUND; + + if (NT_SUCCESS(Status) /*&& pvData*/) { /* BufLen includes the NULL terminator count */ - RtlInitEmptyUnicodeString(&Vendor, pvData, BufLen * sizeof(WCHAR)); - Vendor.Length = Vendor.MaximumLength - sizeof(UNICODE_NULL); + DPRINT1("Found version vendor: \"%S\" for file %S\n", pvData, FileName); - DPRINT1("Found version vendor: \"%wZ\" for file %wZ\n", &Vendor, &FileName); + StringCbCopyNW(VendorName->Buffer, VendorName->MaximumLength, + pvData, BufLen * sizeof(WCHAR)); + VendorName->Length = wcslen(VendorName->Buffer) * sizeof(WCHAR); - Success = !!FindSubStrI(pvData, VendorName); - } - else - { - DPRINT1("No version vendor found for file %wZ\n", &FileName); + Success = TRUE; } } + if (!NT_SUCCESS(Status)) + DPRINT1("No version vendor found for file %S\n", FileName); + UnmapFile: /* Finally, unmap and close the file */ UnMapFile(SectionHandle, ViewBase); @@ -300,36 +589,55 @@ UnmapFile: return Success; } +// +// TODO: Instead of returning TRUE/FALSE, it would be nice to return +// a flag indicating: +// - whether the installation is actually valid; +// - if it's broken or not (aka. needs for repair, or just upgrading). +// static BOOLEAN IsValidNTOSInstallation( - IN HANDLE PartitionHandle, - IN PCWSTR SystemRoot) + IN HANDLE SystemRootDirectory OPTIONAL, + IN PCWSTR SystemRoot OPTIONAL) { BOOLEAN Success = FALSE; USHORT i; WCHAR PathBuffer[MAX_PATH]; + UNICODE_STRING VendorName; - // DoesPathExist(PartitionHandle, SystemRoot, L"System32\\"); etc... + /* + * Use either the 'SystemRootDirectory' handle or the 'SystemRoot' string, + * depending on what the user gave to us in entry. + */ + if (SystemRootDirectory) + SystemRoot = NULL; + // else SystemRootDirectory == NULL and SystemRoot is what it is. + + /* If both the parameters are NULL we cannot do anything else more */ + if (!SystemRootDirectory && !SystemRoot) + return FALSE; + + // DoesPathExist(SystemRootDirectory, SystemRoot, L"System32\\"); etc... /* Check for the existence of \SystemRoot\System32 */ - StringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer), L"%s%s", SystemRoot, L"System32\\"); - if (!DoesPathExist(PartitionHandle, PathBuffer)) + StringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer), L"%s%s", SystemRoot ? SystemRoot : L"", L"System32\\"); + if (!DoesPathExist(SystemRootDirectory, PathBuffer)) { // DPRINT1("Failed to open directory %wZ, Status 0x%08lx\n", &FileName, Status); return FALSE; } /* Check for the existence of \SystemRoot\System32\drivers */ - StringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer), L"%s%s", SystemRoot, L"System32\\drivers\\"); - if (!DoesPathExist(PartitionHandle, PathBuffer)) + StringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer), L"%s%s", SystemRoot ? SystemRoot : L"", L"System32\\drivers\\"); + if (!DoesPathExist(SystemRootDirectory, PathBuffer)) { // DPRINT1("Failed to open directory %wZ, Status 0x%08lx\n", &FileName, Status); return FALSE; } /* Check for the existence of \SystemRoot\System32\config */ - StringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer), L"%s%s", SystemRoot, L"System32\\config\\"); - if (!DoesPathExist(PartitionHandle, PathBuffer)) + StringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer), L"%s%s", SystemRoot ? SystemRoot : L"", L"System32\\config\\"); + if (!DoesPathExist(SystemRootDirectory, PathBuffer)) { // DPRINT1("Failed to open directory %wZ, Status 0x%08lx\n", &FileName, Status); return FALSE; @@ -340,38 +648,64 @@ IsValidNTOSInstallation( * Check for the existence of SYSTEM and SOFTWARE hives in \SystemRoot\System32\config * (but we don't check here whether they are actually valid). */ - if (!DoesFileExist(PartitionHandle, SystemRoot, L"System32\\config\\SYSTEM")) + if (!DoesFileExist(SystemRootDirectory, SystemRoot, L"System32\\config\\SYSTEM")) { // DPRINT1("Failed to open file %wZ, Status 0x%08lx\n", &FileName, Status); return FALSE; } - if (!DoesFileExist(PartitionHandle, SystemRoot, L"System32\\config\\SOFTWARE")) + if (!DoesFileExist(SystemRootDirectory, SystemRoot, L"System32\\config\\SOFTWARE")) { // DPRINT1("Failed to open file %wZ, Status 0x%08lx\n", &FileName, Status); return FALSE; } #endif - for (i = 0; i < ARRAYSIZE(KnownVendors); ++i) + RtlInitEmptyUnicodeString(&VendorName, PathBuffer, sizeof(PathBuffer)); + + /* Check for the existence of \SystemRoot\System32\ntoskrnl.exe and retrieves its vendor name */ + Success = CheckForValidPEAndVendor(SystemRootDirectory, SystemRoot, L"System32\\ntoskrnl.exe", &VendorName); + if (!Success) + DPRINT1("Kernel file ntoskrnl.exe is either not a PE file, or does not have any vendor?\n"); + + /* The kernel gives the OS its flavour */ + if (Success) { - /* Check for the existence of \SystemRoot\System32\ntoskrnl.exe and verify its version */ - Success = CheckForValidPEAndVendor(PartitionHandle, SystemRoot, L"System32\\ntoskrnl.exe", KnownVendors[i]); + for (i = 0; i < ARRAYSIZE(KnownVendors); ++i) + { + Success = !!FindSubStrI(VendorName.Buffer, KnownVendors[i]); + if (Success) + { + /* We have found a correct vendor combination */ + DPRINT1("IsValidNTOSInstallation: We've got an NTOS installation from %S !\n", KnownVendors[i]); + break; + } + } + } - /* OPTIONAL: Check for the existence of \SystemRoot\System32\ntkrnlpa.exe */ + /* OPTIONAL: Check for the existence of \SystemRoot\System32\ntkrnlpa.exe */ - /* Check for the existence of \SystemRoot\System32\ntdll.dll */ - Success = CheckForValidPEAndVendor(PartitionHandle, SystemRoot, L"System32\\ntdll.dll", KnownVendors[i]); - - /* We have found a correct vendor combination */ - if (Success) - break; + /* Check for the existence of \SystemRoot\System32\ntdll.dll and retrieves its vendor name */ + Success = CheckForValidPEAndVendor(SystemRootDirectory, SystemRoot, L"System32\\ntdll.dll", &VendorName); + if (!Success) + DPRINT1("User-mode file ntdll.dll is either not a PE file, or does not have any vendor?\n"); + if (Success) + { + for (i = 0; i < ARRAYSIZE(KnownVendors); ++i) + { + if (!!FindSubStrI(VendorName.Buffer, KnownVendors[i])) + { + /* We have found a correct vendor combination */ + DPRINT1("IsValidNTOSInstallation: The user-mode file ntdll.dll is from %S\n", KnownVendors[i]); + break; + } + } } return Success; } static VOID -ListNTOSInstalls( +DumpNTOSInstalls( IN PGENERIC_LIST List) { PGENERIC_LIST_ENTRY Entry; @@ -442,6 +776,10 @@ AddNTOSInstallation( { DPRINT1("An NTOS installation with name \"%S\" already exists on disk #%d, partition #%d, in SystemRoot %S\n", NtOsInstall->InstallationName, NtOsInstall->DiskNumber, NtOsInstall->PartitionNumber, NtOsInstall->SystemRoot); + // + // NOTE: We may use its "IsDefault" attribute, and only keep the entries that have IsDefault == TRUE... + // Setting IsDefault to TRUE would imply searching for the "Default" entry in the loader configuration file. + // return NtOsInstall; } @@ -464,9 +802,9 @@ AddNTOSInstallation( static VOID FindNTOSInstallations( - IN PGENERIC_LIST List, - IN ULONG DiskNumber, - IN ULONG PartitionNumber) + IN OUT PGENERIC_LIST List, + IN PPARTLIST PartList, + IN PPARTENTRY PartEntry) { NTSTATUS Status; UINT i; @@ -476,15 +814,18 @@ FindNTOSInstallations( UNICODE_STRING PartitionRootPath; HANDLE SectionHandle; // SIZE_T ViewSize; + ULONG FileSize; PVOID ViewBase; WCHAR PathBuffer[MAX_PATH]; - WCHAR SystemRoot[MAX_PATH]; - WCHAR InstallNameW[MAX_PATH]; + +PDISKENTRY DiskEntry = PartEntry->DiskEntry; +ULONG DiskNumber = DiskEntry->DiskNumber; +ULONG PartitionNumber = PartEntry->PartitionNumber; /* Set PartitionRootPath */ - swprintf(PathBuffer, - L"\\Device\\Harddisk%lu\\Partition%lu\\", - DiskNumber, PartitionNumber); + StringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer), + L"\\Device\\Harddisk%lu\\Partition%lu\\", + DiskNumber, PartitionNumber); RtlInitUnicodeString(&PartitionRootPath, PathBuffer); DPRINT1("FindNTOSInstallations: PartitionRootPath: %wZ\n", &PartitionRootPath); @@ -519,7 +860,7 @@ FindNTOSInstallations( /* Check whether the loader configuration file exists */ Status = OpenAndMapFile(PartitionHandle, NULL, NtosBootLoaders[i].LoaderConfigurationFile, - &FileHandle, &SectionHandle, &ViewBase); + &FileHandle, &SectionHandle, &ViewBase, &FileSize); if (!NT_SUCCESS(Status)) { /* The loader does not exist, continue with another one */ @@ -529,31 +870,21 @@ FindNTOSInstallations( } /* The loader configuration file exists, interpret it to find valid installations */ - // TODO!! - DPRINT1("TODO: Analyse the OS installations inside %S !\n", NtosBootLoaders[i].LoaderConfigurationFile); - - // Here we get a SystemRootPath for each installation // FIXME! - // FIXME: Do NOT hardcode the path!! But retrieve it from boot.ini etc... - StringCchCopyW(SystemRoot, ARRAYSIZE(SystemRoot), L"WINDOWS\\"); - if (IsValidNTOSInstallation(PartitionHandle, SystemRoot)) + DPRINT1("Analyse the OS installations inside '%S' in disk #%d, partition #%d\n", + NtosBootLoaders[i].LoaderConfigurationFile, DiskNumber, PartitionNumber); + switch (NtosBootLoaders[i].Type) { - DPRINT1("Found a valid NTOS installation in disk #%d, partition #%d, SystemRoot %S\n", - DiskNumber, PartitionNumber, SystemRoot); - StringCchPrintfW(InstallNameW, ARRAYSIZE(InstallNameW), L"%C: \\Device\\Harddisk%lu\\Partition%lu\\%s \"%s\"", - 'X' /* FIXME: Partition letter */, DiskNumber, PartitionNumber, SystemRoot, L"Windows (placeholder)"); - AddNTOSInstallation(List, DiskNumber, PartitionNumber, SystemRoot, InstallNameW); - } + case FreeLdr: + Status = FreeLdrEnumerateInstallations(List, PartList, ViewBase, FileSize); + break; - // Here we get a SystemRootPath for each installation // FIXME! - // FIXME: Do NOT hardcode the path!! But retrieve it from boot.ini etc... - StringCchCopyW(SystemRoot, ARRAYSIZE(SystemRoot), L"ReactOS\\"); - if (IsValidNTOSInstallation(PartitionHandle, SystemRoot)) - { - DPRINT1("Found a valid NTOS installation in disk #%d, partition #%d, SystemRoot %S\n", - DiskNumber, PartitionNumber, SystemRoot); - StringCchPrintfW(InstallNameW, ARRAYSIZE(InstallNameW), L"%C: \\Device\\Harddisk%lu\\Partition%lu\\%s \"%s\"", - 'X' /* FIXME: Partition letter */, DiskNumber, PartitionNumber, SystemRoot, L"ReactOS (placeholder)"); - AddNTOSInstallation(List, DiskNumber, PartitionNumber, SystemRoot, InstallNameW); + case NtLdr: + Status = NtLdrEnumerateInstallations(List, PartList, ViewBase, FileSize); + break; + + default: + DPRINT1("Loader type %d is currently unsupported!\n", NtosBootLoaders[i].Type); + Status = STATUS_SUCCESS; } /* Finally, unmap and close the file */ @@ -611,6 +942,8 @@ CreateNTOSInstallationsList( PartEntry = CONTAINING_RECORD(Entry2, PARTENTRY, ListEntry); Entry2 = Entry2->Flink; + ASSERT(PartEntry->DiskEntry == DiskEntry); + 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", PartEntry->PartitionNumber, PartEntry->PartitionIndex, PartEntry->PartitionType, PartEntry->LogicalPartition ? "TRUE" : "FALSE", @@ -621,7 +954,7 @@ CreateNTOSInstallationsList( ShouldICheckThisPartition(PartEntry) ? "YES!" : "NO!"); if (ShouldICheckThisPartition(PartEntry)) - FindNTOSInstallations(List, DiskEntry->DiskNumber, PartEntry->PartitionNumber); + FindNTOSInstallations(List, PartList, PartEntry); } /* Then, the logical partitions (present in the extended partition) */ @@ -631,6 +964,8 @@ CreateNTOSInstallationsList( PartEntry = CONTAINING_RECORD(Entry2, PARTENTRY, ListEntry); Entry2 = Entry2->Flink; + ASSERT(PartEntry->DiskEntry == DiskEntry); + 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", PartEntry->PartitionNumber, PartEntry->PartitionIndex, PartEntry->PartitionType, PartEntry->LogicalPartition ? "TRUE" : "FALSE", @@ -641,12 +976,12 @@ CreateNTOSInstallationsList( ShouldICheckThisPartition(PartEntry) ? "YES!" : "NO!"); if (ShouldICheckThisPartition(PartEntry)) - FindNTOSInstallations(List, DiskEntry->DiskNumber, PartEntry->PartitionNumber); + FindNTOSInstallations(List, PartList, PartEntry); } } /**** Debugging: List all the collected installations ****/ - ListNTOSInstalls(List); + DumpNTOSInstalls(List); return List; } diff --git a/base/setup/usetup/osdetect.h b/base/setup/usetup/osdetect.h index 117681e47a1..bef666a1729 100644 --- a/base/setup/usetup/osdetect.h +++ b/base/setup/usetup/osdetect.h @@ -11,8 +11,12 @@ typedef struct _NTOS_INSTALLATION LIST_ENTRY ListEntry; ULONG DiskNumber; ULONG PartitionNumber; -// Vendor???? - WCHAR SystemRoot[MAX_PATH]; + PPARTENTRY PartEntry; +// BOOLEAN IsDefault; // TRUE / FALSE whether this installation is marked as "default" in its corresponding loader configuration file. +// Vendor???? (Microsoft / ReactOS) +/**/WCHAR SystemRoot[MAX_PATH];/**/ + UNICODE_STRING SystemArcPath; // Normalized ARC path + UNICODE_STRING SystemNtPath; // Corresponding NT path /**/WCHAR InstallationName[MAX_PATH];/**/ } NTOS_INSTALLATION, *PNTOS_INSTALLATION;