[USETUP] Improve the NTOS installations detector.
authorHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Mon, 22 May 2017 01:19:08 +0000 (01:19 +0000)
committerHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Thu, 31 May 2018 16:00:59 +0000 (18:00 +0200)
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
base/setup/usetup/osdetect.h

index 23f1dee..34c18e7 100644 (file)
@@ -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);
+        // 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' in OS section '%S'\n", InstallName, SectionName);
+
+        /* Search for an existing ReactOS entry */
+        OsIniSection = IniCacheGetSection(IniCache, SectionName);
+        if (!OsIniSection)
+            continue;
+
+        /* 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) ))
+            {
+                /* 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))
-            break;
+        {
+            DPRINT1("    A Win2k3 install '%S' without an ARC path?!\n", InstallName);
+            continue;
+        }
 
-        // TODO some foobaring...
+        {
+        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;
+        }
 
-        // TODO 2 : Remind the entry name so that we may display it as available installation...
+        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;
+        }
 
-        /* Search for an existing ReactOS entry */
-        OsIniSection = IniCacheGetSection(IniCache, SectionName2);
-        if (OsIniSection != NULL)
+        if (IsValidNTOSInstallation(SystemRootDirectory, NULL))
         {
-            BOOLEAN UseExistingEntry = TRUE;
+            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);
 
-            /* Check for supported boot type "Windows2003" */
-            Status = IniCacheGetKey(OsIniSection, L"BootType", &KeyData);
-            if (NT_SUCCESS(Status))
+            /* From the NT path, compute the disk, partition and path components */
+            if (NtPathToDiskPartComponents(SystemRootPath.Buffer, &DiskNumber, &PartitionNumber, &PathComponent))
             {
-                if ((KeyData == NULL) ||
-                    ( (_wcsicmp(KeyData, L"Windows2003") != 0) &&
-                      (_wcsicmp(KeyData, L"\"Windows2003\"") != 0) ))
-                {
-                    /* This is not a ReactOS entry */
-                    UseExistingEntry = FALSE;
-                }
+                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)
-    {
-        /* Check for the existence of \SystemRoot\System32\ntoskrnl.exe and verify its version */
-        Success = CheckForValidPEAndVendor(PartitionHandle, SystemRoot, L"System32\\ntoskrnl.exe", KnownVendors[i]);
+    RtlInitEmptyUnicodeString(&VendorName, PathBuffer, sizeof(PathBuffer));
 
-        /* OPTIONAL: Check for the existence of \SystemRoot\System32\ntkrnlpa.exe */
+    /* 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");
 
-        /* Check for the existence of \SystemRoot\System32\ntdll.dll */
-        Success = CheckForValidPEAndVendor(PartitionHandle, SystemRoot, L"System32\\ntdll.dll", KnownVendors[i]);
+    /* The kernel gives the OS its flavour */
+    if (Success)
+    {
+        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;
+            }
+        }
+    }
 
-        /* We have found a correct vendor combination */
-        if (Success)
-            break;
+    /* OPTIONAL: Check for the existence of \SystemRoot\System32\ntkrnlpa.exe */
+
+    /* 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;
 }
index 117681e..bef666a 100644 (file)
@@ -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;