Check for removable media and Partition length is 0, for DiskClassCheckReadWrite.
[reactos.git] / reactos / drivers / storage / disk / disk.c
index 449ad71..b0e8904 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  ReactOS kernel
- *  Copyright (C) 2001, 2002, 2003 ReactOS Team
+ *  Copyright (C) 2001, 2002, 2003, 2004 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
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
-/* $Id: disk.c,v 1.26 2003/04/27 18:10:38 ekohl Exp $
+/* $Id$
  *
  * COPYRIGHT:       See COPYING in the top level directory
  * PROJECT:         ReactOS kernel
  * FILE:            services/storage/disk/disk.c
  * PURPOSE:         disk class driver
- * PROGRAMMER:      Eric Kohl (ekohl@rz-online.de)
+ * PROGRAMMER:      Eric Kohl
  */
 
 /* INCLUDES *****************************************************************/
 
 #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;
@@ -49,7 +54,6 @@ typedef struct _DISK_DATA
   BOOLEAN DriveNotReady;
 } DISK_DATA, *PDISK_DATA;
 
-
 BOOLEAN STDCALL
 DiskClassFindDevices(PDRIVER_OBJECT DriverObject,
                     PUNICODE_STRING RegistryPath,
@@ -64,6 +68,9 @@ NTSTATUS STDCALL
 DiskClassCheckReadWrite(IN PDEVICE_OBJECT DeviceObject,
                        IN PIRP Irp);
 
+static VOID
+DiskClassCreateMediaChangeEvent(IN PDEVICE_EXTENSION DeviceExtension,
+                                IN ULONG DeviceNumber); 
 
 static NTSTATUS
 DiskClassCreateDeviceObject(IN PDRIVER_OBJECT DriverObject,
@@ -83,6 +90,11 @@ 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);
@@ -90,6 +102,15 @@ DiskClassUpdatePartitionDeviceObjects (IN PDEVICE_OBJECT DeviceObject,
 static VOID
 ScsiDiskUpdateFixedDiskGeometry(IN PDEVICE_EXTENSION DeviceExtension);
 
+static BOOLEAN
+ScsiDiskCalcMbrCheckSum(IN PDEVICE_EXTENSION DeviceExtension,
+                       OUT PULONG Checksum);
+
+
+static NTSTATUS
+DiskBuildPartitionTable(IN PDEVICE_OBJECT DiskDeviceObject,
+                     IN PIRP Irp);
+
 
 /* FUNCTIONS ****************************************************************/
 
@@ -197,7 +218,7 @@ DiskClassFindDevices(PDRIVER_OBJECT DriverObject,
   PCHAR Buffer;
   ULONG Bus;
   ULONG DeviceCount;
-  BOOLEAN FoundDevice;
+  BOOLEAN FoundDevice = FALSE;
   NTSTATUS Status;
 
   DPRINT("DiskClassFindDevices() called.\n");
@@ -249,6 +270,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) &&
@@ -288,6 +311,28 @@ DiskClassFindDevices(PDRIVER_OBJECT DriverObject,
 }
 
 
+static VOID
+DiskClassCreateMediaChangeEvent(IN PDEVICE_EXTENSION DeviceExtension,
+                                IN ULONG DeviceNumber)
+{
+  WCHAR NameBuffer[MAX_PATH];
+  UNICODE_STRING Name;
+
+  swprintf (NameBuffer,
+           L"\\Device\\MediaChangeEvent%lu",
+           DeviceNumber);
+  RtlInitUnicodeString (&Name,
+                       NameBuffer);
+
+  DeviceExtension->MediaChangeEvent =
+    IoCreateSynchronizationEvent (&Name,
+                                 &DeviceExtension->MediaChangeEventHandle);
+
+  KeClearEvent (DeviceExtension->MediaChangeEvent);
+}
+
+
+
 /**********************************************************************
  * NAME                                                        EXPORTED
  *     DiskClassCheckDevice
@@ -345,6 +390,8 @@ DiskClassCheckReadWrite(IN PDEVICE_OBJECT DeviceObject,
 {
   PDEVICE_EXTENSION DeviceExtension;
   PDISK_DATA DiskData;
+  PIO_STACK_LOCATION IrpStack;
+  ULARGE_INTEGER EndingOffset;
 
   DPRINT("DiskClassCheckReadWrite() called\n");
 
@@ -359,6 +406,32 @@ DiskClassCheckReadWrite(IN PDEVICE_OBJECT DeviceObject,
       return(STATUS_INVALID_PARAMETER);
     }
 
+
+
+  IrpStack = IoGetCurrentIrpStackLocation(Irp);  
+  EndingOffset.QuadPart = IrpStack->Parameters.Read.ByteOffset.QuadPart +
+                          IrpStack->Parameters.Read.Length;
+
+
+  DPRINT("Ending %I64d, and RealEnding %I64d! PartSize %I64d\n",EndingOffset.QuadPart,
+          DeviceExtension->PartitionLength.QuadPart,
+         DeviceExtension->PartitionLength.QuadPart /
+          DeviceExtension->DiskGeometry->BytesPerSector);
+
+  if ((DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) &&
+      (DeviceExtension->DiskGeometry->MediaType == RemovableMedia))
+    {
+/* Assume if removable media and if Partition length is 0, Partition not built yet! */
+       if (DeviceExtension->PartitionLength.QuadPart == 0)
+            return(STATUS_SUCCESS);
+    }
+
+  if (EndingOffset.QuadPart > DeviceExtension->PartitionLength.QuadPart)
+    {
+      Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
+      return(STATUS_INVALID_PARAMETER);
+    }
+
   return(STATUS_SUCCESS);
 }
 
@@ -391,7 +464,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,
@@ -494,6 +567,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;
@@ -502,6 +576,20 @@ DiskClassCreateDeviceObject(IN PDRIVER_OBJECT DriverObject,
   DiskDeviceExtension->PathId = InquiryData->PathId;
   DiskDeviceExtension->TargetId = InquiryData->TargetId;
   DiskDeviceExtension->Lun = InquiryData->Lun;
+  DiskDeviceExtension->SrbFlags = 0;
+
+  /* Enable the command queueing, if it possible */
+  if (Capabilities->TaggedQueuing &&
+      ((PINQUIRYDATA)InquiryData->InquiryData)->CommandQueue)
+    {
+      DiskDeviceExtension->SrbFlags |= SRB_FLAGS_QUEUE_ACTION_ENABLE;
+    }
+
+  /* Get timeout value */
+  DiskDeviceExtension->TimeOutValue =
+    ScsiClassQueryTimeOutRegistryValue(RegistryPath);
+  if (DiskDeviceExtension->TimeOutValue == 0)
+    DiskDeviceExtension->TimeOutValue = SCSI_DISK_TIMEOUT;
 
   /* Initialize the lookaside list for SRBs */
   ScsiClassInitializeSrbLookasideList(DiskDeviceExtension,
@@ -536,12 +624,38 @@ DiskClassCreateDeviceObject(IN PDRIVER_OBJECT DriverObject,
       return(STATUS_INSUFFICIENT_RESOURCES);
     }
 
+  /* Allocate sense data buffer */
+  DiskDeviceExtension->SenseData = ExAllocatePool(NonPagedPoolCacheAligned,
+                                                 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 */
+      ScsiClassClaimDevice(PortDeviceObject,
+                          InquiryData,
+                          TRUE,
+                          NULL);
+
+      /* Delete the harddisk device directory */
+      ZwMakeTemporaryObject(Handle);
+      ZwClose(Handle);
+
+      return(STATUS_INSUFFICIENT_RESOURCES);
+    }
+
   /* Read the drive's capacity */
   Status = ScsiClassReadDriveCapacity(DiskDeviceObject);
   if (!NT_SUCCESS(Status) &&
       (DiskDeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) == 0)
     {
-      DPRINT1("Failed to retrieve drive capacity!\n");
+      DPRINT("Failed to retrieve drive capacity!\n");
       return(STATUS_SUCCESS);
     }
   else
@@ -552,6 +666,16 @@ DiskClassCreateDeviceObject(IN PDRIVER_OBJECT DriverObject,
 
   DPRINT("SectorSize: %lu\n", DiskDeviceExtension->DiskGeometry->BytesPerSector);
 
+  if ((DiskDeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) &&
+      (DiskDeviceExtension->DiskGeometry->MediaType == RemovableMedia))
+    {
+      DiskClassCreateMediaChangeEvent(DiskDeviceExtension,DiskNumber); 
+      if (DiskDeviceExtension->MediaChangeEvent != NULL)
+       {
+         DPRINT("Allocated media change event!\n");
+       }
+    }
+
   /* Check disk for presence of a disk manager */
   HalExamineMBR(DiskDeviceObject,
                DiskDeviceExtension->DiskGeometry->BytesPerSector,
@@ -628,9 +752,35 @@ DiskClassCreateDeviceObject(IN PDRIVER_OBJECT DriverObject,
   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++)
@@ -642,8 +792,10 @@ DiskClassCreateDeviceObject(IN PDRIVER_OBJECT DriverObject,
                 PartitionEntry->PartitionNumber,
                 PartitionEntry->BootIndicator,
                 PartitionEntry->PartitionType,
-                PartitionEntry->StartingOffset.QuadPart / 512 /*DrvParms.BytesPerSector*/,
-                PartitionEntry->PartitionLength.QuadPart / 512 /* DrvParms.BytesPerSector*/);
+                PartitionEntry->StartingOffset.QuadPart /
+                  DiskDeviceExtension->DiskGeometry->BytesPerSector,
+                PartitionEntry->PartitionLength.QuadPart /
+                  DiskDeviceExtension->DiskGeometry->BytesPerSector);
 
          /* Create partition device object */
          sprintf(NameBuffer2,
@@ -654,7 +806,7 @@ DiskClassCreateDeviceObject(IN PDRIVER_OBJECT DriverObject,
          Status = ScsiClassCreateDeviceObject(DriverObject,
                                               NameBuffer2,
                                               DiskDeviceObject,
-                                              &PartitionDeviceObject,
+                                              &PartitionDeviceObject ,
                                               InitializationData);
          DPRINT("ScsiClassCreateDeviceObject(): Status %x\n", Status);
          if (NT_SUCCESS(Status))
@@ -665,8 +817,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;
@@ -683,6 +837,7 @@ DiskClassCreateDeviceObject(IN PDRIVER_OBJECT DriverObject,
              PartitionDeviceExtension->TargetId = InquiryData->TargetId;
              PartitionDeviceExtension->Lun = InquiryData->Lun;
              PartitionDeviceExtension->SectorShift = DiskDeviceExtension->SectorShift;
+             PartitionDeviceExtension->TimeOutValue = SCSI_DISK_TIMEOUT;
 
              /* Initialize lookaside list for SRBs */
              ScsiClassInitializeSrbLookasideList(PartitionDeviceExtension,
@@ -703,7 +858,7 @@ DiskClassCreateDeviceObject(IN PDRIVER_OBJECT DriverObject,
            }
          else
            {
-             DPRINT1("ScsiClassCreateDeviceObject() failed to create partition device object (Status %x)\n", Status);
+             DPRINT("ScsiClassCreateDeviceObject() failed to create partition device object (Status %x)\n", Status);
 
              break;
            }
@@ -719,6 +874,111 @@ DiskClassCreateDeviceObject(IN PDRIVER_OBJECT DriverObject,
 }
 
 
+static NTSTATUS
+DiskBuildPartitionTable(IN PDEVICE_OBJECT DiskDeviceObject,
+                        IN PIRP Irp)
+{
+  PDRIVE_LAYOUT_INFORMATION PartitionList = NULL;
+  PDEVICE_EXTENSION DiskDeviceExtension, DDE;
+  PDISK_DATA DiskData, DD;
+  PPARTITION_INFORMATION PartitionEntry;
+  ULONG PartitionNumber;
+  NTSTATUS Status;
+
+  DPRINT("DiskBuildPartitionTable() start\n");
+
+  DiskDeviceExtension = (PDEVICE_EXTENSION)DiskDeviceObject->DeviceExtension;
+  DiskData = (PDISK_DATA)(DiskDeviceExtension + 1);
+
+  DDE = (PDEVICE_EXTENSION) DiskDeviceExtension->PhysicalDevice->DeviceExtension;
+  DD = (PDISK_DATA)(DDE +1);
+
+  /* Clear flag for Partition0, just incase it was set. */
+  DD->DriveNotReady = FALSE;
+
+  Status = ScsiClassReadDriveCapacity(DiskDeviceObject);
+  if (!NT_SUCCESS(Status))
+    {
+      /* Drive is not ready. */
+      DPRINT("Drive not ready\n");
+      DiskData->DriveNotReady = TRUE;
+      return Status;
+    }
+
+  /* Read partition table */
+  Status = IoReadPartitionTable(DiskDeviceExtension->PhysicalDevice,
+                               DiskDeviceExtension->DiskGeometry->BytesPerSector,
+                               TRUE,
+                               &PartitionList);
+
+  DPRINT("IoReadPartitionTable(): Status: %lx\n", Status);
+
+  if (!NT_SUCCESS(Status))
+    {
+      /* Drive is not ready. */
+      DPRINT("Drive not ready\n");
+      DiskData->DriveNotReady = TRUE;
+      if (PartitionList != NULL)
+       ExFreePool(PartitionList);
+      return Status;
+    }
+
+  if (NT_SUCCESS(Status))
+    {
+      DPRINT("Read partition table!\n");
+      DPRINT("  Number of partitions: %u\n", PartitionList->PartitionCount);
+
+      /* Set disk signature */
+      DiskData->Signature = PartitionList->Signature;
+
+      DiskData->NextPartition = NULL;
+
+      if (PartitionList->PartitionCount)
+        {
+         for (PartitionNumber = 0; PartitionNumber < PartitionList->PartitionCount; PartitionNumber++)
+           {
+             PartitionEntry = &PartitionList->PartitionEntry[PartitionNumber];
+
+             DiskData->PartitionType = PartitionEntry->PartitionType;
+             DiskData->PartitionNumber = PartitionNumber + 1;
+             DiskData->PartitionOrdinal = PartitionNumber + 1;
+             DiskData->HiddenSectors = PartitionEntry->HiddenSectors;
+             DiskData->BootIndicator = PartitionEntry->BootIndicator;
+             DiskData->DriveNotReady = FALSE;
+             DiskDeviceExtension->StartingOffset = PartitionEntry->StartingOffset;
+             DiskDeviceExtension->PartitionLength = PartitionEntry->PartitionLength;
+
+             DPRINT1("Partition %02ld: nr: %d boot: %1x type: %x offset: %I64d size: %I64d\n",
+                     PartitionNumber,
+                     DiskData->PartitionNumber,
+                     DiskData->BootIndicator,
+                     DiskData->PartitionType,
+                     DiskDeviceExtension->StartingOffset.QuadPart /
+                       DiskDeviceExtension->DiskGeometry->BytesPerSector,
+                     DiskDeviceExtension->PartitionLength.QuadPart /
+                       DiskDeviceExtension->DiskGeometry->BytesPerSector);
+           }
+       }
+      else
+       {
+         DiskData->PartitionType = 0;
+         DiskData->PartitionNumber = 1;
+         DiskData->PartitionOrdinal = 0;
+         DiskData->HiddenSectors = 0;
+         DiskData->BootIndicator = 0;
+         DiskData->DriveNotReady = FALSE;
+         DiskDeviceExtension->StartingOffset.QuadPart = 0;
+         DiskDeviceExtension->PartitionLength.QuadPart += DiskDeviceExtension->StartingOffset.QuadPart;
+       }
+    }
+
+  DPRINT("DiskBuildPartitionTable() done\n");
+  if (PartitionList != NULL)
+       ExFreePool(PartitionList);
+  return(STATUS_SUCCESS);
+}
+
+
 /**********************************************************************
  * NAME                                                        EXPORTED
  *     DiskClassDeviceControl
@@ -739,7 +999,7 @@ DiskClassCreateDeviceObject(IN PDRIVER_OBJECT DriverObject,
 NTSTATUS STDCALL
 DiskClassDeviceControl(IN PDEVICE_OBJECT DeviceObject,
                       IN PIRP Irp)
-{
+{ 
   PDEVICE_EXTENSION DeviceExtension;
   PIO_STACK_LOCATION IrpStack;
   ULONG ControlCode, InputLength, OutputLength;
@@ -765,34 +1025,49 @@ 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 = TRUE;
+               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:
        DPRINT("IOCTL_DISK_GET_PARTITION_INFO\n");
+
+       if ((DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) &&
+       (DeviceExtension->DiskGeometry->MediaType == RemovableMedia))
+       {
+               /* Update a partition list for a single entry. */
+               Status = DiskBuildPartitionTable(DeviceObject,Irp);
+       }
+       
        if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength <
            sizeof(PARTITION_INFORMATION))
          {
@@ -803,7 +1078,7 @@ DiskClassDeviceControl(IN PDEVICE_OBJECT DeviceObject,
            Status = STATUS_INVALID_DEVICE_REQUEST;
          }
        else
-         {
+       {
            PPARTITION_INFORMATION PartitionInfo;
 
            PartitionInfo = (PPARTITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer;
@@ -924,18 +1199,52 @@ DiskClassDeviceControl(IN PDEVICE_OBJECT DeviceObject,
                                               DeviceExtension->DiskGeometry->SectorsPerTrack,
                                               DeviceExtension->DiskGeometry->TracksPerCylinder,
                                               PartitionList);
-               if (NT_SUCCESS(Status))
-                 {
-                   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:
@@ -944,7 +1253,7 @@ DiskClassDeviceControl(IN PDEVICE_OBJECT DeviceObject,
       case IOCTL_DISK_REQUEST_STRUCTURE:
       case IOCTL_DISK_REQUEST_DATA:
        /* If we get here, something went wrong. Inform the requestor */
-       DPRINT1("Unhandled control code: %lx\n", ControlCode);
+       DPRINT("Unhandled control code: %lx\n", ControlCode);
        Status = STATUS_INVALID_DEVICE_REQUEST;
        Information = 0;
        break;
@@ -1023,6 +1332,9 @@ DiskClassShutdownFlush(IN PDEVICE_OBJECT DeviceObject,
   Srb->TargetId = DeviceExtension->TargetId;
   Srb->Lun = DeviceExtension->Lun;
 
+  /* Set timeout */
+  Srb->TimeOutValue = DeviceExtension->TimeOutValue * 4;
+
   /* Flush write cache */
   Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
   Srb->SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER;
@@ -1168,7 +1480,7 @@ DiskClassUpdatePartitionDeviceObjects(IN PDEVICE_OBJECT DiskDeviceObject,
               DeviceExtension->PartitionLength.QuadPart))
            continue;
 
-         DPRINT1("Found matching partition entry for partition %lu\n",
+         DPRINT("Found matching partition entry for partition %lu\n",
                  DiskData->PartitionNumber);
 
          /* Found matching partition */
@@ -1191,17 +1503,17 @@ DiskClassUpdatePartitionDeviceObjects(IN PDEVICE_OBJECT DiskDeviceObject,
          /* Assign new partiton ordinal */
          DiskData->PartitionOrdinal = PartitionOrdinal;
 
-         DPRINT1("Partition ordinal %lu was assigned to partition %lu\n",
-                 DiskData->PartitionOrdinal,
-                 DiskData->PartitionNumber);
+         DPRINT("Partition ordinal %lu was assigned to partition %lu\n",
+                DiskData->PartitionOrdinal,
+                DiskData->PartitionNumber);
        }
       else
        {
          /* Delete this partition */
          DeviceExtension->PartitionLength.QuadPart = 0ULL;
 
-         DPRINT1("Deleting partition %lu\n",
-                 DiskData->PartitionNumber);
+         DPRINT("Deleting partition %lu\n",
+                DiskData->PartitionNumber);
        }
     }
 
@@ -1247,7 +1559,7 @@ DiskClassUpdatePartitionDeviceObjects(IN PDEVICE_OBJECT DiskDeviceObject,
       if (PartitionNumber == 0)
        {
          /* Create a new partition device object */
-         DPRINT1("Create new partition device object\n");
+         DPRINT("Create new partition device object\n");
 
          /* Get new partiton number */
          LastPartitionNumber++;
@@ -1270,7 +1582,7 @@ DiskClassUpdatePartitionDeviceObjects(IN PDEVICE_OBJECT DiskDeviceObject,
                                  &DeviceObject);
          if (!NT_SUCCESS(Status))
            {
-             DPRINT1("IoCreateDevice() failed (Status %lx)\n", Status);
+             DPRINT("IoCreateDevice() failed (Status %lx)\n", Status);
              continue;
            }
 
@@ -1298,7 +1610,7 @@ DiskClassUpdatePartitionDeviceObjects(IN PDEVICE_OBJECT DiskDeviceObject,
       else
        {
          /* Reuse an existing partition device object */
-         DPRINT1("Reuse an exisiting partition device object\n");
+         DPRINT("Reuse an exisiting partition device object\n");
          DiskData = (PDISK_DATA)(DeviceExtension + 1);
        }
 
@@ -1314,15 +1626,248 @@ DiskClassUpdatePartitionDeviceObjects(IN PDEVICE_OBJECT DiskDeviceObject,
       /* Update partition number in the partition list */
       PartitionEntry->PartitionNumber = PartitionNumber;
 
-      DPRINT1("Partition ordinal %lu was assigned to partition %lu\n",
-             DiskData->PartitionOrdinal,
-             DiskData->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
@@ -1352,11 +1897,19 @@ ScsiDiskUpdateFixedDiskGeometry(IN PDEVICE_EXTENSION DeviceExtension)
   UNICODE_STRING KeyName;
   UNICODE_STRING ValueName;
   HANDLE SystemKey;
+  HANDLE BusKey;
+  ULONG DiskNumber;
   ULONG Length;
+#if 0
   ULONG i;
+#endif
+  ULONG Cylinders;
+  ULONG Sectors;
+  ULONG SectorsPerTrack;
+  ULONG TracksPerCylinder;
   NTSTATUS Status;
 
-  DPRINT1("ScsiDiskUpdateFixedDiskGeometry() called\n");
+  DPRINT("ScsiDiskUpdateFixedDiskGeometry() called\n");
 
   RtlInitUnicodeString(&KeyName,
                       L"\\Registry\\Machine\\Hardware\\Description\\System");
@@ -1373,7 +1926,7 @@ ScsiDiskUpdateFixedDiskGeometry(IN PDEVICE_EXTENSION DeviceExtension)
                     &ObjectAttributes);
   if (!NT_SUCCESS(Status))
     {
-      DPRINT1("ZwOpenKey() failed (Status %lx)\n", Status);
+      DPRINT("ZwOpenKey() failed (Status %lx)\n", Status);
       return;
     }
 
@@ -1382,7 +1935,7 @@ ScsiDiskUpdateFixedDiskGeometry(IN PDEVICE_EXTENSION DeviceExtension)
                               1024);
   if (ValueBuffer == NULL)
     {
-      DPRINT1("Failed to allocate value buffer\n");
+      DPRINT("Failed to allocate value buffer\n");
       ZwClose(SystemKey);
       return;
     }
@@ -1399,14 +1952,42 @@ ScsiDiskUpdateFixedDiskGeometry(IN PDEVICE_EXTENSION DeviceExtension)
                           &Length);
   if (!NT_SUCCESS(Status))
     {
-      DPRINT1("ZwQueryValueKey() failed (Status %lx)\n", 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);
@@ -1414,21 +1995,166 @@ ScsiDiskUpdateFixedDiskGeometry(IN PDEVICE_EXTENSION DeviceExtension)
   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",
+      DPRINT("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)
+    {
+      DPRINT("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;
 
-  DPRINT1("*** System stopped ***\n");
-  for(;;);
+  if (DeviceExtension->DMActive)
+    {
+      DPRINT("FIXME: Update geometry with respect to the installed disk manager!\n");
+
+      /* FIXME: Update geometry for disk managers */
+
+    }
 
   ExFreePool(ValueBuffer);
 
-  DPRINT1("ScsiDiskUpdateFixedDiskGeometry() done\n");
+  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 */