[SETUPLIB][USETUP] Improve disk HW numbering, removable disk support, and "super...
[reactos.git] / base / setup / lib / utils / partlist.c
index 32adeda..1930748 100644 (file)
@@ -404,7 +404,8 @@ DiskIdentifierQueryRoutine(
     UNICODE_STRING NameU;
 
     if (ValueType == REG_SZ &&
-        ValueLength == 20 * sizeof(WCHAR))
+        ValueLength == 20 * sizeof(WCHAR) &&
+        ((PWCHAR)ValueData)[8] == L'-')
     {
         NameU.Buffer = (PWCHAR)ValueData;
         NameU.Length = NameU.MaximumLength = 8 * sizeof(WCHAR);
@@ -517,6 +518,7 @@ EnumerateBiosDiskEntries(
     RTL_QUERY_REGISTRY_TABLE QueryTable[3];
     WCHAR Name[120];
     ULONG AdapterCount;
+    ULONG ControllerCount;
     ULONG DiskCount;
     NTSTATUS Status;
     PCM_INT13_DRIVE_PARAMETER Int13Drives;
@@ -540,8 +542,7 @@ EnumerateBiosDiskEntries(
         return;
     }
 
-    AdapterCount = 0;
-    while (TRUE)
+    for (AdapterCount = 0; ; ++AdapterCount)
     {
         RtlStringCchPrintfW(Name, ARRAYSIZE(Name),
                             L"%s\\%lu",
@@ -566,11 +567,11 @@ EnumerateBiosDiskEntries(
                                         NULL);
         if (NT_SUCCESS(Status))
         {
-            while (TRUE)
+            for (ControllerCount = 0; ; ++ControllerCount)
             {
                 RtlStringCchPrintfW(Name, ARRAYSIZE(Name),
-                                    L"%s\\%lu\\DiskController\\0",
-                                    ROOT_NAME, AdapterCount);
+                                    L"%s\\%lu\\DiskController\\%lu",
+                                    ROOT_NAME, AdapterCount, ControllerCount);
                 Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
                                                 Name,
                                                 &QueryTable[2],
@@ -583,8 +584,8 @@ EnumerateBiosDiskEntries(
                 }
 
                 RtlStringCchPrintfW(Name, ARRAYSIZE(Name),
-                                    L"%s\\%lu\\DiskController\\0\\DiskPeripheral",
-                                    ROOT_NAME, AdapterCount);
+                                    L"%s\\%lu\\DiskController\\%lu\\DiskPeripheral",
+                                    ROOT_NAME, AdapterCount, ControllerCount);
                 Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
                                                 Name,
                                                 &QueryTable[2],
@@ -597,18 +598,18 @@ EnumerateBiosDiskEntries(
                     QueryTable[1].Name = L"Configuration Data";
                     QueryTable[1].QueryRoutine = DiskConfigurationDataQueryRoutine;
 
-                    DiskCount = 0;
-                    while (TRUE)
+                    for (DiskCount = 0; ; ++DiskCount)
                     {
                         BiosDiskEntry = (BIOSDISKENTRY*)RtlAllocateHeap(ProcessHeap, HEAP_ZERO_MEMORY, sizeof(BIOSDISKENTRY));
                         if (BiosDiskEntry == NULL)
                         {
-                            break;
+                            RtlFreeHeap(ProcessHeap, 0, Int13Drives);
+                            return;
                         }
 
                         RtlStringCchPrintfW(Name, ARRAYSIZE(Name),
-                                            L"%s\\%lu\\DiskController\\0\\DiskPeripheral\\%lu",
-                                            ROOT_NAME, AdapterCount, DiskCount);
+                                            L"%s\\%lu\\DiskController\\%lu\\DiskPeripheral\\%lu",
+                                            ROOT_NAME, AdapterCount, ControllerCount, DiskCount);
                         Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
                                                         Name,
                                                         QueryTable,
@@ -617,11 +618,14 @@ EnumerateBiosDiskEntries(
                         if (!NT_SUCCESS(Status))
                         {
                             RtlFreeHeap(ProcessHeap, 0, BiosDiskEntry);
-                            break;
+                            RtlFreeHeap(ProcessHeap, 0, Int13Drives);
+                            return;
                         }
 
+                        BiosDiskEntry->AdapterNumber = 0; // And NOT "AdapterCount" as it needs to be hardcoded for BIOS!
+                        BiosDiskEntry->ControllerNumber = ControllerCount;
                         BiosDiskEntry->DiskNumber = DiskCount;
-                        BiosDiskEntry->Recognized = FALSE;
+                        BiosDiskEntry->DiskEntry = NULL;
 
                         if (DiskCount < Int13Drives[0].NumberDrives)
                         {
@@ -629,11 +633,14 @@ EnumerateBiosDiskEntries(
                         }
                         else
                         {
-                            DPRINT1("Didn't find int13 drive datas for disk %u\n", DiskCount);
+                            DPRINT1("Didn't find Int13 drive data for disk %u\n", DiskCount);
                         }
 
                         InsertTailList(&PartList->BiosDiskListHead, &BiosDiskEntry->ListEntry);
 
+                        DPRINT("--->\n");
+                        DPRINT("AdapterNumber:     %lu\n", BiosDiskEntry->AdapterNumber);
+                        DPRINT("ControllerNumber:  %lu\n", BiosDiskEntry->ControllerNumber);
                         DPRINT("DiskNumber:        %lu\n", BiosDiskEntry->DiskNumber);
                         DPRINT("Signature:         %08lx\n", BiosDiskEntry->Signature);
                         DPRINT("Checksum:          %08lx\n", BiosDiskEntry->Checksum);
@@ -645,17 +652,11 @@ EnumerateBiosDiskEntries(
                         DPRINT("SectorsPerTrack:   %d\n", BiosDiskEntry->Int13DiskData.SectorsPerTrack);
                         DPRINT("MaxHeads:          %d\n", BiosDiskEntry->Int13DiskData.MaxHeads);
                         DPRINT("NumberDrives:      %d\n", BiosDiskEntry->Int13DiskData.NumberDrives);
-
-                        DiskCount++;
+                        DPRINT("<---\n");
                     }
                 }
-
-                RtlFreeHeap(ProcessHeap, 0, Int13Drives);
-                return;
             }
         }
-
-        AdapterCount++;
     }
 
     RtlFreeHeap(ProcessHeap, 0, Int13Drives);
@@ -664,6 +665,81 @@ EnumerateBiosDiskEntries(
 }
 
 
+/*
+ * Detects whether a disk reports as a "super-floppy", i.e. an unpartitioned
+ * disk with a valid VBR, following the criteria used by IoReadPartitionTable()
+ * and IoWritePartitionTable():
+ * only one single partition starting at the beginning of the disk; the reported
+ * defaults are: partition number being zero and its type being FAT16 non-bootable.
+ * Note also that accessing \Device\HarddiskN\Partition0 or Partition1 returns
+ * the same data.
+ */
+// static
+BOOLEAN
+IsSuperFloppy(
+    IN PDISKENTRY DiskEntry)
+{
+    PPARTITION_INFORMATION PartitionInfo;
+    ULONGLONG PartitionLengthEstimate;
+
+    /* No layout buffer: we cannot say anything yet */
+    if (DiskEntry->LayoutBuffer == NULL)
+        return FALSE;
+
+    /* We must have only one partition */
+    if (DiskEntry->LayoutBuffer->PartitionCount != 1)
+        return FALSE;
+
+    /* Get the single partition entry */
+    PartitionInfo = DiskEntry->LayoutBuffer->PartitionEntry;
+
+    /* The single partition must start at the beginning of the disk */
+    if (!(PartitionInfo->StartingOffset.QuadPart == 0 &&
+          PartitionInfo->HiddenSectors == 0))
+    {
+        return FALSE;
+    }
+
+    /* The disk signature is usually set to one; warn in case it's not */
+    if (DiskEntry->LayoutBuffer->Signature != 1)
+    {
+        DPRINT1("Super-Floppy disk %lu signature %08x != 1!\n",
+                DiskEntry->DiskNumber, DiskEntry->LayoutBuffer->Signature);
+    }
+
+    /*
+     * The partition number must be zero or one, be recognized,
+     * have FAT16 type and report as non-bootable.
+     */
+    if ((PartitionInfo->PartitionNumber != 0 &&
+         PartitionInfo->PartitionNumber != 1) ||
+        PartitionInfo->RecognizedPartition != TRUE ||
+        PartitionInfo->PartitionType != PARTITION_FAT_16 ||
+        PartitionInfo->BootIndicator != FALSE)
+    {
+        DPRINT1("Super-Floppy disk %lu does not return default settings!\n"
+                "    PartitionNumber = %lu, expected 0\n"
+                "    RecognizedPartition = %s, expected TRUE\n"
+                "    PartitionType = 0x%02x, expected 0x04 (PARTITION_FAT_16)\n"
+                "    BootIndicator = %s, expected FALSE\n",
+                DiskEntry->DiskNumber,
+                PartitionInfo->PartitionNumber,
+                PartitionInfo->RecognizedPartition ? "TRUE" : "FALSE",
+                PartitionInfo->PartitionType,
+                PartitionInfo->BootIndicator ? "TRUE" : "FALSE");
+    }
+
+    /* The partition lengths should agree */
+    PartitionLengthEstimate = DiskEntry->SectorCount.QuadPart * DiskEntry->BytesPerSector;
+    if (PartitionInfo->PartitionLength.QuadPart != PartitionLengthEstimate)
+    {
+        DPRINT1("PartitionLength = %I64u is different from PartitionLengthEstimate = %I64u\n",
+                PartitionInfo->PartitionLength.QuadPart, PartitionLengthEstimate);
+    }
+
+    return TRUE;
+}
+
 
 /*
  * Inserts the disk region represented by PartEntry into either the primary
@@ -1293,6 +1369,67 @@ UpdateDiskSignatures(
     }
 }
 
+static
+VOID
+UpdateHwDiskNumbers(
+    IN PPARTLIST List)
+{
+    PLIST_ENTRY ListEntry;
+    PBIOSDISKENTRY BiosDiskEntry;
+    PDISKENTRY DiskEntry;
+    ULONG HwAdapterNumber = 0;
+    ULONG HwControllerNumber = 0;
+    ULONG RemovableDiskCount = 0;
+
+    /*
+     * Enumerate the disks recognized by the BIOS and recompute the disk
+     * numbers on the system when *ALL* removable disks are not connected.
+     * The entries are inserted in increasing order of AdapterNumber,
+     * ControllerNumber and DiskNumber.
+     */
+    for (ListEntry = List->BiosDiskListHead.Flink;
+         ListEntry != &List->BiosDiskListHead;
+         ListEntry = ListEntry->Flink)
+    {
+        BiosDiskEntry = CONTAINING_RECORD(ListEntry, BIOSDISKENTRY, ListEntry);
+        DiskEntry = BiosDiskEntry->DiskEntry;
+
+        /*
+         * If the adapter or controller numbers change, update them and reset
+         * the number of removable disks on this adapter/controller.
+         */
+        if (HwAdapterNumber != BiosDiskEntry->AdapterNumber ||
+            HwControllerNumber != BiosDiskEntry->ControllerNumber)
+        {
+            HwAdapterNumber = BiosDiskEntry->AdapterNumber;
+            HwControllerNumber = BiosDiskEntry->ControllerNumber;
+            RemovableDiskCount = 0;
+        }
+
+        /* Adjust the actual hardware disk number */
+        if (DiskEntry)
+        {
+            ASSERT(DiskEntry->HwDiskNumber == BiosDiskEntry->DiskNumber);
+
+            if (DiskEntry->MediaType == RemovableMedia)
+            {
+                /* Increase the number of removable disks and set the disk number to zero */
+                ++RemovableDiskCount;
+                DiskEntry->HwFixedDiskNumber = 0;
+            }
+            else // if (DiskEntry->MediaType == FixedMedia)
+            {
+                /* Adjust the fixed disk number, offset by the number of removable disks found before this one */
+                DiskEntry->HwFixedDiskNumber = BiosDiskEntry->DiskNumber - RemovableDiskCount;
+            }
+        }
+        else
+        {
+            DPRINT1("BIOS disk %lu is not recognized by NTOS!\n", BiosDiskEntry->DiskNumber);
+        }
+    }
+}
+
 static
 VOID
 AddDiskToList(
@@ -1394,7 +1531,9 @@ AddDiskToList(
     Checksum = ~Checksum + 1;
 
     RtlStringCchPrintfW(Identifier, ARRAYSIZE(Identifier),
-                        L"%08x-%08x-A", Checksum, Signature);
+                        L"%08x-%08x-%c",
+                        Checksum, Signature,
+                        (Mbr->Magic == PARTITION_MAGIC) ? L'A' : L'X');
     DPRINT("Identifier: %S\n", Identifier);
 
     DiskEntry = RtlAllocateHeap(ProcessHeap,
@@ -1407,6 +1546,39 @@ AddDiskToList(
         return;
     }
 
+    DiskEntry->PartList = List;
+
+#if 0
+    {
+        FILE_FS_DEVICE_INFORMATION FileFsDevice;
+
+        /* Query the device for its type */
+        Status = NtQueryVolumeInformationFile(FileHandle,
+                                              &Iosb,
+                                              &FileFsDevice,
+                                              sizeof(FileFsDevice),
+                                              FileFsDeviceInformation);
+        if (!NT_SUCCESS(Status))
+        {
+            DPRINT1("Couldn't detect device type for disk %lu of identifier '%S'...\n", DiskNumber, Identifier);
+        }
+        else
+        {
+            DPRINT1("Disk %lu : DeviceType: 0x%08x ; Characteristics: 0x%08x\n", DiskNumber, FileFsDevice.DeviceType, FileFsDevice.Characteristics);
+        }
+    }
+    // NOTE: We may also use NtQueryVolumeInformationFile(FileFsDeviceInformation).
+#endif
+    DiskEntry->MediaType = DiskGeometry.MediaType;
+    if (DiskEntry->MediaType == RemovableMedia)
+    {
+        DPRINT1("Disk %lu of identifier '%S' is removable\n", DiskNumber, Identifier);
+    }
+    else // if (DiskEntry->MediaType == FixedMedia)
+    {
+        DPRINT1("Disk %lu of identifier '%S' is fixed\n", DiskNumber, Identifier);
+    }
+
 //    DiskEntry->Checksum = Checksum;
 //    DiskEntry->Signature = Signature;
     DiskEntry->BiosFound = FALSE;
@@ -1457,34 +1629,42 @@ AddDiskToList(
          */
         if (BiosDiskEntry->Signature == Signature &&
             BiosDiskEntry->Checksum == Checksum &&
-            !BiosDiskEntry->Recognized)
+            BiosDiskEntry->DiskEntry == NULL)
         {
             if (!DiskEntry->BiosFound)
             {
-                DiskEntry->BiosDiskNumber = BiosDiskEntry->DiskNumber;
+                DiskEntry->HwAdapterNumber = BiosDiskEntry->AdapterNumber;
+                DiskEntry->HwControllerNumber = BiosDiskEntry->ControllerNumber;
+                DiskEntry->HwDiskNumber = BiosDiskEntry->DiskNumber;
+
+                if (DiskEntry->MediaType == RemovableMedia)
+                {
+                    /* Set the removable disk number to zero */
+                    DiskEntry->HwFixedDiskNumber = 0;
+                }
+                else // if (DiskEntry->MediaType == FixedMedia)
+                {
+                    /* The fixed disk number will later be adjusted using the number of removable disks */
+                    DiskEntry->HwFixedDiskNumber = BiosDiskEntry->DiskNumber;
+                }
+
                 DiskEntry->BiosFound = TRUE;
-                BiosDiskEntry->Recognized = TRUE;
+                BiosDiskEntry->DiskEntry = DiskEntry;
+                break;
             }
             else
             {
                 // FIXME: What to do?
+                DPRINT1("Disk %lu of identifier '%S' has already been found?!\n", DiskNumber, Identifier);
             }
         }
     }
 
     if (!DiskEntry->BiosFound)
     {
-#if 0
-        RtlFreeHeap(ProcessHeap, 0, DiskEntry);
-        return;
-#else
-        DPRINT1("WARNING: Setup could not find a matching BIOS disk entry. Disk %d is not be bootable by the BIOS!\n", DiskNumber);
-#endif
+        DPRINT1("WARNING: Setup could not find a matching BIOS disk entry. Disk %lu may not be bootable by the BIOS!\n", DiskNumber);
     }
 
-    InitializeListHead(&DiskEntry->PrimaryPartListHead);
-    InitializeListHead(&DiskEntry->LogicalPartListHead);
-
     DiskEntry->Cylinders = DiskGeometry.Cylinders.QuadPart;
     DiskEntry->TracksPerCylinder = DiskGeometry.TracksPerCylinder;
     DiskEntry->SectorsPerTrack = DiskGeometry.SectorsPerTrack;
@@ -1528,6 +1708,9 @@ AddDiskToList(
      * See examples in https://git.reactos.org/?p=reactos.git;a=blob;f=reactos/ntoskrnl/io/iomgr/error.c;hb=2f3a93ee9cec8322a86bf74b356f1ad83fc912dc#l267
      */
 
+    InitializeListHead(&DiskEntry->PrimaryPartListHead);
+    InitializeListHead(&DiskEntry->LogicalPartListHead);
+
     InsertAscendingList(&List->DiskListHead, DiskEntry, DISKENTRY, ListEntry, DiskNumber);
 
 
@@ -1600,6 +1783,9 @@ AddDiskToList(
     DumpPartitionTable(DiskEntry);
 #endif
 
+    if (IsSuperFloppy(DiskEntry))
+        DPRINT1("Disk %lu is a super-floppy\n", DiskNumber);
+
     if (DiskEntry->LayoutBuffer->PartitionEntry[0].StartingOffset.QuadPart != 0 &&
         DiskEntry->LayoutBuffer->PartitionEntry[0].PartitionLength.QuadPart != 0 &&
         DiskEntry->LayoutBuffer->PartitionEntry[0].PartitionType != PARTITION_ENTRY_UNUSED)
@@ -1670,9 +1856,6 @@ CreatePartitionList(VOID)
     if (List == NULL)
         return NULL;
 
-    List->CurrentDisk = NULL;
-    List->CurrentPartition = NULL;
-
     List->SystemPartition = NULL;
     List->OriginalSystemPartition = NULL;
 
@@ -1724,33 +1907,9 @@ CreatePartitionList(VOID)
     }
 
     UpdateDiskSignatures(List);
-
+    UpdateHwDiskNumbers(List);
     AssignDriveLetters(List);
 
-    /* Search for first usable disk and partition */
-    if (IsListEmpty(&List->DiskListHead))
-    {
-        List->CurrentDisk = NULL;
-        List->CurrentPartition = NULL;
-    }
-    else
-    {
-        List->CurrentDisk = CONTAINING_RECORD(List->DiskListHead.Flink,
-                                              DISKENTRY,
-                                              ListEntry);
-
-        if (IsListEmpty(&List->CurrentDisk->PrimaryPartListHead))
-        {
-            List->CurrentPartition = NULL;
-        }
-        else
-        {
-            List->CurrentPartition = CONTAINING_RECORD(List->CurrentDisk->PrimaryPartListHead.Flink,
-                                                       PARTENTRY,
-                                                       ListEntry);
-        }
-    }
-
     return List;
 }
 
@@ -1814,7 +1973,7 @@ DestroyPartitionList(
 PDISKENTRY
 GetDiskByBiosNumber(
     IN PPARTLIST List,
-    IN ULONG BiosDiskNumber)
+    IN ULONG HwDiskNumber)
 {
     PDISKENTRY DiskEntry;
     PLIST_ENTRY Entry;
@@ -1826,7 +1985,7 @@ GetDiskByBiosNumber(
     {
         DiskEntry = CONTAINING_RECORD(Entry, DISKENTRY, ListEntry);
 
-        if (DiskEntry->BiosDiskNumber == BiosDiskNumber)
+        if (DiskEntry->HwDiskNumber == HwDiskNumber)
         {
             /* Disk found */
             return DiskEntry;
@@ -2006,7 +2165,7 @@ GetDiskOrPartition(
 //
 // NOTE: Was introduced broken in r6258 by Casper
 //
-BOOLEAN
+PPARTENTRY
 SelectPartition(
     IN PPARTLIST List,
     IN ULONG DiskNumber,
@@ -2017,59 +2176,55 @@ SelectPartition(
 
     DiskEntry = GetDiskByNumber(List, DiskNumber);
     if (!DiskEntry)
-        return FALSE;
+        return NULL;
 
     PartEntry = GetPartition(/*List,*/ DiskEntry, PartitionNumber);
     if (!PartEntry)
-        return FALSE;
+        return NULL;
 
     ASSERT(PartEntry->DiskEntry == DiskEntry);
     ASSERT(DiskEntry->DiskNumber == DiskNumber);
     ASSERT(PartEntry->PartitionNumber == PartitionNumber);
 
-    List->CurrentDisk = DiskEntry;
-    List->CurrentPartition = PartEntry;
-    return TRUE;
+    return PartEntry;
 }
 
 PPARTENTRY
 GetNextPartition(
-    IN PPARTLIST List)
+    IN PPARTLIST List,
+    IN PPARTENTRY CurrentPart OPTIONAL)
 {
     PLIST_ENTRY DiskListEntry;
     PLIST_ENTRY PartListEntry;
-    PDISKENTRY DiskEntry;
-    PPARTENTRY PartEntry;
+    PDISKENTRY CurrentDisk;
 
     /* Fail if no disks are available */
     if (IsListEmpty(&List->DiskListHead))
         return NULL;
 
-    /* Check for next usable entry on current disk */
-    if (List->CurrentPartition != NULL)
+    /* Check for the next usable entry on the current partition's disk */
+    if (CurrentPart != NULL)
     {
-        if (List->CurrentPartition->LogicalPartition)
+        CurrentDisk = CurrentPart->DiskEntry;
+
+        if (CurrentPart->LogicalPartition)
         {
             /* Logical partition */
 
-            PartListEntry = List->CurrentPartition->ListEntry.Flink;
-            if (PartListEntry != &List->CurrentDisk->LogicalPartListHead)
+            PartListEntry = CurrentPart->ListEntry.Flink;
+            if (PartListEntry != &CurrentDisk->LogicalPartListHead)
             {
                 /* Next logical partition */
-                PartEntry = CONTAINING_RECORD(PartListEntry, PARTENTRY, ListEntry);
-
-                List->CurrentPartition = PartEntry;
-                return List->CurrentPartition;
+                CurrentPart = CONTAINING_RECORD(PartListEntry, PARTENTRY, ListEntry);
+                return CurrentPart;
             }
             else
             {
-                PartListEntry = List->CurrentDisk->ExtendedPartition->ListEntry.Flink;
-                if (PartListEntry != &List->CurrentDisk->PrimaryPartListHead)
+                PartListEntry = CurrentDisk->ExtendedPartition->ListEntry.Flink;
+                if (PartListEntry != &CurrentDisk->PrimaryPartListHead)
                 {
-                    PartEntry = CONTAINING_RECORD(PartListEntry, PARTENTRY, ListEntry);
-
-                    List->CurrentPartition = PartEntry;
-                    return List->CurrentPartition;
+                    CurrentPart = CONTAINING_RECORD(PartListEntry, PARTENTRY, ListEntry);
+                    return CurrentPart;
                 }
             }
         }
@@ -2077,55 +2232,49 @@ GetNextPartition(
         {
             /* Primary or extended partition */
 
-            if (List->CurrentPartition->IsPartitioned &&
-                IsContainerPartition(List->CurrentPartition->PartitionType))
+            if (CurrentPart->IsPartitioned &&
+                IsContainerPartition(CurrentPart->PartitionType))
             {
                 /* First logical partition */
-                PartListEntry = List->CurrentDisk->LogicalPartListHead.Flink;
-                if (PartListEntry != &List->CurrentDisk->LogicalPartListHead)
+                PartListEntry = CurrentDisk->LogicalPartListHead.Flink;
+                if (PartListEntry != &CurrentDisk->LogicalPartListHead)
                 {
-                    PartEntry = CONTAINING_RECORD(PartListEntry, PARTENTRY, ListEntry);
-
-                    List->CurrentPartition = PartEntry;
-                    return List->CurrentPartition;
+                    CurrentPart = CONTAINING_RECORD(PartListEntry, PARTENTRY, ListEntry);
+                    return CurrentPart;
                 }
             }
             else
             {
                 /* Next primary partition */
-                PartListEntry = List->CurrentPartition->ListEntry.Flink;
-                if (PartListEntry != &List->CurrentDisk->PrimaryPartListHead)
+                PartListEntry = CurrentPart->ListEntry.Flink;
+                if (PartListEntry != &CurrentDisk->PrimaryPartListHead)
                 {
-                    PartEntry = CONTAINING_RECORD(PartListEntry, PARTENTRY, ListEntry);
-
-                    List->CurrentPartition = PartEntry;
-                    return List->CurrentPartition;
+                    CurrentPart = CONTAINING_RECORD(PartListEntry, PARTENTRY, ListEntry);
+                    return CurrentPart;
                 }
             }
         }
     }
 
     /* Search for the first partition entry on the next disk */
-    for (DiskListEntry = List->CurrentDisk->ListEntry.Flink;
+    for (DiskListEntry = (CurrentPart ? CurrentDisk->ListEntry.Flink
+                                      : List->DiskListHead.Flink);
          DiskListEntry != &List->DiskListHead;
          DiskListEntry = DiskListEntry->Flink)
     {
-        DiskEntry = CONTAINING_RECORD(DiskListEntry, DISKENTRY, ListEntry);
+        CurrentDisk = CONTAINING_RECORD(DiskListEntry, DISKENTRY, ListEntry);
 
-        if (DiskEntry->DiskStyle == PARTITION_STYLE_GPT)
+        if (CurrentDisk->DiskStyle == PARTITION_STYLE_GPT)
         {
             DPRINT("GPT-partitioned disk detected, not currently supported by SETUP!\n");
             continue;
         }
 
-        PartListEntry = DiskEntry->PrimaryPartListHead.Flink;
-        if (PartListEntry != &DiskEntry->PrimaryPartListHead)
+        PartListEntry = CurrentDisk->PrimaryPartListHead.Flink;
+        if (PartListEntry != &CurrentDisk->PrimaryPartListHead)
         {
-            PartEntry = CONTAINING_RECORD(PartListEntry, PARTENTRY, ListEntry);
-
-            List->CurrentDisk = DiskEntry;
-            List->CurrentPartition = PartEntry;
-            return List->CurrentPartition;
+            CurrentPart = CONTAINING_RECORD(PartListEntry, PARTENTRY, ListEntry);
+            return CurrentPart;
         }
     }
 
@@ -2134,96 +2283,92 @@ GetNextPartition(
 
 PPARTENTRY
 GetPrevPartition(
-    IN PPARTLIST List)
+    IN PPARTLIST List,
+    IN PPARTENTRY CurrentPart OPTIONAL)
 {
     PLIST_ENTRY DiskListEntry;
     PLIST_ENTRY PartListEntry;
-    PDISKENTRY DiskEntry;
-    PPARTENTRY PartEntry;
+    PDISKENTRY CurrentDisk;
 
     /* Fail if no disks are available */
     if (IsListEmpty(&List->DiskListHead))
         return NULL;
 
-    /* Check for previous usable entry on current disk */
-    if (List->CurrentPartition != NULL)
+    /* Check for the previous usable entry on the current partition's disk */
+    if (CurrentPart != NULL)
     {
-        if (List->CurrentPartition->LogicalPartition)
+        CurrentDisk = CurrentPart->DiskEntry;
+
+        if (CurrentPart->LogicalPartition)
         {
             /* Logical partition */
-            PartListEntry = List->CurrentPartition->ListEntry.Blink;
-            if (PartListEntry != &List->CurrentDisk->LogicalPartListHead)
+
+            PartListEntry = CurrentPart->ListEntry.Blink;
+            if (PartListEntry != &CurrentDisk->LogicalPartListHead)
             {
                 /* Previous logical partition */
-                PartEntry = CONTAINING_RECORD(PartListEntry, PARTENTRY, ListEntry);
+                CurrentPart = CONTAINING_RECORD(PartListEntry, PARTENTRY, ListEntry);
             }
             else
             {
                 /* Extended partition */
-                PartEntry = List->CurrentDisk->ExtendedPartition;
+                CurrentPart = CurrentDisk->ExtendedPartition;
             }
-
-            List->CurrentPartition = PartEntry;
-            return List->CurrentPartition;
+            return CurrentPart;
         }
         else
         {
             /* Primary or extended partition */
 
-            PartListEntry = List->CurrentPartition->ListEntry.Blink;
-            if (PartListEntry != &List->CurrentDisk->PrimaryPartListHead)
+            PartListEntry = CurrentPart->ListEntry.Blink;
+            if (PartListEntry != &CurrentDisk->PrimaryPartListHead)
             {
-                PartEntry = CONTAINING_RECORD(PartListEntry, PARTENTRY, ListEntry);
+                CurrentPart = CONTAINING_RECORD(PartListEntry, PARTENTRY, ListEntry);
 
-                if (PartEntry->IsPartitioned &&
-                    IsContainerPartition(PartEntry->PartitionType))
+                if (CurrentPart->IsPartitioned &&
+                    IsContainerPartition(CurrentPart->PartitionType))
                 {
-                    PartListEntry = List->CurrentDisk->LogicalPartListHead.Blink;
-                    PartEntry = CONTAINING_RECORD(PartListEntry, PARTENTRY, ListEntry);
+                    PartListEntry = CurrentDisk->LogicalPartListHead.Blink;
+                    CurrentPart = CONTAINING_RECORD(PartListEntry, PARTENTRY, ListEntry);
                 }
 
-                List->CurrentPartition = PartEntry;
-                return List->CurrentPartition;
+                return CurrentPart;
             }
         }
     }
 
     /* Search for the last partition entry on the previous disk */
-    for (DiskListEntry = List->CurrentDisk->ListEntry.Blink;
+    for (DiskListEntry = (CurrentPart ? CurrentDisk->ListEntry.Blink
+                                      : List->DiskListHead.Blink);
          DiskListEntry != &List->DiskListHead;
          DiskListEntry = DiskListEntry->Blink)
     {
-        DiskEntry = CONTAINING_RECORD(DiskListEntry, DISKENTRY, ListEntry);
+        CurrentDisk = CONTAINING_RECORD(DiskListEntry, DISKENTRY, ListEntry);
 
-        if (DiskEntry->DiskStyle == PARTITION_STYLE_GPT)
+        if (CurrentDisk->DiskStyle == PARTITION_STYLE_GPT)
         {
             DPRINT("GPT-partitioned disk detected, not currently supported by SETUP!\n");
             continue;
         }
 
-        PartListEntry = DiskEntry->PrimaryPartListHead.Blink;
-        if (PartListEntry != &DiskEntry->PrimaryPartListHead)
+        PartListEntry = CurrentDisk->PrimaryPartListHead.Blink;
+        if (PartListEntry != &CurrentDisk->PrimaryPartListHead)
         {
-            PartEntry = CONTAINING_RECORD(PartListEntry, PARTENTRY, ListEntry);
+            CurrentPart = CONTAINING_RECORD(PartListEntry, PARTENTRY, ListEntry);
 
-            if (PartEntry->IsPartitioned &&
-                IsContainerPartition(PartEntry->PartitionType))
+            if (CurrentPart->IsPartitioned &&
+                IsContainerPartition(CurrentPart->PartitionType))
             {
-                PartListEntry = DiskEntry->LogicalPartListHead.Blink;
-                if (PartListEntry != &DiskEntry->LogicalPartListHead)
+                PartListEntry = CurrentDisk->LogicalPartListHead.Blink;
+                if (PartListEntry != &CurrentDisk->LogicalPartListHead)
                 {
-                    PartEntry = CONTAINING_RECORD(PartListEntry, PARTENTRY, ListEntry);
-
-                    List->CurrentDisk = DiskEntry;
-                    List->CurrentPartition = PartEntry;
-                    return List->CurrentPartition;
+                    CurrentPart = CONTAINING_RECORD(PartListEntry, PARTENTRY, ListEntry);
+                    return CurrentPart;
                 }
             }
             else
             {
-                List->CurrentDisk = DiskEntry;
-                List->CurrentPartition = PartEntry;
-                return List->CurrentPartition;
+                return CurrentPart;
             }
         }
     }
@@ -2553,9 +2698,9 @@ UpdateDiskLayout(
 static
 PPARTENTRY
 GetPrevUnpartitionedEntry(
-    IN PDISKENTRY DiskEntry,
     IN PPARTENTRY PartEntry)
 {
+    PDISKENTRY DiskEntry = PartEntry->DiskEntry;
     PPARTENTRY PrevPartEntry;
     PLIST_ENTRY ListHead;
 
@@ -2575,7 +2720,7 @@ GetPrevUnpartitionedEntry(
         PrevPartEntry = CONTAINING_RECORD(PartEntry->ListEntry.Blink,
                                           PARTENTRY,
                                           ListEntry);
-        if (PrevPartEntry->IsPartitioned == FALSE)
+        if (!PrevPartEntry->IsPartitioned)
         {
             ASSERT(PrevPartEntry->PartitionType == PARTITION_ENTRY_UNUSED);
             return PrevPartEntry;
@@ -2588,9 +2733,9 @@ GetPrevUnpartitionedEntry(
 static
 PPARTENTRY
 GetNextUnpartitionedEntry(
-    IN PDISKENTRY DiskEntry,
     IN PPARTENTRY PartEntry)
 {
+    PDISKENTRY DiskEntry = PartEntry->DiskEntry;
     PPARTENTRY NextPartEntry;
     PLIST_ENTRY ListHead;
 
@@ -2610,7 +2755,7 @@ GetNextUnpartitionedEntry(
         NextPartEntry = CONTAINING_RECORD(PartEntry->ListEntry.Flink,
                                           PARTENTRY,
                                           ListEntry);
-        if (NextPartEntry->IsPartitioned == FALSE)
+        if (!NextPartEntry->IsPartitioned)
         {
             ASSERT(NextPartEntry->PartitionType == PARTITION_ENTRY_UNUSED);
             return NextPartEntry;
@@ -2892,10 +3037,11 @@ DismountVolume(
     return Status;
 }
 
-VOID
+BOOLEAN
 DeletePartition(
     IN PPARTLIST List,
-    IN PPARTENTRY PartEntry)
+    IN PPARTENTRY PartEntry,
+    OUT PPARTENTRY* FreeRegion OPTIONAL)
 {
     PDISKENTRY DiskEntry;
     PPARTENTRY PrevPartEntry;
@@ -2908,7 +3054,7 @@ DeletePartition(
         PartEntry->DiskEntry == NULL ||
         PartEntry->IsPartitioned == FALSE)
     {
-        return;
+        return FALSE;
     }
 
     ASSERT(PartEntry->PartitionType != PARTITION_ENTRY_UNUSED);
@@ -2917,6 +3063,7 @@ DeletePartition(
     if (List->SystemPartition == PartEntry)
     {
         ASSERT(List->SystemPartition);
+        ASSERT(List->SystemPartition->DiskEntry->MediaType != RemovableMedia);
 
         if (List->SystemPartition == List->OriginalSystemPartition)
             List->OriginalSystemPartition = NULL;
@@ -2949,56 +3096,49 @@ DeletePartition(
         DismountVolume(PartEntry);
     }
 
-    /* Adjust unpartitioned disk space entries */
+    /* Adjust the unpartitioned disk space entries */
 
     /* Get pointer to previous and next unpartitioned entries */
-    PrevPartEntry = GetPrevUnpartitionedEntry(DiskEntry, PartEntry);
-    NextPartEntry = GetNextUnpartitionedEntry(DiskEntry, PartEntry);
+    PrevPartEntry = GetPrevUnpartitionedEntry(PartEntry);
+    NextPartEntry = GetNextUnpartitionedEntry(PartEntry);
 
     if (PrevPartEntry != NULL && NextPartEntry != NULL)
     {
-        /* Merge previous, current and next unpartitioned entry */
+        /* Merge the previous, current and next unpartitioned entries */
 
-        /* Adjust the previous entries length */
+        /* Adjust the previous entry length */
         PrevPartEntry->SectorCount.QuadPart += (PartEntry->SectorCount.QuadPart + NextPartEntry->SectorCount.QuadPart);
 
-        /* Remove the current entry */
+        /* Remove the current and next entries */
         RemoveEntryList(&PartEntry->ListEntry);
         RtlFreeHeap(ProcessHeap, 0, PartEntry);
-
-        /* Remove the next entry */
         RemoveEntryList(&NextPartEntry->ListEntry);
         RtlFreeHeap(ProcessHeap, 0, NextPartEntry);
 
-        /* Update current partition */
-        if (List->CurrentPartition == PartEntry ||
-            List->CurrentPartition == NextPartEntry)
-        {
-            List->CurrentPartition = PrevPartEntry;
-        }
+        /* Optionally return the freed region */
+        if (FreeRegion)
+            *FreeRegion = PrevPartEntry;
     }
     else if (PrevPartEntry != NULL && NextPartEntry == NULL)
     {
-        /* Merge current and previous unpartitioned entry */
+        /* Merge the current and the previous unpartitioned entries */
 
-        /* Adjust the previous entries length */
+        /* Adjust the previous entry length */
         PrevPartEntry->SectorCount.QuadPart += PartEntry->SectorCount.QuadPart;
 
         /* Remove the current entry */
         RemoveEntryList(&PartEntry->ListEntry);
         RtlFreeHeap(ProcessHeap, 0, PartEntry);
 
-        /* Update current partition */
-        if (List->CurrentPartition == PartEntry)
-        {
-            List->CurrentPartition = PrevPartEntry;
-        }
+        /* Optionally return the freed region */
+        if (FreeRegion)
+            *FreeRegion = PrevPartEntry;
     }
     else if (PrevPartEntry == NULL && NextPartEntry != NULL)
     {
-        /* Merge current and next unpartitioned entry */
+        /* Merge the current and the next unpartitioned entries */
 
-        /* Adjust the next entries offset and length */
+        /* Adjust the next entry offset and length */
         NextPartEntry->StartSector.QuadPart = PartEntry->StartSector.QuadPart;
         NextPartEntry->SectorCount.QuadPart += PartEntry->SectorCount.QuadPart;
 
@@ -3006,15 +3146,13 @@ DeletePartition(
         RemoveEntryList(&PartEntry->ListEntry);
         RtlFreeHeap(ProcessHeap, 0, PartEntry);
 
-        /* Update current partition */
-        if (List->CurrentPartition == PartEntry)
-        {
-            List->CurrentPartition = NextPartEntry;
-        }
+        /* Optionally return the freed region */
+        if (FreeRegion)
+            *FreeRegion = NextPartEntry;
     }
     else
     {
-        /* Nothing to merge but change current entry */
+        /* Nothing to merge but change the current entry */
         PartEntry->IsPartitioned = FALSE;
         PartEntry->PartitionType = PARTITION_ENTRY_UNUSED;
         PartEntry->FormatState = Unformatted;
@@ -3023,18 +3161,17 @@ DeletePartition(
         PartEntry->OnDiskPartitionNumber = 0;
         PartEntry->PartitionNumber = 0;
         // PartEntry->PartitionIndex = 0;
+
+        /* Optionally return the freed region */
+        if (FreeRegion)
+            *FreeRegion = PartEntry;
     }
 
     UpdateDiskLayout(DiskEntry);
 
     AssignDriveLetters(List);
-}
 
-VOID
-DeleteCurrentPartition(
-    IN PPARTLIST List)
-{
-    DeletePartition(List, List->CurrentPartition);
+    return TRUE;
 }
 
 /*
@@ -3185,7 +3322,10 @@ IsSupportedActivePartition(
 
 VOID
 CheckActiveSystemPartition(
-    IN PPARTLIST List)
+    IN PPARTLIST List,
+    IN BOOLEAN ForceSelect,
+    IN PDISKENTRY AlternateDisk OPTIONAL,
+    IN PPARTENTRY AlternatePart OPTIONAL)
 {
     PLIST_ENTRY ListEntry;
     PDISKENTRY DiskEntry;
@@ -3199,7 +3339,7 @@ CheckActiveSystemPartition(
         /* No system partition! */
         List->SystemPartition = NULL;
         List->OriginalSystemPartition = NULL;
-        return;
+        goto NoSystemPartition;
     }
 
     if (List->SystemPartition != NULL)
@@ -3216,11 +3356,21 @@ CheckActiveSystemPartition(
     List->SystemPartition = NULL;
     List->OriginalSystemPartition = NULL;
 
+    /* Adjust the optional alternate disk if needed */
+    if (!AlternateDisk && AlternatePart)
+        AlternateDisk = AlternatePart->DiskEntry;
+
+    /* Ensure that the alternate partition is on the alternate disk */
+    if (AlternatePart)
+        ASSERT(AlternateDisk && (AlternatePart->DiskEntry == AlternateDisk));
+
+    /* Ensure that the alternate disk is in the list */
+    if (AlternateDisk)
+        ASSERT(AlternateDisk->PartList == List);
 
 //
-// Pass == 1 : Checking the first disk.
+// Pass == 1 : Check the first (system) disk.
 //
-    DPRINT("We are here (1)!\n");
 
     /*
      * First, check whether the first disk (the one that will be booted
@@ -3230,13 +3380,11 @@ CheckActiveSystemPartition(
     DiskEntry = CONTAINING_RECORD(List->DiskListHead.Flink,
                                   DISKENTRY, ListEntry);
 
-    // if (DiskEntry->DiskStyle == PARTITION_STYLE_GPT)
-    // {
-        // DPRINT1("GPT-partitioned disk detected, not currently supported by SETUP!\n");
-        // continue;
-    // }
-
-    DPRINT("We are here (1a)!\n");
+    if (DiskEntry->DiskStyle == PARTITION_STYLE_GPT)
+    {
+        DPRINT1("First (system) disk -- GPT-partitioned disk detected, not currently supported by SETUP!\n");
+        goto UseAlternateDisk;
+    }
 
     ActivePartition = GetActiveDiskPartition(DiskEntry);
     if (ActivePartition)
@@ -3252,14 +3400,13 @@ CheckActiveSystemPartition(
         }
     }
 
-    DPRINT("We are here (1b)!\n");
-
-    /* If this first disk is not the current installation disk, do the minimal checks */
-    if (DiskEntry != List->CurrentDisk)
+    /* If this first disk is not the optional alternate disk, perform the minimal checks */
+    if (DiskEntry != AlternateDisk)
     {
         /*
-         * We don't. Enumerate all the (primary) partitions in the first disk,
-         * excluding the current active partition, to find a candidate new one.
+         * No active partition has been recognized. Enumerate all the (primary)
+         * partitions in the first disk, excluding the possible current active
+         * partition, to find a new candidate.
          */
         for (ListEntry = DiskEntry->PrimaryPartListHead.Flink;
              ListEntry != &DiskEntry->PrimaryPartListHead;
@@ -3336,26 +3483,24 @@ CheckActiveSystemPartition(
     }
 
 
-    /**** Case where we don't have an active partition on the first disk ****/
-
 //
-// Pass == 2 : Checking the CurrentDisk on which we install.
+// Pass == 2 : No active partition found: Check the alternate disk if specified.
 //
-    DPRINT("We are here (2)!\n");
 
-    if (DiskEntry != List->CurrentDisk)
-    {
-        /* Choose the currently selected disk */
-        DiskEntry = List->CurrentDisk;
+UseAlternateDisk:
+    if (!AlternateDisk || (!ForceSelect && (DiskEntry != AlternateDisk)))
+        goto NoSystemPartition;
 
-        if (DiskEntry->DiskStyle == PARTITION_STYLE_GPT)
-        {
-            DPRINT1("Current disk?! -- GPT-partitioned disk detected, not currently supported by SETUP!\n");
-            DPRINT1("No supported active partition found on this system!\n");
-            return;
-        }
+    if (AlternateDisk->DiskStyle == PARTITION_STYLE_GPT)
+    {
+        DPRINT1("Alternate disk -- GPT-partitioned disk detected, not currently supported by SETUP!\n");
+        goto NoSystemPartition;
+    }
 
-        DPRINT("We are here (2x)!\n");
+    if (DiskEntry != AlternateDisk)
+    {
+        /* Choose the alternate disk */
+        DiskEntry = AlternateDisk;
 
         ActivePartition = GetActiveDiskPartition(DiskEntry);
         if (ActivePartition)
@@ -3369,19 +3514,16 @@ CheckActiveSystemPartition(
         }
     }
 
-
-    /**** Here, we either don't have an active partition, or we have one BUT it is not supported ****/
+    /* We now may have an unsupported active partition, or none */
 
 /***
  *** TODO: Improve the selection:
  *** - If we want a really separate system partition from the partition where
  ***   we install, do something similar to what's done below in the code.
  *** - Otherwise if we allow for the system partition to be also the partition
- ***   where we install, just directly fall down to List->CurrentPartition.
+ ***   where we install, just directly fall down to using AlternatePart.
  ***/
 
-    DPRINT("We are here (2a)!\n");
-
     /* Retrieve the first partition of the disk */
     PartEntry = CONTAINING_RECORD(DiskEntry->PrimaryPartListHead.Flink,
                                   PARTENTRY, ListEntry);
@@ -3416,8 +3558,6 @@ CheckActiveSystemPartition(
         DPRINT1("NewDisk TRUE but first partition is used?\n");
     }
 
-    DPRINT("We are here (3)!\n");
-
     /*
      * The disk is not new, check if any partition is initialized;
      * if not, the first one becomes the system partition.
@@ -3455,20 +3595,27 @@ CheckActiveSystemPartition(
         goto SetSystemPartition;
     }
 
-    DPRINT("We are here (4)!\n");
-
     /*
      * The disk is not new, we did not find any actual active partition,
      * or the one we found was not supported, or any possible other canditate
-     * is not supported. We then use the current (installation) partition.
+     * is not supported. We then use the alternate partition if specified.
      */
-    /* Nothing, use the alternative system partition */
-    DPRINT1("No system partition found, use the alternative partition!\n");
-    CandidatePartition = List->CurrentPartition;
-    goto UseAlternativeSystemPartition;
+    if (AlternatePart)
+    {
+        DPRINT1("No system partition found, use the alternative partition!\n");
+        CandidatePartition = AlternatePart;
+        goto UseAlternativeSystemPartition;
+    }
+    else
+    {
+NoSystemPartition:
+        DPRINT1("No valid or supported system partition has been found on this system!\n");
+        return;
+    }
 
 
 SystemPartitionFound:
+    ASSERT(CandidatePartition);
     List->SystemPartition = CandidatePartition;
 
     DPRINT1("Use existing active system partition %lu in disk %lu, drive letter %C\n",
@@ -3498,6 +3645,7 @@ FindAndUseAlternativeSystemPartition:
     }
 
 UseAlternativeSystemPartition:
+    ASSERT(CandidatePartition);
     List->SystemPartition = CandidatePartition;
 
     DPRINT1("Use alternative active system partition %lu in disk %lu, drive letter %C\n",
@@ -3641,7 +3789,7 @@ WritePartitions(
     //
     // NOTE: Originally (see r40437), we used to install here also a new MBR
     // for this disk (by calling InstallMbrBootCodeToDisk), only if:
-    // DiskEntry->NewDisk == TRUE and DiskEntry->BiosDiskNumber == 0.
+    // DiskEntry->NewDisk == TRUE and DiskEntry->HwDiskNumber == 0.
     // Then after that, both DiskEntry->NewDisk and DiskEntry->NoMbr were set
     // to FALSE. In the other place (in usetup.c) where InstallMbrBootCodeToDisk
     // was called too, the installation test was modified by checking whether
@@ -3860,6 +4008,10 @@ PrimaryPartitionCreationChecks(
     if (PartEntry->IsPartitioned)
         return ERROR_NEW_PARTITION;
 
+    /* Only one primary partition is allowed on super-floppy */
+    if (IsSuperFloppy(DiskEntry))
+        return ERROR_PARTITION_TABLE_FULL;
+
     /* Fail if there are already 4 primary partitions in the list */
     if (GetPrimaryPartitionCount(DiskEntry) >= 4)
         return ERROR_PARTITION_TABLE_FULL;
@@ -3883,6 +4035,10 @@ ExtendedPartitionCreationChecks(
     if (PartEntry->IsPartitioned)
         return ERROR_NEW_PARTITION;
 
+    /* Only one primary partition is allowed on super-floppy */
+    if (IsSuperFloppy(DiskEntry))
+        return ERROR_PARTITION_TABLE_FULL;
+
     /* Fail if there are already 4 primary partitions in the list */
     if (GetPrimaryPartitionCount(DiskEntry) >= 4)
         return ERROR_PARTITION_TABLE_FULL;
@@ -3910,6 +4066,10 @@ LogicalPartitionCreationChecks(
     if (PartEntry->IsPartitioned)
         return ERROR_NEW_PARTITION;
 
+    /* Only one primary partition is allowed on super-floppy */
+    if (IsSuperFloppy(DiskEntry))
+        return ERROR_PARTITION_TABLE_FULL;
+
     return ERROR_SUCCESS;
 }