update for HEAD-2003091401
[reactos.git] / drivers / storage / disk / disk.c
index 682ae7c..db2bc4b 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ReactOS kernel
- *  Copyright (C) 2001, 2002 ReactOS Team
+ *  Copyright (C) 2001, 2002, 2003 ReactOS Team
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
 
 #define VERSION  "0.0.1"
 
+#define SCSI_DISK_TIMEOUT      10      /* Default timeout: 10 seconds */
+#define MODE_DATA_SIZE         192
+
 
 typedef struct _DISK_DATA
 {
+  PDEVICE_EXTENSION NextPartition;
+  ULONG Signature;
+  ULONG MbrCheckSum;
   ULONG HiddenSectors;
   ULONG PartitionNumber;
   ULONG PartitionOrdinal;
@@ -82,28 +88,47 @@ NTSTATUS STDCALL
 DiskClassShutdownFlush(IN PDEVICE_OBJECT DeviceObject,
                       IN PIRP Irp);
 
+static BOOLEAN
+ScsiDiskSearchForDisk(IN PDEVICE_EXTENSION DeviceExtension,
+                     IN HANDLE BusKey,
+                     OUT PULONG DetectedDiskNumber);
+
+static VOID
+DiskClassUpdatePartitionDeviceObjects (IN PDEVICE_OBJECT DeviceObject,
+                                      IN PIRP Irp);
+
+static VOID
+ScsiDiskUpdateFixedDiskGeometry(IN PDEVICE_EXTENSION DeviceExtension);
+
+static BOOLEAN
+ScsiDiskCalcMbrCheckSum(IN PDEVICE_EXTENSION DeviceExtension,
+                       OUT PULONG Checksum);
 
 
 /* FUNCTIONS ****************************************************************/
 
-//    DriverEntry
-//
-//  DESCRIPTION:
-//    This function initializes the driver, locates and claims 
-//    hardware resources, and creates various NT objects needed
-//    to process I/O requests.
-//
-//  RUN LEVEL:
-//    PASSIVE_LEVEL
-//
-//  ARGUMENTS:
-//    IN  PDRIVER_OBJECT   DriverObject  System allocated Driver Object
-//                                       for this driver
-//    IN  PUNICODE_STRING  RegistryPath  Name of registry driver service 
-//                                       key
-//
-//  RETURNS:
-//    NTSTATUS
+/**********************************************************************
+ * NAME                                                        EXPORTED
+ *     DriverEntry
+ *
+ * DESCRIPTION
+ *     This function initializes the driver, locates and claims 
+ *     hardware resources, and creates various NT objects needed
+ *     to process I/O requests.
+ *
+ * RUN LEVEL
+ *     PASSIVE_LEVEL
+ *
+ * ARGUMENTS
+ *     DriverObject
+ *             System allocated Driver Object for this driver
+ *
+ *     RegistryPath
+ *             Name of registry driver service key
+ *
+ * RETURN VALUE
+ *     Status
+ */
 
 NTSTATUS STDCALL
 DriverEntry(IN PDRIVER_OBJECT DriverObject,
@@ -238,6 +263,8 @@ DiskClassFindDevices(PDRIVER_OBJECT DriverObject,
        {
          InquiryData = (PINQUIRYDATA)UnitInfo->InquiryData;
 
+         DPRINT("Device type %u\n", InquiryData->DeviceType);
+
          if (((InquiryData->DeviceType == DIRECT_ACCESS_DEVICE) ||
               (InquiryData->DeviceType == OPTICAL_DEVICE)) &&
              (InquiryData->DeviceTypeQualifier == 0) &&
@@ -353,7 +380,7 @@ DiskClassCheckReadWrite(IN PDEVICE_OBJECT DeviceObject,
 
 
 /**********************************************************************
- * NAME                                                        EXPORTED
+ * NAME                                                        INTERNAL
  *     DiskClassCreateDeviceObject
  *
  * DESCRIPTION
@@ -380,7 +407,7 @@ DiskClassCheckReadWrite(IN PDEVICE_OBJECT DeviceObject,
 
 static NTSTATUS
 DiskClassCreateDeviceObject(IN PDRIVER_OBJECT DriverObject,
-                           IN PUNICODE_STRING RegistryPath, /* what's this used for? */
+                           IN PUNICODE_STRING RegistryPath,
                            IN PDEVICE_OBJECT PortDeviceObject,
                            IN ULONG PortNumber,
                            IN ULONG DiskNumber,
@@ -401,6 +428,7 @@ DiskClassCreateDeviceObject(IN PDRIVER_OBJECT DriverObject,
   PPARTITION_INFORMATION PartitionEntry;
   PDISK_DATA DiskData;
   ULONG PartitionNumber;
+  PVOID MbrBuffer;
   NTSTATUS Status;
 
   DPRINT("DiskClassCreateDeviceObject() called\n");
@@ -482,6 +510,7 @@ DiskClassCreateDeviceObject(IN PDRIVER_OBJECT DriverObject,
   DiskDeviceExtension = DiskDeviceObject->DeviceExtension;
   DiskDeviceExtension->LockCount = 0;
   DiskDeviceExtension->DeviceNumber = DiskNumber;
+  DiskDeviceExtension->DeviceObject = DiskDeviceObject;
   DiskDeviceExtension->PortDeviceObject = PortDeviceObject;
   DiskDeviceExtension->PhysicalDevice = DiskDeviceObject;
   DiskDeviceExtension->PortCapabilities = Capabilities;
@@ -491,6 +520,16 @@ DiskClassCreateDeviceObject(IN PDRIVER_OBJECT DriverObject,
   DiskDeviceExtension->TargetId = InquiryData->TargetId;
   DiskDeviceExtension->Lun = InquiryData->Lun;
 
+  /* Get timeout value */
+  DiskDeviceExtension->TimeOutValue =
+    ScsiClassQueryTimeOutRegistryValue(RegistryPath);
+  if (DiskDeviceExtension->TimeOutValue == 0)
+    DiskDeviceExtension->TimeOutValue = SCSI_DISK_TIMEOUT;
+
+  /* Initialize the lookaside list for SRBs */
+  ScsiClassInitializeSrbLookasideList(DiskDeviceExtension,
+                                     4);
+
   /* zero-out disk data */
   DiskData = (PDISK_DATA)(DiskDeviceExtension + 1);
   RtlZeroMemory(DiskData,
@@ -503,6 +542,34 @@ DiskClassCreateDeviceObject(IN PDRIVER_OBJECT DriverObject,
     {
       DPRINT("Failed to allocate geometry buffer!\n");
 
+      ExDeleteNPagedLookasideList(&DiskDeviceExtension->SrbLookasideListHead);
+
+      IoDeleteDevice(DiskDeviceObject);
+
+      /* Release (unclaim) the disk */
+      ScsiClassClaimDevice(PortDeviceObject,
+                          InquiryData,
+                          TRUE,
+                          NULL);
+
+      /* Delete the harddisk device directory */
+      ZwMakeTemporaryObject(Handle);
+      ZwClose(Handle);
+
+      return(STATUS_INSUFFICIENT_RESOURCES);
+    }
+
+  /* Allocate sense data buffer */
+  DiskDeviceExtension->SenseData = ExAllocatePool(NonPagedPool,
+                                                 sizeof(SENSE_BUFFER_SIZE));
+  if (DiskDeviceExtension->SenseData == NULL)
+    {
+      DPRINT("Failed to allocate sense data buffer!\n");
+
+      ExFreePool (DiskDeviceExtension->DiskGeometry);
+
+      ExDeleteNPagedLookasideList(&DiskDeviceExtension->SrbLookasideListHead);
+
       IoDeleteDevice(DiskDeviceObject);
 
       /* Release (unclaim) the disk */
@@ -534,6 +601,25 @@ DiskClassCreateDeviceObject(IN PDRIVER_OBJECT DriverObject,
 
   DPRINT("SectorSize: %lu\n", DiskDeviceExtension->DiskGeometry->BytesPerSector);
 
+  /* Check disk for presence of a disk manager */
+  HalExamineMBR(DiskDeviceObject,
+               DiskDeviceExtension->DiskGeometry->BytesPerSector,
+               0x54,
+               &MbrBuffer);
+  if (MbrBuffer != NULL)
+    {
+      /* Start disk at sector 63 if the Ontrack Disk Manager was found */
+      DPRINT("Found 'Ontrack Disk Manager'!\n");
+
+      DiskDeviceExtension->DMSkew = 63;
+      DiskDeviceExtension->DMByteSkew =
+       63 * DiskDeviceExtension->DiskGeometry->BytesPerSector;
+      DiskDeviceExtension->DMActive = TRUE;
+
+      ExFreePool(MbrBuffer);
+      MbrBuffer = NULL;
+    }
+
   if ((DiskDeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) &&
       (DiskDeviceExtension->DiskGeometry->MediaType == RemovableMedia))
     {
@@ -552,48 +638,76 @@ DiskClassCreateDeviceObject(IN PDRIVER_OBJECT DriverObject,
     }
   else
     {
-  /* Read partition table */
-  Status = IoReadPartitionTable(DiskDeviceObject,
-                               DiskDeviceExtension->DiskGeometry->BytesPerSector,
-                               TRUE,
-                               &PartitionList);
+      /* Read partition table */
+      Status = IoReadPartitionTable(DiskDeviceObject,
+                                   DiskDeviceExtension->DiskGeometry->BytesPerSector,
+                                   TRUE,
+                                   &PartitionList);
 
-  DPRINT("IoReadPartitionTable(): Status: %lx\n", Status);
+      DPRINT("IoReadPartitionTable(): Status: %lx\n", Status);
 
-  if ((!NT_SUCCESS(Status) || PartitionList->PartitionCount == 0) &&
-      DiskDeviceObject->Characteristics & FILE_REMOVABLE_MEDIA)
-    {
-      if (!NT_SUCCESS(Status))
+      if ((!NT_SUCCESS(Status) || PartitionList->PartitionCount == 0) &&
+         DiskDeviceObject->Characteristics & FILE_REMOVABLE_MEDIA)
        {
-         /* Drive is not ready. */
-         DPRINT("Drive not ready\n");
-         DiskData->DriveNotReady = TRUE;
-       }
-      else
-       {
-         ExFreePool(PartitionList);
-       }
+         if (!NT_SUCCESS(Status))
+           {
+             /* Drive is not ready. */
+             DPRINT("Drive not ready\n");
+             DiskData->DriveNotReady = TRUE;
+           }
+         else
+           {
+             ExFreePool(PartitionList);
+           }
 
-      /* Allocate a partition list for a single entry. */
-      PartitionList = ExAllocatePool(NonPagedPool,
-                                    sizeof(DRIVE_LAYOUT_INFORMATION));
-      if (PartitionList != NULL)
-       {
-         RtlZeroMemory(PartitionList,
-                       sizeof(DRIVE_LAYOUT_INFORMATION));
-         PartitionList->PartitionCount = 1;
+         /* Allocate a partition list for a single entry. */
+         PartitionList = ExAllocatePool(NonPagedPool,
+                                        sizeof(DRIVE_LAYOUT_INFORMATION));
+         if (PartitionList != NULL)
+           {
+             RtlZeroMemory(PartitionList,
+                           sizeof(DRIVE_LAYOUT_INFORMATION));
+             PartitionList->PartitionCount = 1;
 
-         Status = STATUS_SUCCESS;
+             Status = STATUS_SUCCESS;
+           }
        }
     }
-  }
 
   if (NT_SUCCESS(Status))
     {
       DPRINT("Read partition table!\n");
-
       DPRINT("  Number of partitions: %u\n", PartitionList->PartitionCount);
 
+      /* Set disk signature */
+      DiskData->Signature = PartitionList->Signature;
+
+      /* Calculate MBR checksum if disk got no signature */
+      if (DiskData->Signature == 0)
+       {
+         if (!ScsiDiskCalcMbrCheckSum(DiskDeviceExtension,
+                                      &DiskData->MbrCheckSum))
+           {
+             DPRINT("MBR checksum calculation failed for disk %lu\n",
+                    DiskDeviceExtension->DeviceNumber);
+           }
+         else
+           {
+             DPRINT("MBR checksum for disk %lu is %lx\n",
+                    DiskDeviceExtension->DeviceNumber,
+                    DiskData->MbrCheckSum);
+           }
+       }
+      else
+       {
+         DPRINT("Signature on disk %lu is %lx\n",
+                DiskDeviceExtension->DeviceNumber,
+                DiskData->Signature);
+       }
+
+      /* Update disk geometry if disk is visible to the BIOS */
+      ScsiDiskUpdateFixedDiskGeometry(DiskDeviceExtension);
+
       for (PartitionNumber = 0; PartitionNumber < PartitionList->PartitionCount; PartitionNumber++)
        {
          PartitionEntry = &PartitionList->PartitionEntry[PartitionNumber];
@@ -626,8 +740,10 @@ DiskClassCreateDeviceObject(IN PDRIVER_OBJECT DriverObject,
              PartitionDeviceObject->AlignmentRequirement = DiskDeviceObject->AlignmentRequirement;
 
              PartitionDeviceExtension = PartitionDeviceObject->DeviceExtension;
+             PartitionDeviceExtension->SenseData = DiskDeviceExtension->SenseData;
              PartitionDeviceExtension->LockCount = 0;
              PartitionDeviceExtension->DeviceNumber = DiskNumber;
+             PartitionDeviceExtension->DeviceObject = PartitionDeviceObject;
              PartitionDeviceExtension->PortDeviceObject = PortDeviceObject;
              PartitionDeviceExtension->DiskGeometry = DiskDeviceExtension->DiskGeometry;
              PartitionDeviceExtension->PhysicalDevice = DiskDeviceExtension->PhysicalDevice;
@@ -636,13 +752,26 @@ DiskClassCreateDeviceObject(IN PDRIVER_OBJECT DriverObject,
                PartitionEntry->StartingOffset.QuadPart;
              PartitionDeviceExtension->PartitionLength.QuadPart =
                PartitionEntry->PartitionLength.QuadPart;
+             PartitionDeviceExtension->DMSkew = DiskDeviceExtension->DMSkew;
+             PartitionDeviceExtension->DMByteSkew = DiskDeviceExtension->DMByteSkew;
+             PartitionDeviceExtension->DMActive = DiskDeviceExtension->DMActive;
              PartitionDeviceExtension->PortNumber = (UCHAR)PortNumber;
              PartitionDeviceExtension->PathId = InquiryData->PathId;
              PartitionDeviceExtension->TargetId = InquiryData->TargetId;
              PartitionDeviceExtension->Lun = InquiryData->Lun;
              PartitionDeviceExtension->SectorShift = DiskDeviceExtension->SectorShift;
+             PartitionDeviceExtension->TimeOutValue = SCSI_DISK_TIMEOUT;
+
+             /* Initialize lookaside list for SRBs */
+             ScsiClassInitializeSrbLookasideList(PartitionDeviceExtension,
+                                                 8);
 
+             /* Link current partition device extension to previous disk data */
+             DiskData->NextPartition = PartitionDeviceExtension;
+
+             /* Initialize current disk data */
              DiskData = (PDISK_DATA)(PartitionDeviceExtension + 1);
+             DiskData->NextPartition = NULL;
              DiskData->PartitionType = PartitionEntry->PartitionType;
              DiskData->PartitionNumber = PartitionNumber + 1;
              DiskData->PartitionOrdinal = PartitionNumber + 1;
@@ -714,30 +843,37 @@ DiskClassDeviceControl(IN PDEVICE_OBJECT DeviceObject,
        if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(DISK_GEOMETRY))
          {
            Status = STATUS_INVALID_PARAMETER;
+           break;
          }
-       else
+
+       if (DeviceExtension->DiskGeometry == NULL)
          {
-           PDISK_GEOMETRY Geometry;
+           DPRINT("No disk geometry available!\n");
+           DeviceExtension->DiskGeometry = ExAllocatePool(NonPagedPool,
+                                                          sizeof(DISK_GEOMETRY));
+         }
 
-           if (DeviceExtension->DiskGeometry == NULL)
-             {
-               DPRINT("No disk geometry available!\n");
-               DeviceExtension->DiskGeometry = ExAllocatePool(NonPagedPool,
-                                                              sizeof(DISK_GEOMETRY));
-             }
+       if (DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA)
+         {
            Status = ScsiClassReadDriveCapacity(DeviceObject);
            DPRINT("ScsiClassReadDriveCapacity() returned (Status %lx)\n", Status);
-           if (NT_SUCCESS(Status))
+           if (!NT_SUCCESS(Status))
              {
-               Geometry = (PDISK_GEOMETRY)Irp->AssociatedIrp.SystemBuffer;
-               RtlMoveMemory(Geometry,
-                             DeviceExtension->DiskGeometry,
-                             sizeof(DISK_GEOMETRY));
-
-               Status = STATUS_SUCCESS;
-               Information = sizeof(DISK_GEOMETRY);
+               /* Drive is not ready */
+               DiskData->DriveNotReady = FALSE;
+               break;
              }
+
+           /* Drive is ready */
+           DiskData->DriveNotReady = FALSE;
          }
+
+       RtlMoveMemory(Irp->AssociatedIrp.SystemBuffer,
+                     DeviceExtension->DiskGeometry,
+                     sizeof(DISK_GEOMETRY));
+
+       Status = STATUS_SUCCESS;
+       Information = sizeof(DISK_GEOMETRY);
        break;
 
       case IOCTL_DISK_GET_PARTITION_INFO:
@@ -863,25 +999,66 @@ DiskClassDeviceControl(IN PDEVICE_OBJECT DeviceObject,
              }
            else
              {
-               Status = IoWritePartitionTable(DeviceExtension->DeviceObject,
+               /* Update partition device objects */
+               DiskClassUpdatePartitionDeviceObjects (DeviceObject,
+                                                      Irp);
+
+               /* Write partition table */
+               Status = IoWritePartitionTable(DeviceExtension->PhysicalDevice,
                                               DeviceExtension->DiskGeometry->BytesPerSector,
                                               DeviceExtension->DiskGeometry->SectorsPerTrack,
                                               DeviceExtension->DiskGeometry->TracksPerCylinder,
                                               PartitionList);
                if (NT_SUCCESS(Status))
                  {
-                   /* FIXME: Update partition device objects */
-
                    Information = TableSize;
                  }
              }
          }
        break;
 
+      case IOCTL_DISK_IS_WRITABLE:
+       {
+         PMODE_PARAMETER_HEADER ModeData;
+         ULONG Length;
+
+         ModeData = ExAllocatePool (NonPagedPool,
+                                    MODE_DATA_SIZE);
+         if (ModeData == NULL)
+           {
+             Status = STATUS_INSUFFICIENT_RESOURCES;
+             break;
+           }
+         RtlZeroMemory (ModeData,
+                        MODE_DATA_SIZE);
+
+         Length = ScsiClassModeSense (DeviceObject,
+                                      (PVOID)ModeData,
+                                      MODE_DATA_SIZE,
+                                      MODE_SENSE_RETURN_ALL);
+         if (Length < sizeof(MODE_PARAMETER_HEADER))
+           {
+             /* FIXME: Retry */
+             Status = STATUS_IO_DEVICE_ERROR;
+             ExFreePool (ModeData);
+             break;
+           }
+
+         if (ModeData->DeviceSpecificParameter & MODE_DSP_WRITE_PROTECT)
+           {
+             Status = STATUS_MEDIA_WRITE_PROTECTED;
+           }
+         else
+           {
+             Status = STATUS_SUCCESS;
+           }
+         ExFreePool (ModeData);
+       }
+       break;
+
       case IOCTL_DISK_VERIFY:
       case IOCTL_DISK_FORMAT_TRACKS:
       case IOCTL_DISK_PERFORMANCE:
-      case IOCTL_DISK_IS_WRITABLE:
       case IOCTL_DISK_LOGGING:
       case IOCTL_DISK_FORMAT_TRACKS_EX:
       case IOCTL_DISK_HISTOGRAM_STRUCTURE:
@@ -969,14 +1146,23 @@ DiskClassShutdownFlush(IN PDEVICE_OBJECT DeviceObject,
   Srb->TargetId = DeviceExtension->TargetId;
   Srb->Lun = DeviceExtension->Lun;
 
+  /* Set timeout */
+  Srb->TimeOutValue = DeviceExtension->TimeOutValue * 4;
 
-  /* FIXME: Flush write cache */
-
+  /* Flush write cache */
+  Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+  Srb->SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER;
+  Srb->CdbLength = 10;
+  Srb->Cdb[0] = SCSIOP_SYNCHRONIZE_CACHE;
+  ScsiClassSendSrbSynchronous(DeviceObject,
+                             Srb,
+                             NULL,
+                             0,
+                             TRUE);
 
   /* Get current stack location */
   IrpStack = IoGetCurrentIrpStackLocation(Irp);
 
-
   /* FIXME: Unlock removable media upon shutdown */
 
 
@@ -1008,4 +1194,779 @@ DiskClassShutdownFlush(IN PDEVICE_OBJECT DeviceObject,
   return(IoCallDriver(DeviceExtension->PortDeviceObject, Irp));
 }
 
+
+/**********************************************************************
+ * NAME                                                        INTERNAL
+ *     DiskClassUpdatePartitionDeviceObjects
+ *
+ * DESCRIPTION
+ *     Deletes, modifies or creates partition device objects.
+ *
+ * RUN LEVEL
+ *     PASSIVE_LEVEL
+ *
+ * ARGUMENTS
+ *     DeviceObject
+ *             Pointer to the device.
+ *
+ *     Irp
+ *             Pointer to the IRP
+ *
+ * RETURN VALUE
+ *     None
+ */
+
+static VOID
+DiskClassUpdatePartitionDeviceObjects(IN PDEVICE_OBJECT DiskDeviceObject,
+                                     IN PIRP Irp)
+{
+  PDRIVE_LAYOUT_INFORMATION PartitionList;
+  PPARTITION_INFORMATION PartitionEntry;
+  PDEVICE_EXTENSION DeviceExtension;
+  PDEVICE_EXTENSION DiskDeviceExtension;
+  PDISK_DATA DiskData;
+  ULONG PartitionCount;
+  ULONG PartitionOrdinal;
+  ULONG PartitionNumber;
+  ULONG LastPartitionNumber;
+  ULONG i;
+  BOOLEAN Found;
+  WCHAR NameBuffer[MAX_PATH];
+  UNICODE_STRING DeviceName;
+  PDEVICE_OBJECT DeviceObject;
+  NTSTATUS Status;
+
+  DPRINT("ScsiDiskUpdatePartitionDeviceObjects() called\n");
+
+  /* Get partition list */
+  PartitionList = Irp->AssociatedIrp.SystemBuffer;
+
+  /* Round partition count up by 4 */
+  PartitionCount = ((PartitionList->PartitionCount + 3) / 4) * 4;
+
+  /* Remove the partition numbers from the partition list */
+  for (i = 0; i < PartitionCount; i++)
+    {
+      PartitionList->PartitionEntry[i].PartitionNumber = 0;
+    }
+
+  DiskDeviceExtension = DiskDeviceObject->DeviceExtension;
+
+  /* Traverse on-disk partition list */
+  LastPartitionNumber = 0;
+  DeviceExtension = DiskDeviceExtension;
+  DiskData = (PDISK_DATA)(DeviceExtension + 1);
+  while (TRUE)
+    {
+      DeviceExtension = DiskData->NextPartition;
+      if (DeviceExtension == NULL)
+       break;
+
+      /* Get disk data */
+      DiskData = (PDISK_DATA)(DeviceExtension + 1);
+
+      /* Update last partition number */
+      if (DiskData->PartitionNumber > LastPartitionNumber)
+       LastPartitionNumber = DiskData->PartitionNumber;
+
+      /* Ignore unused on-disk partitions */
+      if (DeviceExtension->PartitionLength.QuadPart == 0ULL)
+       continue;
+
+      Found = FALSE;
+      PartitionOrdinal = 0;
+      for (i = 0; i < PartitionCount; i++)
+       {
+         /* Get current partition entry */
+         PartitionEntry = &PartitionList->PartitionEntry[i];
+
+         /* Ignore empty (aka unused) or extended partitions */
+         if (PartitionEntry->PartitionType == PARTITION_ENTRY_UNUSED ||
+             IsContainerPartition (PartitionEntry->PartitionType))
+           continue;
+
+         PartitionOrdinal++;
+
+         /* Check for matching partition start offset and length */
+         if ((PartitionEntry->StartingOffset.QuadPart !=
+              DeviceExtension->StartingOffset.QuadPart) ||
+             (PartitionEntry->PartitionLength.QuadPart !=
+              DeviceExtension->PartitionLength.QuadPart))
+           continue;
+
+         DPRINT1("Found matching partition entry for partition %lu\n",
+                 DiskData->PartitionNumber);
+
+         /* Found matching partition */
+         Found = TRUE;
+
+         /* Update partition number in partition list */
+         PartitionEntry->PartitionNumber = DiskData->PartitionNumber;
+         break;
+       }
+
+      if (Found == TRUE)
+       {
+         /* Get disk data for current partition */
+         DiskData = (PDISK_DATA)(DeviceExtension + 1);
+
+         /* Update partition type if partiton will be rewritten */
+         if (PartitionEntry->RewritePartition == TRUE)
+           DiskData->PartitionType = PartitionEntry->PartitionType;
+
+         /* Assign new partiton ordinal */
+         DiskData->PartitionOrdinal = PartitionOrdinal;
+
+         DPRINT("Partition ordinal %lu was assigned to partition %lu\n",
+                DiskData->PartitionOrdinal,
+                DiskData->PartitionNumber);
+       }
+      else
+       {
+         /* Delete this partition */
+         DeviceExtension->PartitionLength.QuadPart = 0ULL;
+
+         DPRINT("Deleting partition %lu\n",
+                DiskData->PartitionNumber);
+       }
+    }
+
+  /* Traverse partiton list and create new partiton devices */
+  PartitionOrdinal = 0;
+  for (i = 0; i < PartitionCount; i++)
+    {
+      /* Get current partition entry */
+      PartitionEntry = &PartitionList->PartitionEntry[i];
+
+      /* Ignore empty (aka unused) or extended partitions */
+      if (PartitionEntry->PartitionType == PARTITION_ENTRY_UNUSED ||
+         IsContainerPartition (PartitionEntry->PartitionType))
+       continue;
+
+      PartitionOrdinal++;
+
+      /* Ignore unchanged partition entries */
+      if (PartitionEntry->RewritePartition == FALSE)
+       continue;
+
+      /* Check for an unused device object */
+      PartitionNumber = 0;
+      DeviceExtension = DiskDeviceExtension;
+      DiskData = (PDISK_DATA)(DeviceExtension + 1);
+      while (TRUE)
+       {
+         DeviceExtension = DiskData->NextPartition;
+         if (DeviceExtension == NULL)
+           break;
+
+         /* Get partition disk data */
+         DiskData = (PDISK_DATA)(DeviceExtension + 1);
+
+         /* Found a free (unused) partition (device object) */
+         if (DeviceExtension->PartitionLength.QuadPart == 0ULL)
+           {
+             PartitionNumber = DiskData->PartitionNumber;
+             break;
+           }
+       }
+
+      if (PartitionNumber == 0)
+       {
+         /* Create a new partition device object */
+         DPRINT("Create new partition device object\n");
+
+         /* Get new partiton number */
+         LastPartitionNumber++;
+         PartitionNumber = LastPartitionNumber;
+
+         /* Create partition device object */
+         swprintf(NameBuffer,
+                  L"\\Device\\Harddisk%lu\\Partition%lu",
+                  DiskDeviceExtension->DeviceNumber,
+                  PartitionNumber);
+         RtlInitUnicodeString(&DeviceName,
+                              NameBuffer);
+
+         Status = IoCreateDevice(DiskDeviceObject->DriverObject,
+                                 sizeof(DEVICE_EXTENSION) + sizeof(DISK_DATA),
+                                 &DeviceName,
+                                 FILE_DEVICE_DISK,
+                                 0,
+                                 FALSE,
+                                 &DeviceObject);
+         if (!NT_SUCCESS(Status))
+           {
+             DPRINT1("IoCreateDevice() failed (Status %lx)\n", Status);
+             continue;
+           }
+
+         DeviceObject->Flags |= DO_DIRECT_IO;
+         DeviceObject->StackSize = DiskDeviceObject->StackSize;
+         DeviceObject->Characteristics = DiskDeviceObject->Characteristics;
+         DeviceObject->AlignmentRequirement = DiskDeviceObject->AlignmentRequirement;
+
+         /* Initialize device extension */
+         DeviceExtension = DeviceObject->DeviceExtension;
+         RtlCopyMemory(DeviceExtension,
+                       DiskDeviceObject->DeviceExtension,
+                       sizeof(DEVICE_EXTENSION));
+         DeviceExtension->DeviceObject = DeviceObject;
+
+         /* Initialize lookaside list for SRBs */
+         ScsiClassInitializeSrbLookasideList(DeviceExtension,
+                                             8);
+
+         /* Link current partition device extension to previous disk data */
+         DiskData->NextPartition = DeviceExtension;
+         DiskData = (PDISK_DATA)(DeviceExtension + 1);
+         DiskData->NextPartition = NULL;
+       }
+      else
+       {
+         /* Reuse an existing partition device object */
+         DPRINT("Reuse an exisiting partition device object\n");
+         DiskData = (PDISK_DATA)(DeviceExtension + 1);
+       }
+
+      /* Update partition data and device extension */
+      DiskData->PartitionNumber = PartitionNumber;
+      DiskData->PartitionOrdinal = PartitionOrdinal;
+      DiskData->PartitionType = PartitionEntry->PartitionType;
+      DiskData->BootIndicator = PartitionEntry->BootIndicator;
+      DiskData->HiddenSectors = PartitionEntry->HiddenSectors;
+      DeviceExtension->StartingOffset = PartitionEntry->StartingOffset;
+      DeviceExtension->PartitionLength = PartitionEntry->PartitionLength;
+
+      /* Update partition number in the partition list */
+      PartitionEntry->PartitionNumber = PartitionNumber;
+
+      DPRINT("Partition ordinal %lu was assigned to partition %lu\n",
+            DiskData->PartitionOrdinal,
+            DiskData->PartitionNumber);
+    }
+
+  DPRINT("ScsiDiskUpdatePartitionDeviceObjects() done\n");
+}
+
+
+/**********************************************************************
+ * NAME                                                        INTERNAL
+ *     ScsiDiskSearchForDisk
+ *
+ * DESCRIPTION
+ *     Searches the hardware tree for the given disk.
+ *
+ * RUN LEVEL
+ *     PASSIVE_LEVEL
+ *
+ * ARGUMENTS
+ *     DeviceExtension
+ *             Disk device extension.
+ *
+ *     BusKey
+ *             Handle to the hardware bus key.
+ *
+ *     DetectedDiskNumber
+ *             Returned disk number.
+ *
+ * RETURN VALUE
+ *     TRUE: Disk was found.
+ *     FALSE: Search failed.
+ */
+
+static BOOLEAN
+ScsiDiskSearchForDisk(IN PDEVICE_EXTENSION DeviceExtension,
+                     IN HANDLE BusKey,
+                     OUT PULONG DetectedDiskNumber)
+{
+  PKEY_VALUE_FULL_INFORMATION ValueData;
+  OBJECT_ATTRIBUTES ObjectAttributes;
+  PDISK_DATA DiskData;
+  UNICODE_STRING IdentifierString;
+  UNICODE_STRING NameString;
+  HANDLE BusInstanceKey;
+  HANDLE ControllerKey;
+  HANDLE DiskKey;
+  HANDLE DiskInstanceKey;
+  ULONG BusNumber;
+  ULONG ControllerNumber;
+  ULONG DiskNumber;
+  ULONG Length;
+  WCHAR Buffer[32];
+  BOOLEAN DiskFound;
+  NTSTATUS Status;
+
+  DPRINT("ScsiDiskSearchForDiskData() called\n");
+
+  DiskFound = FALSE;
+
+  /* Enumerate buses */
+  for (BusNumber = 0; ; BusNumber++)
+    {
+      /* Open bus instance subkey */
+      swprintf(Buffer,
+              L"%lu",
+              BusNumber);
+
+      RtlInitUnicodeString(&NameString,
+                          Buffer);
+
+      InitializeObjectAttributes(&ObjectAttributes,
+                                &NameString,
+                                OBJ_CASE_INSENSITIVE,
+                                BusKey,
+                                NULL);
+
+      Status = ZwOpenKey(&BusInstanceKey,
+                        KEY_READ,
+                        &ObjectAttributes);
+      if (!NT_SUCCESS(Status))
+       {
+         break;
+       }
+
+      /* Open 'DiskController' subkey */
+      RtlInitUnicodeString(&NameString,
+                          L"DiskController");
+
+      InitializeObjectAttributes(&ObjectAttributes,
+                                &NameString,
+                                OBJ_CASE_INSENSITIVE,
+                                BusInstanceKey,
+                                NULL);
+
+      Status = ZwOpenKey(&ControllerKey,
+                        KEY_READ,
+                        &ObjectAttributes);
+      if (!NT_SUCCESS(Status))
+       {
+         ZwClose(BusInstanceKey);
+         continue;
+       }
+
+      /* Enumerate controllers */
+      for (ControllerNumber = 0; ; ControllerNumber++)
+       {
+         /* Open 'DiskPeripheral' subkey */
+         swprintf(Buffer,
+                  L"%lu\\DiskPeripheral",
+                  ControllerNumber);
+
+         RtlInitUnicodeString(&NameString,
+                              Buffer);
+
+         InitializeObjectAttributes(&ObjectAttributes,
+                                    &NameString,
+                                    OBJ_CASE_INSENSITIVE,
+                                    ControllerKey,
+                                    NULL);
+
+         Status = ZwOpenKey(&DiskKey,
+                            KEY_READ,
+                            &ObjectAttributes);
+         if (!NT_SUCCESS(Status))
+           {
+             break;
+           }
+
+         /* Enumerate disks */
+         for (DiskNumber = 0; ; DiskNumber++)
+           {
+             /* Open disk instance subkey */
+             swprintf(Buffer,
+                      L"%lu",
+                      DiskNumber);
+
+             RtlInitUnicodeString(&NameString,
+                                  Buffer);
+
+             InitializeObjectAttributes(&ObjectAttributes,
+                                        &NameString,
+                                        OBJ_CASE_INSENSITIVE,
+                                        DiskKey,
+                                        NULL);
+
+             Status = ZwOpenKey(&DiskInstanceKey,
+                                KEY_READ,
+                                &ObjectAttributes);
+             if (!NT_SUCCESS(Status))
+               {
+                 break;
+               }
+
+             DPRINT("Found disk key: bus %lu  controller %lu  disk %lu\n",
+                    BusNumber,
+                    ControllerNumber,
+                    DiskNumber);
+
+             /* Allocate data buffer */
+             ValueData = ExAllocatePool(PagedPool,
+                                        2048);
+             if (ValueData == NULL)
+               {
+                 ZwClose(DiskInstanceKey);
+                 continue;
+               }
+
+             /* Get the 'Identifier' value */
+             RtlInitUnicodeString(&NameString,
+                                  L"Identifier");
+             Status = ZwQueryValueKey(DiskInstanceKey,
+                                      &NameString,
+                                      KeyValueFullInformation,
+                                      ValueData,
+                                      2048,
+                                      &Length);
+
+             ZwClose(DiskInstanceKey);
+             if (!NT_SUCCESS(Status))
+               {
+                 ExFreePool(ValueData);
+                 continue;
+               }
+
+             IdentifierString.Buffer =
+               (PWSTR)((PUCHAR)ValueData + ValueData->DataOffset);
+             IdentifierString.Length = (USHORT)ValueData->DataLength - 2;
+             IdentifierString.MaximumLength = (USHORT)ValueData->DataLength;
+
+             DPRINT("DiskIdentifier: %wZ\n",
+                    &IdentifierString);
+
+             DiskData = (PDISK_DATA)(DeviceExtension + 1);
+             if (DiskData->Signature != 0)
+               {
+                 /* Comapre disk signature */
+                 swprintf(Buffer,
+                          L"%08lx",
+                          DiskData->Signature);
+                 if (!_wcsnicmp(Buffer, &IdentifierString.Buffer[9], 8))
+                   {
+                     DPRINT("Found disk %lu\n", DiskNumber);
+                     DiskFound = TRUE;
+                     *DetectedDiskNumber = DiskNumber;
+                   }
+               }
+             else
+               {
+                 /* Comapre mbr checksum */
+                 swprintf(Buffer,
+                          L"%08lx",
+                          DiskData->MbrCheckSum);
+                 if (!_wcsnicmp(Buffer, &IdentifierString.Buffer[0], 8))
+                   {
+                     DPRINT("Found disk %lu\n", DiskNumber);
+                     DiskFound = TRUE;
+                     *DetectedDiskNumber = DiskNumber;
+                   }
+               }
+
+             ExFreePool(ValueData);
+
+             ZwClose(DiskInstanceKey);
+
+             if (DiskFound == TRUE)
+               break;
+           }
+
+         ZwClose(DiskKey);
+       }
+
+      ZwClose(ControllerKey);
+      ZwClose(BusInstanceKey);
+    }
+
+  DPRINT("ScsiDiskSearchForDisk() done\n");
+
+  return DiskFound;
+}
+
+
+/**********************************************************************
+ * NAME                                                        INTERNAL
+ *     DiskClassUpdateFixedDiskGeometry
+ *
+ * DESCRIPTION
+ *     Updated the geometry of a disk if the disk can be accessed
+ *     by the BIOS.
+ *
+ * RUN LEVEL
+ *     PASSIVE_LEVEL
+ *
+ * ARGUMENTS
+ *     DeviceExtension
+ *             Disk device extension.
+ *
+ * RETURN VALUE
+ *     None
+ */
+
+static VOID
+ScsiDiskUpdateFixedDiskGeometry(IN PDEVICE_EXTENSION DeviceExtension)
+{
+  PCM_FULL_RESOURCE_DESCRIPTOR ResourceDescriptor;
+  PCM_INT13_DRIVE_PARAMETER DriveParameters;
+  PKEY_VALUE_FULL_INFORMATION ValueBuffer;
+  OBJECT_ATTRIBUTES ObjectAttributes;
+  UNICODE_STRING KeyName;
+  UNICODE_STRING ValueName;
+  HANDLE SystemKey;
+  HANDLE BusKey;
+  ULONG DiskNumber;
+  ULONG Length;
+  ULONG i;
+  ULONG Cylinders;
+  ULONG Sectors;
+  ULONG SectorsPerTrack;
+  ULONG TracksPerCylinder;
+  NTSTATUS Status;
+
+  DPRINT("ScsiDiskUpdateFixedDiskGeometry() called\n");
+
+  RtlInitUnicodeString(&KeyName,
+                      L"\\Registry\\Machine\\Hardware\\Description\\System");
+
+  InitializeObjectAttributes(&ObjectAttributes,
+                            &KeyName,
+                            OBJ_CASE_INSENSITIVE,
+                            NULL,
+                            NULL);
+
+  /* Open the adapter key */
+  Status = ZwOpenKey(&SystemKey,
+                    KEY_READ,
+                    &ObjectAttributes);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT("ZwOpenKey() failed (Status %lx)\n", Status);
+      return;
+    }
+
+  /* Allocate value buffer */
+  ValueBuffer = ExAllocatePool(PagedPool,
+                              1024);
+  if (ValueBuffer == NULL)
+    {
+      DPRINT1("Failed to allocate value buffer\n");
+      ZwClose(SystemKey);
+      return;
+    }
+
+  RtlInitUnicodeString(&ValueName,
+                      L"Configuration Data");
+
+  /* Query 'Configuration Data' value */
+  Status = ZwQueryValueKey(SystemKey,
+                          &ValueName,
+                          KeyValueFullInformation,
+                          ValueBuffer,
+                          1024,
+                          &Length);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT("ZwQueryValueKey() failed (Status %lx)\n", Status);
+      ExFreePool(ValueBuffer);
+      ZwClose(SystemKey);
+      return;
+    }
+
+  /* Open the 'MultifunctionAdapter' subkey */
+  RtlInitUnicodeString(&KeyName,
+                      L"MultifunctionAdapter");
+
+  InitializeObjectAttributes(&ObjectAttributes,
+                            &KeyName,
+                            OBJ_CASE_INSENSITIVE,
+                            SystemKey,
+                            NULL);
+
+  Status = ZwOpenKey(&BusKey,
+                    KEY_READ,
+                    &ObjectAttributes);
+  ZwClose(SystemKey);
+  if (!NT_SUCCESS(Status))
+    {
+      DPRINT("ZwQueryValueKey() failed (Status %lx)\n", Status);
+      ExFreePool(ValueBuffer);
+      return;
+    }
+
+  if (!ScsiDiskSearchForDisk(DeviceExtension, BusKey, &DiskNumber))
+    {
+      DPRINT("ScsiDiskSearchForDisk() failed\n");
+      ZwClose(BusKey);
+      ExFreePool(ValueBuffer);
+      return;
+    }
+
+  ZwClose(BusKey);
+
+  ResourceDescriptor = (PCM_FULL_RESOURCE_DESCRIPTOR)
+    ((PUCHAR)ValueBuffer + ValueBuffer->DataOffset);
+
+  DriveParameters = (PCM_INT13_DRIVE_PARAMETER)
+    ((PUCHAR)ResourceDescriptor + sizeof(CM_FULL_RESOURCE_DESCRIPTOR));
+
+#if 0
+  for (i = 0; i< DriveParameters[0].NumberDrives; i++)
+    {
+      DPRINT1("Drive %lu: %lu Cylinders  %hu Heads  %hu Sectors\n",
+             i,
+             DriveParameters[i].MaxCylinders,
+             DriveParameters[i].MaxHeads,
+             DriveParameters[i].SectorsPerTrack);
+    }
+#endif
+
+  Cylinders = DriveParameters[DiskNumber].MaxCylinders + 1;
+  TracksPerCylinder = DriveParameters[DiskNumber].MaxHeads +1;
+  SectorsPerTrack = DriveParameters[DiskNumber].SectorsPerTrack;
+
+  DPRINT("BIOS geometry: %lu Cylinders  %hu Heads  %hu Sectors\n",
+        Cylinders,
+        TracksPerCylinder,
+        SectorsPerTrack);
+
+  Sectors = (ULONG)
+    (DeviceExtension->PartitionLength.QuadPart >> DeviceExtension->SectorShift);
+
+  DPRINT("Physical sectors: %lu\n",
+        Sectors);
+
+  Length = TracksPerCylinder * SectorsPerTrack;
+  if (Length == 0)
+    {
+      DPRINT1("Invalid track length 0\n");
+      ExFreePool(ValueBuffer);
+      return;
+    }
+
+  Cylinders = Sectors / Length;
+
+  DPRINT("Logical geometry: %lu Cylinders  %hu Heads  %hu Sectors\n",
+        Cylinders,
+        TracksPerCylinder,
+        SectorsPerTrack);
+
+  /* Update the disk geometry */
+  DeviceExtension->DiskGeometry->SectorsPerTrack = SectorsPerTrack;
+  DeviceExtension->DiskGeometry->TracksPerCylinder = TracksPerCylinder;
+  DeviceExtension->DiskGeometry->Cylinders.QuadPart = (ULONGLONG)Cylinders;
+
+  if (DeviceExtension->DMActive)
+    {
+      DPRINT1("FIXME: Update geometry with respect to the installed disk manager!\n");
+
+      /* FIXME: Update geometry for disk managers */
+
+    }
+
+  ExFreePool(ValueBuffer);
+
+  DPRINT("ScsiDiskUpdateFixedDiskGeometry() done\n");
+}
+
+
+/**********************************************************************
+ * NAME                                                        INTERNAL
+ *     ScsiDiskCalcMbrCheckSum
+ *
+ * DESCRIPTION
+ *     Calculates the Checksum from drives MBR.
+ *
+ * RUN LEVEL
+ *     PASSIVE_LEVEL
+ *
+ * ARGUMENTS
+ *     DeviceExtension
+ *             Disk device extension.
+ *
+ *     Checksum
+ *             Pointer to the caller supplied cecksum variable.
+ *
+ * RETURN VALUE
+ *     TRUE: Checksum was calculated.
+ *     FALSE: Calculation failed.
+ */
+
+static BOOLEAN
+ScsiDiskCalcMbrCheckSum(IN PDEVICE_EXTENSION DeviceExtension,
+                       OUT PULONG Checksum)
+{
+  IO_STATUS_BLOCK IoStatusBlock;
+  LARGE_INTEGER SectorOffset;
+  ULONG SectorSize;
+  PULONG MbrBuffer;
+  KEVENT Event;
+  PIRP Irp;
+  ULONG i;
+  ULONG Sum;
+  NTSTATUS Status;
+
+  KeInitializeEvent(&Event,
+                   NotificationEvent,
+                   FALSE);
+
+  /* Get the disk sector size */
+  SectorSize = DeviceExtension->DiskGeometry->BytesPerSector;
+  if (SectorSize < 512)
+    {
+      SectorSize = 512;
+    }
+
+  /* Allocate MBR buffer */
+  MbrBuffer = ExAllocatePool(NonPagedPool,
+                            SectorSize);
+  if (MbrBuffer == NULL)
+    {
+      return FALSE;
+    }
+
+  /* Allocate an IRP */
+  SectorOffset.QuadPart = 0ULL;
+  Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ,
+                                    DeviceExtension->DeviceObject,
+                                    MbrBuffer,
+                                    SectorSize,
+                                    &SectorOffset,
+                                    &Event,
+                                    &IoStatusBlock);
+  if (Irp == NULL)
+    {
+      ExFreePool(MbrBuffer);
+      return FALSE;
+    }
+
+  /* Call the miniport driver */
+  Status = IoCallDriver(DeviceExtension->DeviceObject,
+                       Irp);
+  if (Status == STATUS_PENDING)
+    {
+      KeWaitForSingleObject(&Event,
+                           Suspended,
+                           KernelMode,
+                           FALSE,
+                           NULL);
+      Status = IoStatusBlock.Status;
+    }
+
+  if (!NT_SUCCESS(Status))
+    {
+      ExFreePool(MbrBuffer);
+      return FALSE;
+    }
+
+  /* Calculate MBR checksum */
+  Sum = 0;
+  for (i = 0; i < 128; i++)
+    {
+      Sum += MbrBuffer[i];
+    }
+  *Checksum = ~Sum + 1;
+
+  ExFreePool(MbrBuffer);
+
+  return TRUE;
+}
+
 /* EOF */