xref: /reactos/drivers/storage/class/disk/disk.c (revision 84ccccab)
1 /*
2  * PROJECT:         ReactOS Storage Stack
3  * LICENSE:         DDK - see license.txt in the root dir
4  * FILE:            drivers/storage/disk/disk.c
5  * PURPOSE:         Disk class driver
6  * PROGRAMMERS:     Based on a source code sample from Microsoft NT4 DDK
7  */
8 
9 #include <ntddk.h>
10 #include <ntdddisk.h>
11 #include <scsi.h>
12 #include <ntddscsi.h>
13 #include <mountdev.h>
14 #include <mountmgr.h>
15 #include <ntiologc.h>
16 #include <include/class2.h>
17 #include <stdio.h>
18 
19 #define NDEBUG
20 #include <debug.h>
21 
22 
23 #ifdef POOL_TAGGING
24 #ifdef ExAllocatePool
25 #undef ExAllocatePool
26 #endif
27 #define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,'DscS')
28 #endif
29 
30 typedef enum {
31     NotInitialized,
32     Initializing,
33     Initialized
34 } PARTITION_LIST_STATE;
35 
36 //
37 // Disk device data
38 //
39 
40 typedef struct _DISK_DATA {
41 
42     //
43     // Partition chain
44     //
45 
46     PDEVICE_EXTENSION NextPartition;
47 
48     //
49     // Disk signature (from MBR)
50     //
51 
52     ULONG Signature;
53 
54     //
55     // MBR checksum
56     //
57 
58     ULONG MbrCheckSum;
59 
60     //
61     // Number of hidden sectors for BPB.
62     //
63 
64     ULONG HiddenSectors;
65 
66     //
67     // Partition number of this device object
68     //
69     // This field is set during driver initialization or when the partition
70     // is created to identify a partition to the system.
71     //
72 
73     ULONG PartitionNumber;
74 
75     //
76     // This field is the ordinal of a partition as it appears on a disk.
77     //
78 
79     ULONG PartitionOrdinal;
80 
81     //
82     // Partition type of this device object
83     //
84     // This field is set by:
85     //
86     //     1)  Initially set according to the partition list entry partition
87     //         type returned by IoReadPartitionTable.
88     //
89     //     2)  Subsequently set by the IOCTL_DISK_SET_PARTITION_INFORMATION
90     //         I/O control function when IoSetPartitionInformation function
91     //         successfully updates the partition type on the disk.
92     //
93 
94     UCHAR PartitionType;
95 
96     //
97     // Boot indicator - indicates whether this partition is a bootable (active)
98     // partition for this device
99     //
100     // This field is set according to the partition list entry boot indicator
101     // returned by IoReadPartitionTable.
102     //
103 
104     BOOLEAN BootIndicator;
105 
106     //
107     // DriveNotReady - indicates that the this device is currently not ready
108     // because there is no media in the device.
109     //
110 
111     BOOLEAN DriveNotReady;
112 
113     //
114     // State of PartitionList initialization
115     //
116 
117     PARTITION_LIST_STATE PartitionListState;
118 
119 } DISK_DATA, *PDISK_DATA;
120 
121 //
122 // Define a general structure of identifying disk controllers with bad
123 // hardware.
124 //
125 
126 typedef struct _BAD_CONTROLLER_INFORMATION {
127     PCHAR InquiryString;
128     BOOLEAN DisableTaggedQueuing;
129     BOOLEAN DisableSynchronousTransfers;
130     BOOLEAN DisableDisconnects;
131     BOOLEAN DisableWriteCache;
132 }BAD_CONTROLLER_INFORMATION, *PBAD_CONTROLLER_INFORMATION;
133 
134 BAD_CONTROLLER_INFORMATION const ScsiDiskBadControllers[] = {
135     { "TOSHIBA MK538FB         60",   TRUE,  FALSE, FALSE, FALSE },
136     { "CONNER  CP3500",               FALSE, TRUE,  FALSE, FALSE },
137     { "OLIVETTICP3500",               FALSE, TRUE,  FALSE, FALSE },
138     { "SyQuest SQ5110          CHC",  TRUE,  TRUE,  FALSE, FALSE },
139     { "SEAGATE ST41601N        0102", FALSE, TRUE,  FALSE, FALSE },
140     { "SEAGATE ST3655N",              FALSE, FALSE, FALSE, TRUE  },
141     { "SEAGATE ST3390N",              FALSE, FALSE, FALSE, TRUE  },
142     { "SEAGATE ST12550N",             FALSE, FALSE, FALSE, TRUE  },
143     { "SEAGATE ST32430N",             FALSE, FALSE, FALSE, TRUE  },
144     { "SEAGATE ST31230N",             FALSE, FALSE, FALSE, TRUE  },
145     { "SEAGATE ST15230N",             FALSE, FALSE, FALSE, TRUE  },
146     { "FUJITSU M2652S-512",           TRUE,  FALSE, FALSE, FALSE },
147     { "MAXTOR  MXT-540SL       I1.2", TRUE,  FALSE, FALSE, FALSE },
148     { "COMPAQ  PD-1",                 FALSE, TRUE,  FALSE, FALSE }
149 };
150 
151 
152 #define NUMBER_OF_BAD_CONTROLLERS (sizeof(ScsiDiskBadControllers) / sizeof(BAD_CONTROLLER_INFORMATION))
153 #define DEVICE_EXTENSION_SIZE sizeof(DEVICE_EXTENSION) + sizeof(DISK_DATA)
154 
155 #define MODE_DATA_SIZE      192
156 #define VALUE_BUFFER_SIZE  2048
157 #define SCSI_DISK_TIMEOUT    10
158 #define PARTITION0_LIST_SIZE  4
159 
160 
161 NTSTATUS
162 NTAPI
163 DriverEntry(
164     IN PDRIVER_OBJECT DriverObject,
165     IN PUNICODE_STRING RegistryPath
166     );
167 
168 BOOLEAN
169 NTAPI
170 ScsiDiskDeviceVerification(
171     IN PINQUIRYDATA InquiryData
172     );
173 
174 BOOLEAN
175 NTAPI
176 FindScsiDisks(
177     IN PDRIVER_OBJECT DriveObject,
178     IN PUNICODE_STRING RegistryPath,
179     IN PCLASS_INIT_DATA InitializationData,
180     IN PDEVICE_OBJECT PortDeviceObject,
181     IN ULONG PortNumber
182     );
183 
184 NTSTATUS
185 NTAPI
186 ScsiDiskCreateClose (
187     IN PDEVICE_OBJECT DeviceObject,
188     IN PIRP Irp
189     );
190 
191 NTSTATUS
192 NTAPI
193 ScsiDiskReadWriteVerification(
194     IN PDEVICE_OBJECT DeviceObject,
195     IN PIRP Irp
196     );
197 
198 NTSTATUS
199 NTAPI
200 ScsiDiskDeviceControl(
201     IN PDEVICE_OBJECT DeviceObject,
202     IN PIRP Irp
203     );
204 
205 VOID
206 NTAPI
207 ScsiDiskProcessError(
208     PDEVICE_OBJECT DeviceObject,
209     PSCSI_REQUEST_BLOCK Srb,
210     NTSTATUS *Status,
211     BOOLEAN *Retry
212     );
213 
214 NTSTATUS
215 NTAPI
216 ScsiDiskShutdownFlush(
217     IN PDEVICE_OBJECT DeviceObject,
218     IN PIRP Irp
219     );
220 
221 VOID
222 NTAPI
223 DisableWriteCache(
224     IN PDEVICE_OBJECT DeviceObject,
225     IN PSCSI_INQUIRY_DATA LunInfo
226     );
227 
228 BOOLEAN
229 NTAPI
230 ScsiDiskModeSelect(
231     IN PDEVICE_OBJECT DeviceObject,
232     IN PCHAR ModeSelectBuffer,
233     IN ULONG Length,
234     IN BOOLEAN SavePage
235     );
236 
237 BOOLEAN
238 NTAPI
239 IsFloppyDevice(
240     IN PDEVICE_OBJECT DeviceObject
241     );
242 
243 BOOLEAN
244 NTAPI
245 CalculateMbrCheckSum(
246     IN PDEVICE_EXTENSION DeviceExtension,
247     OUT PULONG Checksum
248     );
249 
250 BOOLEAN
251 NTAPI
252 EnumerateBusKey(
253     IN PDEVICE_EXTENSION DeviceExtension,
254     HANDLE BusKey,
255     PULONG DiskNumber
256     );
257 
258 VOID
259 NTAPI
260 UpdateGeometry(
261     IN PDEVICE_EXTENSION DeviceExtension
262     );
263 
264 NTSTATUS
265 NTAPI
266 UpdateRemovableGeometry (
267     IN PDEVICE_OBJECT DeviceObject,
268     IN PIRP Irp
269     );
270 
271 NTSTATUS
272 NTAPI
273 CreateDiskDeviceObject(
274     IN PDRIVER_OBJECT DriverObject,
275     IN PUNICODE_STRING RegistryPath,
276     IN PDEVICE_OBJECT PortDeviceObject,
277     IN ULONG PortNumber,
278     IN PULONG DeviceCount,
279     IN PIO_SCSI_CAPABILITIES PortCapabilities,
280     IN PSCSI_INQUIRY_DATA LunInfo,
281     IN PCLASS_INIT_DATA InitData
282     );
283 
284 NTSTATUS
285 NTAPI
286 CreatePartitionDeviceObjects(
287     IN PDEVICE_OBJECT PhysicalDeviceObject,
288     IN PUNICODE_STRING RegistryPath
289     );
290 
291 VOID
292 NTAPI
293 UpdateDeviceObjects(
294     IN PDEVICE_OBJECT DeviceObject,
295     IN PIRP Irp
296     );
297 
298 VOID
299 NTAPI
300 ScanForSpecial(
301     PDEVICE_OBJECT DeviceObject,
302     PSCSI_INQUIRY_DATA LunInfo,
303     PIO_SCSI_CAPABILITIES PortCapabilities
304     );
305 
306 VOID
307 NTAPI
308 ResetScsiBus(
309     IN PDEVICE_OBJECT DeviceObject
310     );
311 
312 NTSTATUS
313 NTAPI
314 ScsiDiskFileSystemControl(PDEVICE_OBJECT DeviceObject,
315                           PIRP Irp);
316 
317 #ifdef ALLOC_PRAGMA
318 #pragma alloc_text(PAGE, DriverEntry)
319 #pragma alloc_text(PAGE, FindScsiDisks)
320 #pragma alloc_text(PAGE, CreateDiskDeviceObject)
321 #pragma alloc_text(PAGE, CalculateMbrCheckSum)
322 #pragma alloc_text(PAGE, EnumerateBusKey)
323 #pragma alloc_text(PAGE, UpdateGeometry)
324 #pragma alloc_text(PAGE, IsFloppyDevice)
325 #pragma alloc_text(PAGE, ScanForSpecial)
326 #pragma alloc_text(PAGE, ScsiDiskDeviceControl)
327 #pragma alloc_text(PAGE, ScsiDiskModeSelect)
328 #endif
329 
330 
331 NTSTATUS
332 NTAPI
333 DriverEntry(
334     IN PDRIVER_OBJECT DriverObject,
335     IN PUNICODE_STRING RegistryPath
336     )
337 
338 /*++
339 
340 Routine Description:
341 
342     This routine initializes the SCSI hard disk class driver.
343 
344 Arguments:
345 
346     DriverObject - Pointer to driver object created by system.
347 
348     RegistryPath - Pointer to the name of the services node for this driver.
349 
350 Return Value:
351 
352     The function value is the final status from the initialization operation.
353 
354 --*/
355 
356 {
357     CLASS_INIT_DATA InitializationData;
358 
359     //
360     // Zero InitData
361     //
362 
363     RtlZeroMemory (&InitializationData, sizeof(CLASS_INIT_DATA));
364 
365     //
366     // Set sizes
367     //
368 
369     InitializationData.InitializationDataSize = sizeof(CLASS_INIT_DATA);
370     InitializationData.DeviceExtensionSize = DEVICE_EXTENSION_SIZE;
371 
372     InitializationData.DeviceType = FILE_DEVICE_DISK;
373     InitializationData.DeviceCharacteristics = 0;
374 
375     //
376     // Set entry points
377     //
378 
379     InitializationData.ClassError = ScsiDiskProcessError;
380     InitializationData.ClassReadWriteVerification = ScsiDiskReadWriteVerification;
381     InitializationData.ClassFindDevices = FindScsiDisks;
382     InitializationData.ClassFindDeviceCallBack = ScsiDiskDeviceVerification;
383     InitializationData.ClassDeviceControl = ScsiDiskDeviceControl;
384     InitializationData.ClassShutdownFlush = ScsiDiskShutdownFlush;
385     InitializationData.ClassCreateClose = NULL;
386 
387     //
388     // Call the class init routine
389     //
390 
391     return ScsiClassInitialize( DriverObject, RegistryPath, &InitializationData);
392 
393 } // end DriverEntry()
394 
395 
396 
397 BOOLEAN
398 NTAPI
399 ScsiDiskDeviceVerification(
400     IN PINQUIRYDATA InquiryData
401     )
402 
403 /*++
404 
405 Routine Description:
406 
407     This routine checks InquiryData for the correct device type and qualifier.
408 
409 Arguments:
410 
411     InquiryData - Pointer to the inquiry data for the device in question.
412 
413 Return Value:
414 
415     True is returned if the correct device type is found.
416 
417 --*/
418 {
419 
420     if (((InquiryData->DeviceType == DIRECT_ACCESS_DEVICE) ||
421         (InquiryData->DeviceType == OPTICAL_DEVICE)) &&
422         InquiryData->DeviceTypeQualifier == 0) {
423 
424         return TRUE;
425 
426     } else {
427         return FALSE;
428     }
429 }
430 
431 
432 BOOLEAN
433 NTAPI
434 FindScsiDisks(
435     IN PDRIVER_OBJECT DriverObject,
436     IN PUNICODE_STRING RegistryPath,
437     IN PCLASS_INIT_DATA InitializationData,
438     IN PDEVICE_OBJECT PortDeviceObject,
439     IN ULONG PortNumber
440     )
441 
442 /*++
443 
444 Routine Description:
445 
446     This routine gets a port drivers capabilities, obtains the
447     inquiry data, searches the SCSI bus for the port driver and creates
448     the device objects for the disks found.
449 
450 Arguments:
451 
452     DriverObject - Pointer to driver object created by system.
453 
454     PortDeviceObject - Device object use to send requests to port driver.
455 
456     PortNumber - Number for port driver.  Used to pass on to
457                  CreateDiskDeviceObjects() and create device objects.
458 
459 Return Value:
460 
461     True is returned if one disk was found and successfully created.
462 
463 --*/
464 
465 {
466     PIO_SCSI_CAPABILITIES portCapabilities;
467     PULONG diskCount;
468     PCONFIGURATION_INFORMATION configurationInformation;
469     PCHAR buffer;
470     PSCSI_INQUIRY_DATA lunInfo;
471     PSCSI_ADAPTER_BUS_INFO  adapterInfo;
472     PINQUIRYDATA inquiryData;
473     ULONG scsiBus;
474     ULONG adapterDisk;
475     NTSTATUS status;
476     BOOLEAN foundOne = FALSE;
477 
478     PAGED_CODE();
479 
480     //
481     // Call port driver to get adapter capabilities.
482     //
483 
484     status = ScsiClassGetCapabilities(PortDeviceObject, &portCapabilities);
485 
486     if (!NT_SUCCESS(status)) {
487         DebugPrint((1,"FindScsiDevices: ScsiClassGetCapabilities failed\n"));
488         return(FALSE);
489     }
490 
491     //
492     // Call port driver to get inquiry information to find disks.
493     //
494 
495     status = ScsiClassGetInquiryData(PortDeviceObject, (PSCSI_ADAPTER_BUS_INFO *) &buffer);
496 
497     if (!NT_SUCCESS(status)) {
498         DebugPrint((1,"FindScsiDevices: ScsiClassGetInquiryData failed\n"));
499         return(FALSE);
500     }
501 
502     //
503     // Do a quick scan of the devices on this adapter to determine how many
504     // disks are on this adapter.  This is used to determine the number of
505     // SRB zone elements to allocate.
506     //
507 
508     adapterInfo = (PVOID) buffer;
509 
510     adapterDisk = ScsiClassFindUnclaimedDevices(InitializationData, adapterInfo);
511 
512     //
513     // Allocate a zone of SRB for disks on this adapter.
514     //
515 
516     if (adapterDisk == 0) {
517 
518         //
519         // No free disks were found.
520         //
521 
522         return(FALSE);
523     }
524 
525     //
526     // Get the number of disks already initialized.
527     //
528 
529     configurationInformation = IoGetConfigurationInformation();
530     diskCount = &configurationInformation->DiskCount;
531 
532     //
533     // For each SCSI bus this adapter supports ...
534     //
535 
536     for (scsiBus=0; scsiBus < (ULONG)adapterInfo->NumberOfBuses; scsiBus++) {
537 
538         //
539         // Get the SCSI bus scan data for this bus.
540         //
541 
542         lunInfo = (PVOID) (buffer + adapterInfo->BusData[scsiBus].InquiryDataOffset);
543 
544         //
545         // Search list for unclaimed disk devices.
546         //
547 
548         while (adapterInfo->BusData[scsiBus].InquiryDataOffset) {
549 
550             inquiryData = (PVOID)lunInfo->InquiryData;
551 
552             if (((inquiryData->DeviceType == DIRECT_ACCESS_DEVICE) ||
553                 (inquiryData->DeviceType == OPTICAL_DEVICE)) &&
554                 inquiryData->DeviceTypeQualifier == 0 &&
555                 (!lunInfo->DeviceClaimed)) {
556 
557                 DebugPrint((1,
558                             "FindScsiDevices: Vendor string is %.24s\n",
559                             inquiryData->VendorId));
560 
561                 //
562                 // Create device objects for disk
563                 //
564 
565                 status = CreateDiskDeviceObject(DriverObject,
566                                                 RegistryPath,
567                                                 PortDeviceObject,
568                                                 PortNumber,
569                                                 diskCount,
570                                                 portCapabilities,
571                                                 lunInfo,
572                                                 InitializationData);
573 
574                 if (NT_SUCCESS(status)) {
575 
576                     //
577                     // Increment system disk device count.
578                     //
579 
580                     (*diskCount)++;
581                     foundOne = TRUE;
582 
583                 }
584             }
585 
586             //
587             // Get next LunInfo.
588             //
589 
590             if (lunInfo->NextInquiryDataOffset == 0) {
591                 break;
592             }
593 
594             lunInfo = (PVOID) (buffer + lunInfo->NextInquiryDataOffset);
595 
596         }
597     }
598 
599     //
600     // Buffer is allocated by ScsiClassGetInquiryData and must be free returning.
601     //
602 
603     ExFreePool(buffer);
604 
605     return(foundOne);
606 
607 } // end FindScsiDisks()
608 
609 
610 NTSTATUS
611 NTAPI
612 CreateDiskDeviceObject(
613     IN PDRIVER_OBJECT DriverObject,
614     IN PUNICODE_STRING RegistryPath,
615     IN PDEVICE_OBJECT PortDeviceObject,
616     IN ULONG PortNumber,
617     IN PULONG DeviceCount,
618     IN PIO_SCSI_CAPABILITIES PortCapabilities,
619     IN PSCSI_INQUIRY_DATA LunInfo,
620     IN PCLASS_INIT_DATA InitData
621     )
622 
623 /*++
624 
625 Routine Description:
626 
627     This routine creates an object for the physical device and then searches
628     the device for partitions and creates an object for each partition.
629 
630 Arguments:
631 
632     DriverObject - Pointer to driver object created by system.
633 
634     PortDeviceObject - Miniport device object.
635 
636     PortNumber   - port number.  Used in creating disk objects.
637 
638     DeviceCount  - Number of previously installed devices.
639 
640     PortCapabilities - Capabilities of this SCSI port.
641 
642     LunInfo      - LUN specific information.
643 
644 Return Value:
645 
646     NTSTATUS
647 
648 --*/
649 {
650     CCHAR          ntNameBuffer[MAXIMUM_FILENAME_LENGTH];
651     STRING         ntNameString;
652     UNICODE_STRING ntUnicodeString;
653     OBJECT_ATTRIBUTES objectAttributes;
654     HANDLE         handle;
655     NTSTATUS       status;
656     PDEVICE_OBJECT deviceObject = NULL;
657     //PDEVICE_OBJECT physicalDevice;
658     PDISK_GEOMETRY_EX diskGeometry = NULL;
659     PDEVICE_EXTENSION deviceExtension = NULL;
660     //PDEVICE_EXTENSION physicalDeviceExtension;
661     UCHAR          pathId = LunInfo->PathId;
662     UCHAR          targetId = LunInfo->TargetId;
663     UCHAR          lun = LunInfo->Lun;
664     //BOOLEAN        writeCache;
665     PVOID          senseData = NULL;
666     //ULONG          srbFlags;
667     ULONG          timeOut = 0;
668     BOOLEAN        srbListInitialized = FALSE;
669 
670 
671     PAGED_CODE();
672 
673     //
674     // Set up an object directory to contain the objects for this
675     // device and all its partitions.
676     //
677 
678     sprintf(ntNameBuffer,
679             "\\Device\\Harddisk%lu",
680             *DeviceCount);
681 
682     RtlInitString(&ntNameString,
683                   ntNameBuffer);
684 
685     status = RtlAnsiStringToUnicodeString(&ntUnicodeString,
686                                           &ntNameString,
687                                           TRUE);
688 
689     if (!NT_SUCCESS(status)) {
690         return(status);
691     }
692 
693     InitializeObjectAttributes(&objectAttributes,
694                                &ntUnicodeString,
695                                OBJ_CASE_INSENSITIVE | OBJ_PERMANENT,
696                                NULL,
697                                NULL);
698 
699     status = ZwCreateDirectoryObject(&handle,
700                                      DIRECTORY_ALL_ACCESS,
701                                      &objectAttributes);
702 
703     RtlFreeUnicodeString(&ntUnicodeString);
704 
705     if (!NT_SUCCESS(status)) {
706 
707         DebugPrint((1,
708                     "CreateDiskDeviceObjects: Could not create directory %s\n",
709                     ntNameBuffer));
710 
711         return(status);
712     }
713 
714     //
715     // Claim the device.
716     //
717 
718     status = ScsiClassClaimDevice(PortDeviceObject,
719                                   LunInfo,
720                                   FALSE,
721                                   &PortDeviceObject);
722 
723     if (!NT_SUCCESS(status)) {
724         ZwMakeTemporaryObject(handle);
725         ZwClose(handle);
726         return status;
727     }
728 
729     //
730     // Create a device object for this device. Each physical disk will
731     // have at least one device object. The required device object
732     // describes the entire device. Its directory path is
733     // \Device\HarddiskN\Partition0, where N = device number.
734     //
735 
736     sprintf(ntNameBuffer,
737             "\\Device\\Harddisk%lu\\Partition0",
738             *DeviceCount);
739 
740 
741     status = ScsiClassCreateDeviceObject(DriverObject,
742                                          ntNameBuffer,
743                                          NULL,
744                                          &deviceObject,
745                                          InitData);
746 
747     if (!NT_SUCCESS(status)) {
748 
749         DebugPrint((1,
750                     "CreateDiskDeviceObjects: Can not create device object %s\n",
751                     ntNameBuffer));
752 
753         goto CreateDiskDeviceObjectsExit;
754     }
755 
756     //
757     // Indicate that IRPs should include MDLs for data transfers.
758     //
759 
760     deviceObject->Flags |= DO_DIRECT_IO;
761 
762     //
763     // Check if this is during initialization. If not indicate that
764     // system initialization already took place and this disk is ready
765     // to be accessed.
766     //
767 
768     if (!RegistryPath) {
769         deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
770     }
771 
772     //
773     // Check for removable media support.
774     //
775 
776     if (((PINQUIRYDATA)LunInfo->InquiryData)->RemovableMedia) {
777         deviceObject->Characteristics |= FILE_REMOVABLE_MEDIA;
778     }
779 
780     //
781     // Set up required stack size in device object.
782     //
783 
784     deviceObject->StackSize = (CCHAR)PortDeviceObject->StackSize + 1;
785 
786     deviceExtension = deviceObject->DeviceExtension;
787 
788     //
789     // Allocate spinlock for split request completion.
790     //
791 
792     KeInitializeSpinLock(&deviceExtension->SplitRequestSpinLock);
793 
794     //
795     // Initialize lock count to zero. The lock count is used to
796     // disable the ejection mechanism on devices that support
797     // removable media. Only the lock count in the physical
798     // device extension is used.
799     //
800 
801     deviceExtension->LockCount = 0;
802 
803     //
804     // Save system disk number.
805     //
806 
807     deviceExtension->DeviceNumber = *DeviceCount;
808 
809     //
810     // Copy port device object pointer to the device extension.
811     //
812 
813     deviceExtension->PortDeviceObject = PortDeviceObject;
814 
815     //
816     // Set the alignment requirements for the device based on the
817     // host adapter requirements
818     //
819 
820     if (PortDeviceObject->AlignmentRequirement > deviceObject->AlignmentRequirement) {
821         deviceObject->AlignmentRequirement = PortDeviceObject->AlignmentRequirement;
822     }
823 
824     //
825     // This is the physical device object.
826     //
827 
828     //physicalDevice = deviceObject;
829     //physicalDeviceExtension = deviceExtension;
830 
831     //
832     // Save address of port driver capabilities.
833     //
834 
835     deviceExtension->PortCapabilities = PortCapabilities;
836 
837     //
838     // Build the lookaside list for srb's for the physical disk. Should only
839     // need a couple.
840     //
841 
842     ScsiClassInitializeSrbLookasideList(deviceExtension,
843                                         PARTITION0_LIST_SIZE);
844 
845     srbListInitialized = TRUE;
846 
847     //
848     // Initialize the srb flags.
849     //
850 
851     if (((PINQUIRYDATA)LunInfo->InquiryData)->CommandQueue &&
852         PortCapabilities->TaggedQueuing) {
853 
854         deviceExtension->SrbFlags  = SRB_FLAGS_QUEUE_ACTION_ENABLE;
855 
856     } else {
857 
858         deviceExtension->SrbFlags  = 0;
859 
860     }
861 
862     //
863     // Allow queued requests if this is not removable media.
864     //
865 
866     if (!(deviceObject->Characteristics & FILE_REMOVABLE_MEDIA)) {
867 
868         deviceExtension->SrbFlags |= SRB_FLAGS_NO_QUEUE_FREEZE;
869 
870     }
871 
872     //
873     // Look for controller that require special flags.
874     //
875 
876     ScanForSpecial(deviceObject,
877                     LunInfo,
878                     PortCapabilities);
879 
880     //srbFlags = deviceExtension->SrbFlags;
881 
882     //
883     // Allocate buffer for drive geometry.
884     //
885 
886     diskGeometry = ExAllocatePool(NonPagedPool, sizeof(DISK_GEOMETRY_EX));
887 
888     if (diskGeometry == NULL) {
889 
890         DebugPrint((1,
891            "CreateDiskDeviceObjects: Can not allocate disk geometry buffer\n"));
892         status = STATUS_INSUFFICIENT_RESOURCES;
893         goto CreateDiskDeviceObjectsExit;
894     }
895 
896     deviceExtension->DiskGeometry = diskGeometry;
897 
898     //
899     // Allocate request sense buffer.
900     //
901 
902     senseData = ExAllocatePool(NonPagedPoolCacheAligned, SENSE_BUFFER_SIZE);
903 
904     if (senseData == NULL) {
905 
906         //
907         // The buffer can not be allocated.
908         //
909 
910         DebugPrint((1,
911            "CreateDiskDeviceObjects: Can not allocate request sense buffer\n"));
912 
913         status = STATUS_INSUFFICIENT_RESOURCES;
914         goto CreateDiskDeviceObjectsExit;
915     }
916 
917     //
918     // Set the sense data pointer in the device extension.
919     //
920 
921     deviceExtension->SenseData = senseData;
922 
923     //
924     // Physical device object will describe the entire
925     // device, starting at byte offset 0.
926     //
927 
928     deviceExtension->StartingOffset.QuadPart = (LONGLONG)(0);
929 
930     //
931     // TargetId/LUN describes a device location on the SCSI bus.
932     // This information comes from the inquiry buffer.
933     //
934 
935     deviceExtension->PortNumber = (UCHAR)PortNumber;
936     deviceExtension->PathId = pathId;
937     deviceExtension->TargetId = targetId;
938     deviceExtension->Lun = lun;
939 
940     //
941     // Set timeout value in seconds.
942     //
943 
944     timeOut = ScsiClassQueryTimeOutRegistryValue(RegistryPath);
945     if (timeOut) {
946         deviceExtension->TimeOutValue = timeOut;
947     } else {
948         deviceExtension->TimeOutValue = SCSI_DISK_TIMEOUT;
949     }
950 
951     //
952     // Back pointer to device object.
953     //
954 
955     deviceExtension->DeviceObject = deviceObject;
956 
957     //
958     // If this is a removable device, then make sure it is not a floppy.
959     // Perform a mode sense command to determine the media type. Note
960     // IsFloppyDevice also checks for write cache enabled.
961     //
962 
963     if (IsFloppyDevice(deviceObject) && deviceObject->Characteristics & FILE_REMOVABLE_MEDIA &&
964         (((PINQUIRYDATA)LunInfo->InquiryData)->DeviceType == DIRECT_ACCESS_DEVICE)) {
965 
966         status = STATUS_NO_SUCH_DEVICE;
967         goto CreateDiskDeviceObjectsExit;
968     }
969 
970     DisableWriteCache(deviceObject,LunInfo);
971 
972     //writeCache = deviceExtension->DeviceFlags & DEV_WRITE_CACHE;
973 
974     //
975     // NOTE: At this point one device object has been successfully created.
976     // from here on out return success.
977     //
978 
979     //
980     // Do READ CAPACITY. This SCSI command
981     // returns the number of bytes on a device.
982     // Device extension is updated with device size.
983     //
984 
985     status = ScsiClassReadDriveCapacity(deviceObject);
986 
987     //
988     // If the read capacity failed then just return, unless this is a
989     // removable disk where a device object partition needs to be created.
990     //
991 
992     if (!NT_SUCCESS(status) &&
993         !(deviceObject->Characteristics & FILE_REMOVABLE_MEDIA)) {
994 
995         DebugPrint((1,
996             "CreateDiskDeviceObjects: Can't read capacity for device %s\n",
997             ntNameBuffer));
998 
999         return(STATUS_SUCCESS);
1000 
1001     } else {
1002 
1003         //
1004         // Make sure the volume verification bit is off so that
1005         // IoReadPartitionTable will work.
1006         //
1007 
1008         deviceObject->Flags &= ~DO_VERIFY_VOLUME;
1009     }
1010 
1011     status = CreatePartitionDeviceObjects(deviceObject, RegistryPath);
1012 
1013     if (NT_SUCCESS(status))
1014         return STATUS_SUCCESS;
1015 
1016 
1017 CreateDiskDeviceObjectsExit:
1018 
1019     //
1020     // Release the device since an error occurred.
1021     //
1022 
1023     ScsiClassClaimDevice(PortDeviceObject,
1024                          LunInfo,
1025                          TRUE,
1026                          NULL);
1027 
1028     if (diskGeometry != NULL) {
1029         ExFreePool(diskGeometry);
1030     }
1031 
1032     if (senseData != NULL) {
1033         ExFreePool(senseData);
1034     }
1035 
1036     if (deviceObject != NULL) {
1037 
1038         if (srbListInitialized) {
1039             ExDeleteNPagedLookasideList(&deviceExtension->SrbLookasideListHead);
1040         }
1041 
1042         IoDeleteDevice(deviceObject);
1043     }
1044 
1045     //
1046     // Delete directory and return.
1047     //
1048 
1049     if (!NT_SUCCESS(status)) {
1050         ZwMakeTemporaryObject(handle);
1051     }
1052 
1053     ZwClose(handle);
1054 
1055     return(status);
1056 
1057 } // end CreateDiskDeviceObjects()
1058 
1059 
1060 NTSTATUS
1061 NTAPI
1062 CreatePartitionDeviceObjects(
1063     IN PDEVICE_OBJECT PhysicalDeviceObject,
1064     IN PUNICODE_STRING RegistryPath
1065     )
1066 {
1067     CCHAR          ntNameBuffer[MAXIMUM_FILENAME_LENGTH];
1068     ULONG          partitionNumber = 0;
1069     NTSTATUS       status;
1070     PDEVICE_OBJECT deviceObject = NULL;
1071     PDISK_GEOMETRY_EX diskGeometry = NULL;
1072     PDRIVE_LAYOUT_INFORMATION partitionList = NULL;
1073     PDEVICE_EXTENSION deviceExtension;
1074     PDEVICE_EXTENSION physicalDeviceExtension;
1075     PCLASS_INIT_DATA initData = NULL;
1076     PDISK_DATA     diskData;
1077     PDISK_DATA     physicalDiskData;
1078     ULONG          bytesPerSector;
1079     UCHAR          sectorShift;
1080     ULONG          srbFlags;
1081     ULONG          dmByteSkew = 0;
1082     PULONG         dmSkew;
1083     BOOLEAN        dmActive = FALSE;
1084     ULONG          numberListElements = 0;
1085 
1086 
1087     //
1088     // Get physical device geometry information for partition table reads.
1089     //
1090 
1091     physicalDeviceExtension = PhysicalDeviceObject->DeviceExtension;
1092     diskGeometry = physicalDeviceExtension->DiskGeometry;
1093     bytesPerSector = diskGeometry->Geometry.BytesPerSector;
1094 
1095     //
1096     // Make sure sector size is not zero.
1097     //
1098 
1099     if (bytesPerSector == 0) {
1100 
1101         //
1102         // Default sector size for disk is 512.
1103         //
1104 
1105         bytesPerSector = diskGeometry->Geometry.BytesPerSector = 512;
1106     }
1107 
1108     sectorShift = physicalDeviceExtension->SectorShift;
1109 
1110     //
1111     // Set pointer to disk data area that follows device extension.
1112     //
1113 
1114     diskData = (PDISK_DATA)(physicalDeviceExtension + 1);
1115     diskData->PartitionListState = Initializing;
1116 
1117     //
1118     // Determine is DM Driver is loaded on an IDE drive that is
1119     // under control of Atapi - this could be either a crashdump or
1120     // an Atapi device is sharing the controller with an IDE disk.
1121     //
1122 
1123     HalExamineMBR(PhysicalDeviceObject,
1124                   physicalDeviceExtension->DiskGeometry->Geometry.BytesPerSector,
1125                   (ULONG)0x54,
1126                   (PVOID)&dmSkew);
1127 
1128     if (dmSkew) {
1129 
1130         //
1131         // Update the device extension, so that the call to IoReadPartitionTable
1132         // will get the correct information. Any I/O to this disk will have
1133         // to be skewed by *dmSkew sectors aka DMByteSkew.
1134         //
1135 
1136         physicalDeviceExtension->DMSkew = *dmSkew;
1137         physicalDeviceExtension->DMActive = TRUE;
1138         physicalDeviceExtension->DMByteSkew = physicalDeviceExtension->DMSkew * bytesPerSector;
1139 
1140         //
1141         // Save away the information that we need, since this deviceExtension will soon be
1142         // blown away.
1143         //
1144 
1145         dmActive = TRUE;
1146         dmByteSkew = physicalDeviceExtension->DMByteSkew;
1147 
1148     }
1149 
1150     //
1151     // Create objects for all the partitions on the device.
1152     //
1153 
1154     status = IoReadPartitionTable(PhysicalDeviceObject,
1155                                   physicalDeviceExtension->DiskGeometry->Geometry.BytesPerSector,
1156                                   TRUE,
1157                                   (PVOID)&partitionList);
1158 
1159     //
1160     // If the I/O read partition table failed and this is a removable device,
1161     // then fix up the partition list to make it look like there is one
1162     // zero length partition.
1163     //
1164     DPRINT("IoReadPartitionTable() status: 0x%08X\n", status);
1165     if ((!NT_SUCCESS(status) || partitionList->PartitionCount == 0) &&
1166         PhysicalDeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) {
1167 
1168         if (!NT_SUCCESS(status)) {
1169 
1170             //
1171             // Remember this disk is not ready.
1172             //
1173 
1174             diskData->DriveNotReady = TRUE;
1175 
1176         } else {
1177 
1178             //
1179             // Free the partition list allocated by IoReadPartitionTable.
1180             //
1181 
1182             ExFreePool(partitionList);
1183         }
1184 
1185         //
1186         // Allocate and zero a partition list.
1187         //
1188 
1189         partitionList = ExAllocatePool(NonPagedPool, sizeof(*partitionList));
1190 
1191 
1192         if (partitionList != NULL) {
1193 
1194             RtlZeroMemory( partitionList, sizeof( *partitionList ));
1195 
1196             //
1197             // Set the partition count to one and the status to success
1198             // so one device object will be created. Set the partition type
1199             // to a bogus value.
1200             //
1201 
1202             partitionList->PartitionCount = 1;
1203 
1204             status = STATUS_SUCCESS;
1205         }
1206     }
1207 
1208     if (NT_SUCCESS(status)) {
1209 
1210         //
1211         // Record disk signature.
1212         //
1213 
1214         diskData->Signature = partitionList->Signature;
1215 
1216         //
1217         // If disk signature is zero, then calculate the MBR checksum.
1218         //
1219 
1220         if (!diskData->Signature) {
1221 
1222             if (!CalculateMbrCheckSum(physicalDeviceExtension,
1223                                       &diskData->MbrCheckSum)) {
1224 
1225                 DebugPrint((1,
1226                             "SCSIDISK: Can't calculate MBR checksum for disk %x\n",
1227                             physicalDeviceExtension->DeviceNumber));
1228             } else {
1229 
1230                 DebugPrint((2,
1231                            "SCSIDISK: MBR checksum for disk %x is %x\n",
1232                            physicalDeviceExtension->DeviceNumber,
1233                            diskData->MbrCheckSum));
1234             }
1235         }
1236 
1237         //
1238         // Check the registry and determine if the BIOS knew about this drive.  If
1239         // it did then update the geometry with the BIOS information.
1240         //
1241 
1242         UpdateGeometry(physicalDeviceExtension);
1243 
1244         srbFlags = physicalDeviceExtension->SrbFlags;
1245 
1246         initData = ExAllocatePool(NonPagedPool, sizeof(CLASS_INIT_DATA));
1247         if (!initData)
1248         {
1249             DebugPrint((1,
1250                         "Disk.CreatePartitionDeviceObjects - Allocation of initData failed\n"));
1251 
1252             status = STATUS_INSUFFICIENT_RESOURCES;
1253             goto CreatePartitionDeviceObjectsExit;
1254         }
1255 
1256         RtlZeroMemory(initData, sizeof(CLASS_INIT_DATA));
1257 
1258         initData->InitializationDataSize     = sizeof(CLASS_INIT_DATA);
1259         initData->DeviceExtensionSize        = DEVICE_EXTENSION_SIZE;
1260         initData->DeviceType                 = FILE_DEVICE_DISK;
1261         initData->DeviceCharacteristics      = PhysicalDeviceObject->Characteristics;
1262         initData->ClassError                 = physicalDeviceExtension->ClassError;
1263         initData->ClassReadWriteVerification = physicalDeviceExtension->ClassReadWriteVerification;
1264         initData->ClassFindDevices           = physicalDeviceExtension->ClassFindDevices;
1265         initData->ClassDeviceControl         = physicalDeviceExtension->ClassDeviceControl;
1266         initData->ClassShutdownFlush         = physicalDeviceExtension->ClassShutdownFlush;
1267         initData->ClassCreateClose           = physicalDeviceExtension->ClassCreateClose;
1268         initData->ClassStartIo               = physicalDeviceExtension->ClassStartIo;
1269 
1270         //
1271         // Create device objects for the device partitions (if any).
1272         // PartitionCount includes physical device partition 0,
1273         // so only one partition means no objects to create.
1274         //
1275 
1276         DebugPrint((2,
1277                     "CreateDiskDeviceObjects: Number of partitions is %d\n",
1278                     partitionList->PartitionCount));
1279 
1280         for (partitionNumber = 0; partitionNumber <
1281             partitionList->PartitionCount; partitionNumber++) {
1282 
1283             //
1284             // Create partition object and set up partition parameters.
1285             //
1286 
1287             sprintf(ntNameBuffer,
1288                     "\\Device\\Harddisk%lu\\Partition%lu",
1289                     physicalDeviceExtension->DeviceNumber,
1290                     partitionNumber + 1);
1291 
1292             DebugPrint((2,
1293                         "CreateDiskDeviceObjects: Create device object %s\n",
1294                         ntNameBuffer));
1295 
1296             status = ScsiClassCreateDeviceObject(PhysicalDeviceObject->DriverObject,
1297                                                  ntNameBuffer,
1298                                                  PhysicalDeviceObject,
1299                                                  &deviceObject,
1300                                                  initData);
1301 
1302             if (!NT_SUCCESS(status)) {
1303 
1304                 DebugPrint((1, "CreateDiskDeviceObjects: Can't create device object for %s\n", ntNameBuffer));
1305 
1306                 break;
1307             }
1308 
1309             //
1310             // Set up device object fields.
1311             //
1312 
1313             deviceObject->Flags |= DO_DIRECT_IO;
1314 
1315             //
1316             // Check if this is during initialization. If not indicate that
1317             // system initialization already took place and this disk is ready
1318             // to be accessed.
1319             //
1320 
1321             if (!RegistryPath) {
1322                 deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
1323             }
1324 
1325             deviceObject->StackSize = (CCHAR)physicalDeviceExtension->PortDeviceObject->StackSize + 1;
1326 
1327             //
1328             // Set up device extension fields.
1329             //
1330 
1331             deviceExtension = deviceObject->DeviceExtension;
1332 
1333             if (dmActive) {
1334 
1335                 //
1336                 // Restore any saved DM values.
1337                 //
1338 
1339                 deviceExtension->DMByteSkew = dmByteSkew;
1340                 deviceExtension->DMSkew     = *dmSkew;
1341                 deviceExtension->DMActive   = TRUE;
1342 
1343             }
1344 
1345             //
1346             // Link new device extension to previous disk data
1347             // to support dynamic partitioning.
1348             //
1349 
1350             diskData->NextPartition = deviceExtension;
1351 
1352             //
1353             // Get pointer to new disk data.
1354             //
1355 
1356             diskData = (PDISK_DATA)(deviceExtension + 1);
1357 
1358             //
1359             // Set next partition pointer to NULL in case this is the
1360             // last partition.
1361             //
1362 
1363             diskData->NextPartition = NULL;
1364 
1365             //
1366             // Allocate spinlock for zoning for split-request completion.
1367             //
1368 
1369             KeInitializeSpinLock(&deviceExtension->SplitRequestSpinLock);
1370 
1371             //
1372             // Copy port device object pointer to device extension.
1373             //
1374 
1375             deviceExtension->PortDeviceObject = physicalDeviceExtension->PortDeviceObject;
1376 
1377             //
1378             // Set the alignment requirements for the device based on the
1379             // host adapter requirements
1380             //
1381 
1382             if (physicalDeviceExtension->PortDeviceObject->AlignmentRequirement > deviceObject->AlignmentRequirement) {
1383                 deviceObject->AlignmentRequirement = physicalDeviceExtension->PortDeviceObject->AlignmentRequirement;
1384             }
1385 
1386 
1387             if (srbFlags & SRB_FLAGS_QUEUE_ACTION_ENABLE) {
1388                 numberListElements = 30;
1389             } else {
1390                 numberListElements = 8;
1391             }
1392 
1393             //
1394             // Build the lookaside list for srb's for this partition based on
1395             // whether the adapter and disk can do tagged queueing.
1396             //
1397 
1398             ScsiClassInitializeSrbLookasideList(deviceExtension,
1399                                                 numberListElements);
1400 
1401             deviceExtension->SrbFlags = srbFlags;
1402 
1403             //
1404             // Set the sense-data pointer in the device extension.
1405             //
1406 
1407             deviceExtension->SenseData        = physicalDeviceExtension->SenseData;
1408             deviceExtension->PortCapabilities = physicalDeviceExtension->PortCapabilities;
1409             deviceExtension->DiskGeometry     = diskGeometry;
1410             diskData->PartitionOrdinal        = diskData->PartitionNumber = partitionNumber + 1;
1411             diskData->PartitionType           = partitionList->PartitionEntry[partitionNumber].PartitionType;
1412             diskData->BootIndicator           = partitionList->PartitionEntry[partitionNumber].BootIndicator;
1413 
1414             DebugPrint((2, "CreateDiskDeviceObjects: Partition type is %x\n",
1415                 diskData->PartitionType));
1416 
1417             deviceExtension->StartingOffset  = partitionList->PartitionEntry[partitionNumber].StartingOffset;
1418             deviceExtension->PartitionLength = partitionList->PartitionEntry[partitionNumber].PartitionLength;
1419             diskData->HiddenSectors          = partitionList->PartitionEntry[partitionNumber].HiddenSectors;
1420             deviceExtension->PortNumber      = physicalDeviceExtension->PortNumber;
1421             deviceExtension->PathId          = physicalDeviceExtension->PathId;
1422             deviceExtension->TargetId        = physicalDeviceExtension->TargetId;
1423             deviceExtension->Lun             = physicalDeviceExtension->Lun;
1424 
1425             //
1426             // Check for removable media support.
1427             //
1428 
1429             if (PhysicalDeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) {
1430                 deviceObject->Characteristics |= FILE_REMOVABLE_MEDIA;
1431             }
1432 
1433             //
1434             // Set timeout value in seconds.
1435             //
1436 
1437             deviceExtension->TimeOutValue = physicalDeviceExtension->TimeOutValue;
1438             deviceExtension->DiskGeometry->Geometry.BytesPerSector = bytesPerSector;
1439             deviceExtension->SectorShift  = sectorShift;
1440             deviceExtension->DeviceObject = deviceObject;
1441             deviceExtension->DeviceFlags |= physicalDeviceExtension->DeviceFlags;
1442 
1443         } // end for (partitionNumber) ...
1444 
1445         //
1446         // Free the buffer allocated by reading the
1447         // partition table.
1448         //
1449 
1450         ExFreePool(partitionList);
1451 
1452         if (dmSkew) {
1453             ExFreePool(dmSkew);
1454         }
1455 
1456     } else {
1457 
1458 CreatePartitionDeviceObjectsExit:
1459 
1460         if (partitionList) {
1461             ExFreePool(partitionList);
1462         }
1463         if (initData) {
1464             ExFreePool(initData);
1465         }
1466 
1467         if (dmSkew) {
1468             ExFreePool(dmSkew);
1469         }
1470 
1471         return status;
1472 
1473     } // end if...else
1474 
1475 
1476     physicalDiskData = (PDISK_DATA)(physicalDeviceExtension + 1);
1477     physicalDiskData->PartitionListState = Initialized;
1478 
1479     return(STATUS_SUCCESS);
1480 
1481 
1482 } // end CreatePartitionDeviceObjects()
1483 
1484 
1485 NTSTATUS
1486 NTAPI
1487 ScsiDiskReadWriteVerification(
1488     IN PDEVICE_OBJECT DeviceObject,
1489     IN PIRP Irp
1490     )
1491 
1492 /*++
1493 
1494 Routine Description:
1495 
1496     I/O System entry for read and write requests to SCSI disks.
1497 
1498 Arguments:
1499 
1500     DeviceObject - Pointer to driver object created by system.
1501     Irp - IRP involved.
1502 
1503 Return Value:
1504 
1505     NT Status
1506 
1507 --*/
1508 
1509 {
1510     PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
1511     PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
1512     ULONG transferByteCount = currentIrpStack->Parameters.Read.Length;
1513     LARGE_INTEGER startingOffset;
1514 
1515     //
1516     // HACK: How can we end here with null sector size?!
1517     //
1518 
1519     if (deviceExtension->DiskGeometry->Geometry.BytesPerSector == 0) {
1520         DPRINT1("Hack! Received invalid sector size\n");
1521         deviceExtension->DiskGeometry->Geometry.BytesPerSector = 512;
1522     }
1523 
1524     //
1525     // Verify parameters of this request.
1526     // Check that ending sector is within partition and
1527     // that number of bytes to transfer is a multiple of
1528     // the sector size.
1529     //
1530 
1531     startingOffset.QuadPart = (currentIrpStack->Parameters.Read.ByteOffset.QuadPart +
1532                                transferByteCount);
1533 
1534     if ((startingOffset.QuadPart > deviceExtension->PartitionLength.QuadPart) ||
1535         (transferByteCount & (deviceExtension->DiskGeometry->Geometry.BytesPerSector - 1))) {
1536 
1537         //
1538         // This error maybe caused by the fact that the drive is not ready.
1539         //
1540 
1541         if (((PDISK_DATA)(deviceExtension + 1))->DriveNotReady) {
1542 
1543             //
1544             // Flag this as a user error so that a popup is generated.
1545             //
1546 
1547             Irp->IoStatus.Status = STATUS_DEVICE_NOT_READY;
1548             IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);
1549 
1550         } else {
1551 
1552             //
1553             // Note fastfat depends on this parameter to determine when to
1554             // remount do to a sector size change.
1555             //
1556 
1557             Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
1558         }
1559 
1560         if (startingOffset.QuadPart > deviceExtension->PartitionLength.QuadPart) {
1561             DPRINT1("Reading beyond partition end! startingOffset: %I64d, PartitionLength: %I64d\n", startingOffset.QuadPart, deviceExtension->PartitionLength.QuadPart);
1562         }
1563 
1564         if (transferByteCount & (deviceExtension->DiskGeometry->Geometry.BytesPerSector - 1)) {
1565             DPRINT1("Not reading sectors! TransferByteCount: %lu, BytesPerSector: %lu\n", transferByteCount, deviceExtension->DiskGeometry->Geometry.BytesPerSector);
1566         }
1567 
1568         if (Irp->IoStatus.Status == STATUS_DEVICE_NOT_READY) {
1569             DPRINT1("Failing due to device not ready!\n");
1570         }
1571 
1572         return STATUS_INVALID_PARAMETER;
1573     }
1574 
1575     return STATUS_SUCCESS;
1576 
1577 } // end ScsiDiskReadWrite()
1578 
1579 
1580 NTSTATUS
1581 NTAPI
1582 ScsiDiskDeviceControl(
1583     PDEVICE_OBJECT DeviceObject,
1584     PIRP Irp
1585     )
1586 
1587 /*++
1588 
1589 Routine Description:
1590 
1591     I/O system entry for device controls to SCSI disks.
1592 
1593 Arguments:
1594 
1595     DeviceObject - Pointer to driver object created by system.
1596     Irp - IRP involved.
1597 
1598 Return Value:
1599 
1600     Status is returned.
1601 
1602 --*/
1603 
1604 {
1605     PIO_STACK_LOCATION     irpStack = IoGetCurrentIrpStackLocation(Irp);
1606     PDEVICE_EXTENSION      deviceExtension = DeviceObject->DeviceExtension;
1607     PDISK_DATA             diskData = (PDISK_DATA)(deviceExtension + 1);
1608     PSCSI_REQUEST_BLOCK    srb;
1609     PCDB                   cdb;
1610     PMODE_PARAMETER_HEADER modeData;
1611     PIRP                   irp2;
1612     ULONG                  length;
1613     NTSTATUS               status;
1614     KEVENT                 event;
1615     IO_STATUS_BLOCK        ioStatus;
1616 
1617     PAGED_CODE();
1618 
1619     srb = ExAllocatePool(NonPagedPool, SCSI_REQUEST_BLOCK_SIZE);
1620 
1621     if (srb == NULL) {
1622 
1623         Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
1624         IoCompleteRequest(Irp, IO_NO_INCREMENT);
1625         return(STATUS_INSUFFICIENT_RESOURCES);
1626     }
1627 
1628     //
1629     // Write zeros to Srb.
1630     //
1631 
1632     RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE);
1633 
1634     cdb = (PCDB)srb->Cdb;
1635 
1636     switch (irpStack->Parameters.DeviceIoControl.IoControlCode) {
1637 
1638     case SMART_GET_VERSION: {
1639 
1640         ULONG_PTR buffer;
1641         PSRB_IO_CONTROL  srbControl;
1642         PGETVERSIONINPARAMS versionParams;
1643 
1644         if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
1645             sizeof(GETVERSIONINPARAMS)) {
1646                 status = STATUS_INVALID_PARAMETER;
1647                 break;
1648         }
1649 
1650         //
1651         // Create notification event object to be used to signal the
1652         // request completion.
1653         //
1654 
1655         KeInitializeEvent(&event, NotificationEvent, FALSE);
1656 
1657         srbControl = ExAllocatePool(NonPagedPool,
1658                                     sizeof(SRB_IO_CONTROL) + sizeof(GETVERSIONINPARAMS));
1659 
1660         if (!srbControl) {
1661             status =  STATUS_INSUFFICIENT_RESOURCES;
1662             break;
1663         }
1664 
1665         //
1666         // fill in srbControl fields
1667         //
1668 
1669         srbControl->HeaderLength = sizeof(SRB_IO_CONTROL);
1670         RtlMoveMemory (srbControl->Signature, "SCSIDISK", 8);
1671         srbControl->Timeout = deviceExtension->TimeOutValue;
1672         srbControl->Length = sizeof(GETVERSIONINPARAMS);
1673         srbControl->ControlCode = IOCTL_SCSI_MINIPORT_SMART_VERSION;
1674 
1675         //
1676         // Point to the 'buffer' portion of the SRB_CONTROL
1677         //
1678 
1679         buffer = (ULONG_PTR)srbControl + srbControl->HeaderLength;
1680 
1681         //
1682         // Ensure correct target is set in the cmd parameters.
1683         //
1684 
1685         versionParams = (PGETVERSIONINPARAMS)buffer;
1686         versionParams->bIDEDeviceMap = deviceExtension->TargetId;
1687 
1688         //
1689         // Copy the IOCTL parameters to the srb control buffer area.
1690         //
1691 
1692         RtlMoveMemory((PVOID)buffer, Irp->AssociatedIrp.SystemBuffer, sizeof(GETVERSIONINPARAMS));
1693 
1694 
1695         irp2 = IoBuildDeviceIoControlRequest(IOCTL_SCSI_MINIPORT,
1696                                             deviceExtension->PortDeviceObject,
1697                                             srbControl,
1698                                             sizeof(SRB_IO_CONTROL) + sizeof(GETVERSIONINPARAMS),
1699                                             srbControl,
1700                                             sizeof(SRB_IO_CONTROL) + sizeof(GETVERSIONINPARAMS),
1701                                             FALSE,
1702                                             &event,
1703                                             &ioStatus);
1704 
1705         if (irp2 == NULL) {
1706             status = STATUS_INSUFFICIENT_RESOURCES;
1707             break;
1708         }
1709 
1710         //
1711         // Call the port driver with the request and wait for it to complete.
1712         //
1713 
1714         status = IoCallDriver(deviceExtension->PortDeviceObject, irp2);
1715 
1716         if (status == STATUS_PENDING) {
1717             KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, NULL);
1718             status = ioStatus.Status;
1719         }
1720 
1721         //
1722         // If successful, copy the data received into the output buffer.
1723         // This should only fail in the event that the IDE driver is older than this driver.
1724         //
1725 
1726         if (NT_SUCCESS(status)) {
1727 
1728             buffer = (ULONG_PTR)srbControl + srbControl->HeaderLength;
1729 
1730             RtlMoveMemory ( Irp->AssociatedIrp.SystemBuffer, (PVOID)buffer, sizeof(GETVERSIONINPARAMS));
1731             Irp->IoStatus.Information = sizeof(GETVERSIONINPARAMS);
1732         }
1733 
1734         ExFreePool(srbControl);
1735         break;
1736     }
1737 
1738     case SMART_RCV_DRIVE_DATA: {
1739 
1740         PSENDCMDINPARAMS cmdInParameters = ((PSENDCMDINPARAMS)Irp->AssociatedIrp.SystemBuffer);
1741         ULONG            controlCode = 0;
1742         PSRB_IO_CONTROL  srbControl;
1743         ULONG_PTR        buffer;
1744 
1745         if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
1746             (sizeof(SENDCMDINPARAMS) - 1)) {
1747                 status = STATUS_INVALID_PARAMETER;
1748                 break;
1749 
1750         } else if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
1751             (sizeof(SENDCMDOUTPARAMS) + 512 - 1)) {
1752                 status = STATUS_INVALID_PARAMETER;
1753                 break;
1754         }
1755 
1756         //
1757         // Create notification event object to be used to signal the
1758         // request completion.
1759         //
1760 
1761         KeInitializeEvent(&event, NotificationEvent, FALSE);
1762 
1763         if (cmdInParameters->irDriveRegs.bCommandReg == ID_CMD) {
1764 
1765             length = IDENTIFY_BUFFER_SIZE + sizeof(SENDCMDOUTPARAMS);
1766             controlCode = IOCTL_SCSI_MINIPORT_IDENTIFY;
1767 
1768         } else if (cmdInParameters->irDriveRegs.bCommandReg == SMART_CMD) {
1769             switch (cmdInParameters->irDriveRegs.bFeaturesReg) {
1770                 case READ_ATTRIBUTES:
1771                     controlCode = IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS;
1772                     length = READ_ATTRIBUTE_BUFFER_SIZE + sizeof(SENDCMDOUTPARAMS);
1773                     break;
1774                 case READ_THRESHOLDS:
1775                     controlCode = IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS;
1776                     length = READ_THRESHOLD_BUFFER_SIZE + sizeof(SENDCMDOUTPARAMS);
1777                     break;
1778                 default:
1779                     status = STATUS_INVALID_PARAMETER;
1780                     break;
1781             }
1782         } else {
1783 
1784             status = STATUS_INVALID_PARAMETER;
1785         }
1786 
1787         if (controlCode == 0) {
1788             status = STATUS_INVALID_PARAMETER;
1789             break;
1790         }
1791 
1792         srbControl = ExAllocatePool(NonPagedPool,
1793                                     sizeof(SRB_IO_CONTROL) + length);
1794 
1795         if (!srbControl) {
1796             status =  STATUS_INSUFFICIENT_RESOURCES;
1797             break;
1798         }
1799 
1800         //
1801         // fill in srbControl fields
1802         //
1803 
1804         srbControl->HeaderLength = sizeof(SRB_IO_CONTROL);
1805         RtlMoveMemory (srbControl->Signature, "SCSIDISK", 8);
1806         srbControl->Timeout = deviceExtension->TimeOutValue;
1807         srbControl->Length = length;
1808         srbControl->ControlCode = controlCode;
1809 
1810         //
1811         // Point to the 'buffer' portion of the SRB_CONTROL
1812         //
1813 
1814         buffer = (ULONG_PTR)srbControl + srbControl->HeaderLength;
1815 
1816         //
1817         // Ensure correct target is set in the cmd parameters.
1818         //
1819 
1820         cmdInParameters->bDriveNumber = deviceExtension->TargetId;
1821 
1822         //
1823         // Copy the IOCTL parameters to the srb control buffer area.
1824         //
1825 
1826         RtlMoveMemory((PVOID)buffer, Irp->AssociatedIrp.SystemBuffer, sizeof(SENDCMDINPARAMS) - 1);
1827 
1828         irp2 = IoBuildDeviceIoControlRequest(IOCTL_SCSI_MINIPORT,
1829                                             deviceExtension->PortDeviceObject,
1830                                             srbControl,
1831                                             sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS) - 1,
1832                                             srbControl,
1833                                             sizeof(SRB_IO_CONTROL) + length,
1834                                             FALSE,
1835                                             &event,
1836                                             &ioStatus);
1837 
1838         if (irp2 == NULL) {
1839             status = STATUS_INSUFFICIENT_RESOURCES;
1840             break;
1841         }
1842 
1843         //
1844         // Call the port driver with the request and wait for it to complete.
1845         //
1846 
1847         status = IoCallDriver(deviceExtension->PortDeviceObject, irp2);
1848 
1849         if (status == STATUS_PENDING) {
1850             KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, NULL);
1851             status = ioStatus.Status;
1852         }
1853 
1854         //
1855         // If successful, copy the data received into the output buffer
1856         //
1857 
1858         buffer = (ULONG_PTR)srbControl + srbControl->HeaderLength;
1859 
1860         if (NT_SUCCESS(status)) {
1861 
1862             RtlMoveMemory ( Irp->AssociatedIrp.SystemBuffer, (PVOID)buffer, length - 1);
1863             Irp->IoStatus.Information = length - 1;
1864 
1865         } else {
1866 
1867             RtlMoveMemory ( Irp->AssociatedIrp.SystemBuffer, (PVOID)buffer, (sizeof(SENDCMDOUTPARAMS) - 1));
1868             Irp->IoStatus.Information = sizeof(SENDCMDOUTPARAMS) - 1;
1869 
1870         }
1871 
1872         ExFreePool(srbControl);
1873         break;
1874 
1875     }
1876 
1877     case SMART_SEND_DRIVE_COMMAND: {
1878 
1879         PSENDCMDINPARAMS cmdInParameters = ((PSENDCMDINPARAMS)Irp->AssociatedIrp.SystemBuffer);
1880         PSRB_IO_CONTROL  srbControl;
1881         ULONG            controlCode = 0;
1882         ULONG_PTR        buffer;
1883 
1884         if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
1885                (sizeof(SENDCMDINPARAMS) - 1)) {
1886                 status = STATUS_INVALID_PARAMETER;
1887                 break;
1888 
1889         } else if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
1890                       (sizeof(SENDCMDOUTPARAMS) - 1)) {
1891                 status = STATUS_INVALID_PARAMETER;
1892                 break;
1893         }
1894 
1895         //
1896         // Create notification event object to be used to signal the
1897         // request completion.
1898         //
1899 
1900         KeInitializeEvent(&event, NotificationEvent, FALSE);
1901 
1902         length = 0;
1903 
1904         if (cmdInParameters->irDriveRegs.bCommandReg == SMART_CMD) {
1905             switch (cmdInParameters->irDriveRegs.bFeaturesReg) {
1906 
1907                 case ENABLE_SMART:
1908                     controlCode = IOCTL_SCSI_MINIPORT_ENABLE_SMART;
1909                     break;
1910 
1911                 case DISABLE_SMART:
1912                     controlCode = IOCTL_SCSI_MINIPORT_DISABLE_SMART;
1913                     break;
1914 
1915                 case  RETURN_SMART_STATUS:
1916 
1917                     //
1918                     // Ensure bBuffer is at least 2 bytes (to hold the values of
1919                     // cylinderLow and cylinderHigh).
1920                     //
1921 
1922                     if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
1923                         (sizeof(SENDCMDOUTPARAMS) - 1 + sizeof(IDEREGS))) {
1924 
1925                         status = STATUS_INVALID_PARAMETER;
1926                         break;
1927                     }
1928 
1929                     controlCode = IOCTL_SCSI_MINIPORT_RETURN_STATUS;
1930                     length = sizeof(IDEREGS);
1931                     break;
1932 
1933                 case ENABLE_DISABLE_AUTOSAVE:
1934                     controlCode = IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE;
1935                     break;
1936 
1937                 case SAVE_ATTRIBUTE_VALUES:
1938                     controlCode = IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES;
1939                     break;
1940 
1941                 case EXECUTE_OFFLINE_DIAGS:
1942                     controlCode = IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS;
1943                     break;
1944 
1945           default:
1946                     status = STATUS_INVALID_PARAMETER;
1947                     break;
1948             }
1949         } else {
1950 
1951             status = STATUS_INVALID_PARAMETER;
1952         }
1953 
1954         if (controlCode == 0) {
1955             status = STATUS_INVALID_PARAMETER;
1956             break;
1957         }
1958 
1959         length += (sizeof(SENDCMDOUTPARAMS) > sizeof(SENDCMDINPARAMS)) ? sizeof(SENDCMDOUTPARAMS) : sizeof(SENDCMDINPARAMS);
1960         srbControl = ExAllocatePool(NonPagedPool,
1961                                     sizeof(SRB_IO_CONTROL) + length);
1962 
1963         if (!srbControl) {
1964             status =  STATUS_INSUFFICIENT_RESOURCES;
1965             break;
1966         }
1967 
1968         //
1969         // fill in srbControl fields
1970         //
1971 
1972         srbControl->HeaderLength = sizeof(SRB_IO_CONTROL);
1973         RtlMoveMemory (srbControl->Signature, "SCSIDISK", 8);
1974         srbControl->Timeout = deviceExtension->TimeOutValue;
1975         srbControl->Length = length;
1976 
1977         //
1978         // Point to the 'buffer' portion of the SRB_CONTROL
1979         //
1980 
1981         buffer = (ULONG_PTR)srbControl + srbControl->HeaderLength;
1982 
1983         //
1984         // Ensure correct target is set in the cmd parameters.
1985         //
1986 
1987         cmdInParameters->bDriveNumber = deviceExtension->TargetId;
1988 
1989         //
1990         // Copy the IOCTL parameters to the srb control buffer area.
1991         //
1992 
1993         RtlMoveMemory((PVOID)buffer, Irp->AssociatedIrp.SystemBuffer, sizeof(SENDCMDINPARAMS) - 1);
1994 
1995         srbControl->ControlCode = controlCode;
1996 
1997         irp2 = IoBuildDeviceIoControlRequest(IOCTL_SCSI_MINIPORT,
1998                                             deviceExtension->PortDeviceObject,
1999                                             srbControl,
2000                                             sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS) - 1,
2001                                             srbControl,
2002                                             sizeof(SRB_IO_CONTROL) + length,
2003                                             FALSE,
2004                                             &event,
2005                                             &ioStatus);
2006 
2007         if (irp2 == NULL) {
2008             status = STATUS_INSUFFICIENT_RESOURCES;
2009             break;
2010         }
2011 
2012         //
2013         // Call the port driver with the request and wait for it to complete.
2014         //
2015 
2016         status = IoCallDriver(deviceExtension->PortDeviceObject, irp2);
2017 
2018         if (status == STATUS_PENDING) {
2019             KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, NULL);
2020             status = ioStatus.Status;
2021         }
2022 
2023         //
2024         // Copy the data received into the output buffer. Since the status buffer
2025         // contains error information also, always perform this copy. IO will will
2026         // either pass this back to the app, or zero it, in case of error.
2027         //
2028 
2029         buffer = (ULONG_PTR)srbControl + srbControl->HeaderLength;
2030 
2031         //
2032         // Update the return buffer size based on the sub-command.
2033         //
2034 
2035         if (cmdInParameters->irDriveRegs.bFeaturesReg == RETURN_SMART_STATUS) {
2036             length = sizeof(SENDCMDOUTPARAMS) - 1 + sizeof(IDEREGS);
2037         } else {
2038             length = sizeof(SENDCMDOUTPARAMS) - 1;
2039         }
2040 
2041         RtlMoveMemory ( Irp->AssociatedIrp.SystemBuffer, (PVOID)buffer, length);
2042         Irp->IoStatus.Information = length;
2043 
2044         ExFreePool(srbControl);
2045         break;
2046 
2047     }
2048 
2049     case IOCTL_DISK_GET_DRIVE_GEOMETRY:
2050     case IOCTL_DISK_GET_DRIVE_GEOMETRY_EX:
2051         {
2052 
2053         PDEVICE_EXTENSION physicalDeviceExtension;
2054         PDISK_DATA        physicalDiskData;
2055         BOOLEAN           removable = FALSE;
2056         BOOLEAN           listInitialized = FALSE;
2057         ULONG             copyLength;
2058 
2059         if (irpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_DISK_GET_DRIVE_GEOMETRY) {
2060             if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(DISK_GEOMETRY)) {
2061                 status = STATUS_BUFFER_TOO_SMALL;
2062                 break;
2063             }
2064 
2065             copyLength = sizeof(DISK_GEOMETRY);
2066         } else {
2067             ASSERT(irpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_DISK_GET_DRIVE_GEOMETRY_EX);
2068             if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < FIELD_OFFSET(DISK_GEOMETRY_EX, Data)) {
2069                 status = STATUS_BUFFER_TOO_SMALL;
2070                 break;
2071             }
2072 
2073             if (irpStack->Parameters.DeviceIoControl.OutputBufferLength >= sizeof(DISK_GEOMETRY_EX)) {
2074                 copyLength = sizeof(DISK_GEOMETRY_EX);
2075             } else {
2076                 copyLength = FIELD_OFFSET(DISK_GEOMETRY_EX, Data);
2077             }
2078         }
2079 
2080         status = STATUS_SUCCESS;
2081 
2082         physicalDeviceExtension = deviceExtension->PhysicalDevice->DeviceExtension;
2083         physicalDiskData = (PDISK_DATA)(physicalDeviceExtension + 1);
2084 
2085         removable = (BOOLEAN)DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA;
2086         listInitialized = (physicalDiskData->PartitionListState == Initialized);
2087 
2088         if (removable || (!listInitialized))
2089         {
2090             //
2091             // Issue ReadCapacity to update device extension
2092             // with information for current media.
2093             //
2094 
2095             status = ScsiClassReadDriveCapacity(deviceExtension->PhysicalDevice);
2096 
2097         }
2098 
2099         if (removable) {
2100 
2101             if (!NT_SUCCESS(status)) {
2102 
2103                 //
2104                 // Note the drive is not ready.
2105                 //
2106 
2107                 diskData->DriveNotReady = TRUE;
2108 
2109                 break;
2110             }
2111 
2112             //
2113             // Note the drive is now ready.
2114             //
2115 
2116             diskData->DriveNotReady = FALSE;
2117 
2118         } else if (NT_SUCCESS(status)) {
2119 
2120             // ReadDriveCapacity was alright, create Partition Objects
2121 
2122             if (physicalDiskData->PartitionListState == NotInitialized) {
2123                     status = CreatePartitionDeviceObjects(deviceExtension->PhysicalDevice, NULL);
2124             }
2125         }
2126 
2127         if (NT_SUCCESS(status)) {
2128 
2129             //
2130             // Copy drive geometry information from device extension.
2131             //
2132 
2133             RtlMoveMemory(Irp->AssociatedIrp.SystemBuffer,
2134                           deviceExtension->DiskGeometry,
2135                           copyLength);
2136 
2137             status = STATUS_SUCCESS;
2138             Irp->IoStatus.Information = copyLength;
2139         }
2140 
2141         break;
2142 
2143         }
2144 
2145     case IOCTL_DISK_VERIFY:
2146 
2147         {
2148 
2149         PVERIFY_INFORMATION verifyInfo = Irp->AssociatedIrp.SystemBuffer;
2150         LARGE_INTEGER byteOffset;
2151         ULONG         sectorOffset;
2152         USHORT        sectorCount;
2153 
2154         //
2155         // Validate buffer length.
2156         //
2157 
2158         if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
2159             sizeof(VERIFY_INFORMATION)) {
2160 
2161             status = STATUS_INFO_LENGTH_MISMATCH;
2162             break;
2163         }
2164 
2165         //
2166         // Verify sectors
2167         //
2168 
2169         srb->CdbLength = 10;
2170 
2171         cdb->CDB10.OperationCode = SCSIOP_VERIFY;
2172 
2173         //
2174         // Add disk offset to starting sector.
2175         //
2176 
2177         byteOffset.QuadPart = deviceExtension->StartingOffset.QuadPart +
2178                                         verifyInfo->StartingOffset.QuadPart;
2179 
2180         //
2181         // Convert byte offset to sector offset.
2182         //
2183 
2184         sectorOffset = (ULONG)(byteOffset.QuadPart >> deviceExtension->SectorShift);
2185 
2186         //
2187         // Convert ULONG byte count to USHORT sector count.
2188         //
2189 
2190         sectorCount = (USHORT)(verifyInfo->Length >> deviceExtension->SectorShift);
2191 
2192         //
2193         // Move little endian values into CDB in big endian format.
2194         //
2195 
2196         cdb->CDB10.LogicalBlockByte0 = ((PFOUR_BYTE)&sectorOffset)->Byte3;
2197         cdb->CDB10.LogicalBlockByte1 = ((PFOUR_BYTE)&sectorOffset)->Byte2;
2198         cdb->CDB10.LogicalBlockByte2 = ((PFOUR_BYTE)&sectorOffset)->Byte1;
2199         cdb->CDB10.LogicalBlockByte3 = ((PFOUR_BYTE)&sectorOffset)->Byte0;
2200 
2201         cdb->CDB10.TransferBlocksMsb = ((PFOUR_BYTE)&sectorCount)->Byte1;
2202         cdb->CDB10.TransferBlocksLsb = ((PFOUR_BYTE)&sectorCount)->Byte0;
2203 
2204         //
2205         // The verify command is used by the NT FORMAT utility and
2206         // requests are sent down for 5% of the volume size. The
2207         // request timeout value is calculated based on the number of
2208         // sectors verified.
2209         //
2210 
2211         srb->TimeOutValue = ((sectorCount + 0x7F) >> 7) *
2212                                               deviceExtension->TimeOutValue;
2213 
2214         status = ScsiClassSendSrbAsynchronous(DeviceObject,
2215                                               srb,
2216                                               Irp,
2217                                               NULL,
2218                                               0,
2219                                               FALSE);
2220 
2221         return(status);
2222 
2223         }
2224 
2225     case IOCTL_DISK_GET_PARTITION_INFO:
2226 
2227         //
2228         // Return the information about the partition specified by the device
2229         // object.  Note that no information is ever returned about the size
2230         // or partition type of the physical disk, as this doesn't make any
2231         // sense.
2232         //
2233 
2234         if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
2235             sizeof(PARTITION_INFORMATION)) {
2236 
2237             status = STATUS_INFO_LENGTH_MISMATCH;
2238 
2239         }
2240 #if 0 // HACK: ReactOS partition numbers must be wrong
2241         else if (diskData->PartitionNumber == 0) {
2242 
2243             //
2244             // Partition zero is not a partition so this is not a
2245             // reasonable request.
2246             //
2247 
2248             status = STATUS_INVALID_DEVICE_REQUEST;
2249 
2250         }
2251 #endif
2252         else {
2253 
2254             PPARTITION_INFORMATION outputBuffer;
2255 
2256             if (diskData->PartitionNumber == 0) {
2257                 DPRINT1("HACK: Handling partition 0 request!\n");
2258                 //ASSERT(FALSE);
2259             }
2260 
2261             //
2262             // Update the geometry in case it has changed.
2263             //
2264 
2265             status = UpdateRemovableGeometry (DeviceObject, Irp);
2266 
2267             if (!NT_SUCCESS(status)) {
2268 
2269                 //
2270                 // Note the drive is not ready.
2271                 //
2272 
2273                 diskData->DriveNotReady = TRUE;
2274                 break;
2275             }
2276 
2277             //
2278             // Note the drive is now ready.
2279             //
2280 
2281             diskData->DriveNotReady = FALSE;
2282 
2283             outputBuffer =
2284                     (PPARTITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer;
2285 
2286             outputBuffer->PartitionType = diskData->PartitionType;
2287             outputBuffer->StartingOffset = deviceExtension->StartingOffset;
2288             outputBuffer->PartitionLength.QuadPart = (diskData->PartitionNumber) ?
2289                 deviceExtension->PartitionLength.QuadPart : 0x1FFFFFFFFFFFFFFFLL; // HACK
2290             outputBuffer->HiddenSectors = diskData->HiddenSectors;
2291             outputBuffer->PartitionNumber = diskData->PartitionNumber;
2292             outputBuffer->BootIndicator = diskData->BootIndicator;
2293             outputBuffer->RewritePartition = FALSE;
2294             outputBuffer->RecognizedPartition =
2295                 IsRecognizedPartition(diskData->PartitionType);
2296 
2297             status = STATUS_SUCCESS;
2298             Irp->IoStatus.Information = sizeof(PARTITION_INFORMATION);
2299         }
2300 
2301         break;
2302 
2303     case IOCTL_DISK_GET_PARTITION_INFO_EX:
2304 
2305         //
2306         // Return the information about the partition specified by the device
2307         // object.  Note that no information is ever returned about the size
2308         // or partition type of the physical disk, as this doesn't make any
2309         // sense.
2310         //
2311 
2312         if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
2313             sizeof(PARTITION_INFORMATION_EX)) {
2314 
2315             status = STATUS_INFO_LENGTH_MISMATCH;
2316 
2317         }
2318 #if 0 // HACK: ReactOS partition numbers must be wrong
2319         else if (diskData->PartitionNumber == 0) {
2320 
2321             //
2322             // Partition zero is not a partition so this is not a
2323             // reasonable request.
2324             //
2325 
2326             status = STATUS_INVALID_DEVICE_REQUEST;
2327 
2328         }
2329 #endif
2330         else {
2331 
2332             PPARTITION_INFORMATION_EX outputBuffer;
2333 
2334             if (diskData->PartitionNumber == 0) {
2335                 DPRINT1("HACK: Handling partition 0 request!\n");
2336                 //ASSERT(FALSE);
2337             }
2338 
2339             //
2340             // Update the geometry in case it has changed.
2341             //
2342 
2343             status = UpdateRemovableGeometry (DeviceObject, Irp);
2344 
2345             if (!NT_SUCCESS(status)) {
2346 
2347                 //
2348                 // Note the drive is not ready.
2349                 //
2350 
2351                 diskData->DriveNotReady = TRUE;
2352                 break;
2353             }
2354 
2355             //
2356             // Note the drive is now ready.
2357             //
2358 
2359             diskData->DriveNotReady = FALSE;
2360 
2361             if (diskData->PartitionType == 0 && (diskData->PartitionNumber > 0)) {
2362 
2363                 status = STATUS_INVALID_DEVICE_REQUEST;
2364                 break;
2365             }
2366 
2367             outputBuffer =
2368                     (PPARTITION_INFORMATION_EX)Irp->AssociatedIrp.SystemBuffer;
2369 
2370             //
2371             // FIXME: hack of the year, assume that partition is MBR
2372             // Thing that can obviously be wrong...
2373             //
2374 
2375             outputBuffer->PartitionStyle = PARTITION_STYLE_MBR;
2376             outputBuffer->Mbr.PartitionType = diskData->PartitionType;
2377             outputBuffer->StartingOffset = deviceExtension->StartingOffset;
2378             outputBuffer->PartitionLength.QuadPart = deviceExtension->PartitionLength.QuadPart;
2379             outputBuffer->Mbr.HiddenSectors = diskData->HiddenSectors;
2380             outputBuffer->PartitionNumber = diskData->PartitionNumber;
2381             outputBuffer->Mbr.BootIndicator = diskData->BootIndicator;
2382             outputBuffer->RewritePartition = FALSE;
2383             outputBuffer->Mbr.RecognizedPartition =
2384                 IsRecognizedPartition(diskData->PartitionType);
2385 
2386             status = STATUS_SUCCESS;
2387             Irp->IoStatus.Information = sizeof(PARTITION_INFORMATION_EX);
2388         }
2389 
2390         break;
2391 
2392     case IOCTL_DISK_SET_PARTITION_INFO:
2393 
2394         if (diskData->PartitionNumber == 0) {
2395 
2396             status = STATUS_UNSUCCESSFUL;
2397 
2398         } else {
2399 
2400             PSET_PARTITION_INFORMATION inputBuffer =
2401                 (PSET_PARTITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer;
2402 
2403             //
2404             // Validate buffer length.
2405             //
2406 
2407             if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
2408                 sizeof(SET_PARTITION_INFORMATION)) {
2409 
2410                 status = STATUS_INFO_LENGTH_MISMATCH;
2411                 break;
2412             }
2413 
2414             //
2415             // The HAL routines IoGet- and IoSetPartitionInformation were
2416             // developed before support of dynamic partitioning and therefore
2417             // don't distinguish between partition ordinal (that is the order
2418             // of a partition on a disk) and the partition number. (The
2419             // partition number is assigned to a partition to identify it to
2420             // the system.) Use partition ordinals for these legacy calls.
2421             //
2422 
2423             status = IoSetPartitionInformation(
2424                           deviceExtension->PhysicalDevice,
2425                           deviceExtension->DiskGeometry->Geometry.BytesPerSector,
2426                           diskData->PartitionOrdinal,
2427                           inputBuffer->PartitionType);
2428 
2429             if (NT_SUCCESS(status)) {
2430 
2431                 diskData->PartitionType = inputBuffer->PartitionType;
2432             }
2433         }
2434 
2435         break;
2436 
2437     case IOCTL_DISK_GET_DRIVE_LAYOUT:
2438 
2439         //
2440         // Return the partition layout for the physical drive.  Note that
2441         // the layout is returned for the actual physical drive, regardless
2442         // of which partition was specified for the request.
2443         //
2444 
2445         if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
2446             sizeof(DRIVE_LAYOUT_INFORMATION)) {
2447             status = STATUS_INFO_LENGTH_MISMATCH;
2448 
2449         } else {
2450 
2451             PDRIVE_LAYOUT_INFORMATION partitionList;
2452             PDEVICE_EXTENSION         physicalExtension = deviceExtension;
2453             PPARTITION_INFORMATION    partitionEntry;
2454             PDISK_DATA                diskData;
2455             ULONG                     tempSize;
2456             ULONG                     i;
2457 
2458             //
2459             // Read partition information.
2460             //
2461 
2462             status = IoReadPartitionTable(deviceExtension->PhysicalDevice,
2463                               deviceExtension->DiskGeometry->Geometry.BytesPerSector,
2464                               FALSE,
2465                               &partitionList);
2466 
2467             if (!NT_SUCCESS(status)) {
2468                 break;
2469             }
2470 
2471             //
2472             // The disk layout has been returned in the partitionList
2473             // buffer.  Determine its size and, if the data will fit
2474             // into the intermediary buffer, return it.
2475             //
2476 
2477             tempSize = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION,PartitionEntry[0]);
2478             tempSize += partitionList->PartitionCount *
2479                         sizeof(PARTITION_INFORMATION);
2480 
2481             if (tempSize >
2482                irpStack->Parameters.DeviceIoControl.OutputBufferLength) {
2483 
2484                 status = STATUS_BUFFER_TOO_SMALL;
2485                 ExFreePool(partitionList);
2486                 break;
2487             }
2488 
2489             //
2490             // Walk partition list to associate partition numbers with
2491             // partition entries.
2492             //
2493 
2494             for (i = 0; i < partitionList->PartitionCount; i++) {
2495 
2496                 //
2497                 // Walk partition chain anchored at physical disk extension.
2498                 //
2499 
2500                 deviceExtension = physicalExtension;
2501                 diskData = (PDISK_DATA)(deviceExtension + 1);
2502 
2503                 do {
2504 
2505                     deviceExtension = diskData->NextPartition;
2506 
2507                     //
2508                     // Check if this is the last partition in the chain.
2509                     //
2510 
2511                     if (!deviceExtension) {
2512                        break;
2513                     }
2514 
2515                     //
2516                     // Get the partition device extension from disk data.
2517                     //
2518 
2519                     diskData = (PDISK_DATA)(deviceExtension + 1);
2520 
2521                     //
2522                     // Check if this partition is not currently being used.
2523                     //
2524 
2525                     if (!deviceExtension->PartitionLength.QuadPart) {
2526                        continue;
2527                     }
2528 
2529                     partitionEntry = &partitionList->PartitionEntry[i];
2530 
2531                     //
2532                     // Check if empty, or describes extended partition or hasn't changed.
2533                     //
2534 
2535                     if (partitionEntry->PartitionType == PARTITION_ENTRY_UNUSED ||
2536                         IsContainerPartition(partitionEntry->PartitionType)) {
2537                         continue;
2538                     }
2539 
2540                     //
2541                     // Check if new partition starts where this partition starts.
2542                     //
2543 
2544                     if (partitionEntry->StartingOffset.QuadPart !=
2545                               deviceExtension->StartingOffset.QuadPart) {
2546                         continue;
2547                     }
2548 
2549                     //
2550                     // Check if partition length is the same.
2551                     //
2552 
2553                     if (partitionEntry->PartitionLength.QuadPart ==
2554                               deviceExtension->PartitionLength.QuadPart) {
2555 
2556                         //
2557                         // Partitions match. Update partition number.
2558                         //
2559 
2560                         partitionEntry->PartitionNumber =
2561                             diskData->PartitionNumber;
2562                         break;
2563                     }
2564 
2565                 } while (TRUE);
2566             }
2567 
2568             //
2569             // Copy partition information to system buffer.
2570             //
2571 
2572             RtlMoveMemory(Irp->AssociatedIrp.SystemBuffer,
2573                           partitionList,
2574                           tempSize);
2575             status = STATUS_SUCCESS;
2576             Irp->IoStatus.Information = tempSize;
2577 
2578             //
2579             // Finally, free the buffer allocated by reading the
2580             // partition table.
2581             //
2582 
2583             ExFreePool(partitionList);
2584         }
2585 
2586         break;
2587 
2588     case IOCTL_DISK_SET_DRIVE_LAYOUT:
2589 
2590         {
2591 
2592         //
2593         // Update the disk with new partition information.
2594         //
2595 
2596         PDRIVE_LAYOUT_INFORMATION partitionList = Irp->AssociatedIrp.SystemBuffer;
2597 
2598         //
2599         // Validate buffer length.
2600         //
2601 
2602         if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
2603             sizeof(DRIVE_LAYOUT_INFORMATION)) {
2604 
2605             status = STATUS_INFO_LENGTH_MISMATCH;
2606             break;
2607         }
2608 
2609         length = sizeof(DRIVE_LAYOUT_INFORMATION) +
2610             (partitionList->PartitionCount - 1) * sizeof(PARTITION_INFORMATION);
2611 
2612 
2613         if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
2614             length) {
2615 
2616             status = STATUS_BUFFER_TOO_SMALL;
2617             break;
2618         }
2619 
2620         //
2621         // Verify that device object is for physical disk.
2622         //
2623 
2624         if (deviceExtension->PhysicalDevice->DeviceExtension != deviceExtension) {
2625             status = STATUS_INVALID_PARAMETER;
2626             break;
2627         }
2628 
2629         //
2630         // Walk through partition table comparing partitions to
2631         // existing partitions to create, delete and change
2632         // device objects as necessary.
2633         //
2634 
2635         UpdateDeviceObjects(DeviceObject,
2636                             Irp);
2637 
2638         //
2639         // Write changes to disk.
2640         //
2641 
2642         status = IoWritePartitionTable(
2643                            deviceExtension->DeviceObject,
2644                            deviceExtension->DiskGeometry->Geometry.BytesPerSector,
2645                            deviceExtension->DiskGeometry->Geometry.SectorsPerTrack,
2646                            deviceExtension->DiskGeometry->Geometry.TracksPerCylinder,
2647                            partitionList);
2648         }
2649 
2650         //
2651         // Update IRP with bytes returned.
2652         //
2653 
2654         if (NT_SUCCESS(status)) {
2655             Irp->IoStatus.Information = length;
2656         }
2657 
2658         break;
2659 
2660     case IOCTL_DISK_REASSIGN_BLOCKS:
2661 
2662         //
2663         // Map defective blocks to new location on disk.
2664         //
2665 
2666         {
2667 
2668         PREASSIGN_BLOCKS badBlocks = Irp->AssociatedIrp.SystemBuffer;
2669         ULONG bufferSize;
2670         ULONG blockNumber;
2671         ULONG blockCount;
2672 
2673         //
2674         // Validate buffer length.
2675         //
2676 
2677         if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
2678             sizeof(REASSIGN_BLOCKS)) {
2679 
2680             status = STATUS_INFO_LENGTH_MISMATCH;
2681             break;
2682         }
2683 
2684         bufferSize = sizeof(REASSIGN_BLOCKS) +
2685             (badBlocks->Count - 1) * sizeof(ULONG);
2686 
2687         if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
2688             bufferSize) {
2689 
2690             status = STATUS_INFO_LENGTH_MISMATCH;
2691             break;
2692         }
2693 
2694         //
2695         // Build the data buffer to be transferred in the input buffer.
2696         // The format of the data to the device is:
2697         //
2698         //      2 bytes Reserved
2699         //      2 bytes Length
2700         //      x * 4 btyes Block Address
2701         //
2702         // All values are big endian.
2703         //
2704 
2705         badBlocks->Reserved = 0;
2706         blockCount = badBlocks->Count;
2707 
2708         //
2709         // Convert # of entries to # of bytes.
2710         //
2711 
2712         blockCount *= 4;
2713         badBlocks->Count = (USHORT) ((blockCount >> 8) & 0XFF);
2714         badBlocks->Count |= (USHORT) ((blockCount << 8) & 0XFF00);
2715 
2716         //
2717         // Convert back to number of entries.
2718         //
2719 
2720         blockCount /= 4;
2721 
2722         for (; blockCount > 0; blockCount--) {
2723 
2724             blockNumber = badBlocks->BlockNumber[blockCount-1];
2725 
2726             REVERSE_BYTES((PFOUR_BYTE) &badBlocks->BlockNumber[blockCount-1],
2727                           (PFOUR_BYTE) &blockNumber);
2728         }
2729 
2730         srb->CdbLength = 6;
2731 
2732         cdb->CDB6GENERIC.OperationCode = SCSIOP_REASSIGN_BLOCKS;
2733 
2734         //
2735         // Set timeout value.
2736         //
2737 
2738         srb->TimeOutValue = deviceExtension->TimeOutValue;
2739 
2740         status = ScsiClassSendSrbSynchronous(DeviceObject,
2741                                              srb,
2742                                              badBlocks,
2743                                              bufferSize,
2744                                              TRUE);
2745 
2746         Irp->IoStatus.Status = status;
2747         Irp->IoStatus.Information = 0;
2748         ExFreePool(srb);
2749         IoCompleteRequest(Irp, IO_DISK_INCREMENT);
2750         }
2751 
2752         return(status);
2753 
2754     case IOCTL_DISK_IS_WRITABLE:
2755 
2756         //
2757         // Determine if the device is writable.
2758         //
2759 
2760         modeData = ExAllocatePool(NonPagedPoolCacheAligned, MODE_DATA_SIZE);
2761 
2762         if (modeData == NULL) {
2763             status = STATUS_INSUFFICIENT_RESOURCES;
2764             break;
2765         }
2766 
2767         RtlZeroMemory(modeData, MODE_DATA_SIZE);
2768 
2769         length = ScsiClassModeSense(DeviceObject,
2770                                     (PCHAR) modeData,
2771                                     MODE_DATA_SIZE,
2772                                     MODE_SENSE_RETURN_ALL);
2773 
2774         if (length < sizeof(MODE_PARAMETER_HEADER)) {
2775 
2776             //
2777             // Retry the request in case of a check condition.
2778             //
2779 
2780             length = ScsiClassModeSense(DeviceObject,
2781                                         (PCHAR) modeData,
2782                                         MODE_DATA_SIZE,
2783                                         MODE_SENSE_RETURN_ALL);
2784 
2785             if (length < sizeof(MODE_PARAMETER_HEADER)) {
2786                 status = STATUS_IO_DEVICE_ERROR;
2787                 ExFreePool(modeData);
2788                 break;
2789             }
2790         }
2791 
2792         if (modeData->DeviceSpecificParameter & MODE_DSP_WRITE_PROTECT) {
2793             status = STATUS_MEDIA_WRITE_PROTECTED;
2794         } else {
2795             status = STATUS_SUCCESS;
2796         }
2797 
2798         ExFreePool(modeData);
2799         break;
2800 
2801     case IOCTL_DISK_INTERNAL_SET_VERIFY:
2802 
2803         //
2804         // If the caller is kernel mode, set the verify bit.
2805         //
2806 
2807         if (Irp->RequestorMode == KernelMode) {
2808             DeviceObject->Flags |= DO_VERIFY_VOLUME;
2809         }
2810         status = STATUS_SUCCESS;
2811         break;
2812 
2813     case IOCTL_DISK_INTERNAL_CLEAR_VERIFY:
2814 
2815         //
2816         // If the caller is kernel mode, clear the verify bit.
2817         //
2818 
2819         if (Irp->RequestorMode == KernelMode) {
2820             DeviceObject->Flags &= ~DO_VERIFY_VOLUME;
2821         }
2822         status = STATUS_SUCCESS;
2823         break;
2824 
2825     case IOCTL_DISK_FIND_NEW_DEVICES:
2826 
2827         //
2828         // Search for devices that have been powered on since the last
2829         // device search or system initialization.
2830         //
2831 
2832         DebugPrint((3,"CdRomDeviceControl: Find devices\n"));
2833         status = DriverEntry(DeviceObject->DriverObject,
2834                              NULL);
2835 
2836         Irp->IoStatus.Status = status;
2837         ExFreePool(srb);
2838         IoCompleteRequest(Irp, IO_NO_INCREMENT);
2839         return status;
2840 
2841     case IOCTL_DISK_MEDIA_REMOVAL:
2842 
2843         //
2844         // If the disk is not removable then don't allow this command.
2845         //
2846 
2847         if (!(DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA)) {
2848             status = STATUS_INVALID_DEVICE_REQUEST;
2849             break;
2850         }
2851 
2852         //
2853         // Fall through and let the class driver process the request.
2854         //
2855 
2856     case IOCTL_DISK_GET_LENGTH_INFO:
2857 
2858         //
2859         // Validate buffer length.
2860         //
2861 
2862         if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
2863             sizeof(GET_LENGTH_INFORMATION)) {
2864             status = STATUS_BUFFER_TOO_SMALL;
2865 
2866         } else {
2867 
2868             PGET_LENGTH_INFORMATION lengthInformation = Irp->AssociatedIrp.SystemBuffer;
2869 
2870             //
2871             // Update the geometry in case it has changed.
2872             //
2873 
2874             status = UpdateRemovableGeometry (DeviceObject, Irp);
2875 
2876             if (!NT_SUCCESS(status)) {
2877 
2878                 //
2879                 // Note the drive is not ready.
2880                 //
2881 
2882                 diskData->DriveNotReady = TRUE;
2883                 break;
2884             }
2885 
2886             //
2887             // Note the drive is now ready.
2888             //
2889 
2890             diskData->DriveNotReady = FALSE;
2891 
2892             //
2893             // Output data, and return
2894             //
2895 
2896             lengthInformation->Length.QuadPart = deviceExtension->PartitionLength.QuadPart;
2897             status = STATUS_SUCCESS;
2898             Irp->IoStatus.Information = sizeof(GET_LENGTH_INFORMATION);
2899         }
2900 
2901         break;
2902 
2903     default:
2904 
2905         //
2906         // Free the Srb, since it is not needed.
2907         //
2908 
2909         ExFreePool(srb);
2910 
2911         //
2912         // Pass the request to the common device control routine.
2913         //
2914 
2915         return(ScsiClassDeviceControl(DeviceObject, Irp));
2916 
2917         break;
2918 
2919     } // end switch( ...
2920 
2921     Irp->IoStatus.Status = status;
2922 
2923     if (!NT_SUCCESS(status) && IoIsErrorUserInduced(status)) {
2924 
2925         IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);
2926     }
2927 
2928     IoCompleteRequest(Irp, IO_NO_INCREMENT);
2929     ExFreePool(srb);
2930     return(status);
2931 
2932 } // end ScsiDiskDeviceControl()
2933 
2934 NTSTATUS
2935 NTAPI
2936 ScsiDiskShutdownFlush (
2937     IN PDEVICE_OBJECT DeviceObject,
2938     IN PIRP Irp
2939     )
2940 
2941 /*++
2942 
2943 Routine Description:
2944 
2945     This routine is called for a shutdown and flush IRPs.  These are sent by the
2946     system before it actually shuts down or when the file system does a flush.
2947     A synchronize cache command is sent to the device if it is write caching.
2948     If the device is removable an unlock command will be sent. This routine
2949     will sent a shutdown or flush Srb to the port driver.
2950 
2951 Arguments:
2952 
2953     DriverObject - Pointer to device object to being shutdown by system.
2954 
2955     Irp - IRP involved.
2956 
2957 Return Value:
2958 
2959     NT Status
2960 
2961 --*/
2962 
2963 {
2964     PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
2965     PIO_STACK_LOCATION irpStack;
2966     PSCSI_REQUEST_BLOCK srb;
2967     NTSTATUS status;
2968     PCDB cdb;
2969 
2970     //
2971     // Allocate SCSI request block.
2972     //
2973 
2974     srb = ExAllocatePool(NonPagedPool, sizeof(SCSI_REQUEST_BLOCK));
2975 
2976     if (srb == NULL) {
2977 
2978         //
2979         // Set the status and complete the request.
2980         //
2981 
2982         Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
2983         IoCompleteRequest(Irp, IO_NO_INCREMENT);
2984         return(STATUS_INSUFFICIENT_RESOURCES);
2985     }
2986 
2987     RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE);
2988 
2989     //
2990     // Write length to SRB.
2991     //
2992 
2993     srb->Length = SCSI_REQUEST_BLOCK_SIZE;
2994 
2995     //
2996     // Set SCSI bus address.
2997     //
2998 
2999     srb->PathId = deviceExtension->PathId;
3000     srb->TargetId = deviceExtension->TargetId;
3001     srb->Lun = deviceExtension->Lun;
3002 
3003     //
3004     // Set timeout value and mark the request as not being a tagged request.
3005     //
3006 
3007     srb->TimeOutValue = deviceExtension->TimeOutValue * 4;
3008     srb->QueueTag = SP_UNTAGGED;
3009     srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
3010     srb->SrbFlags = deviceExtension->SrbFlags;
3011 
3012     //
3013     // If the write cache is enabled then send a synchronize cache request.
3014     //
3015 
3016     if (deviceExtension->DeviceFlags & DEV_WRITE_CACHE) {
3017 
3018         srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
3019         srb->CdbLength = 10;
3020 
3021         srb->Cdb[0] = SCSIOP_SYNCHRONIZE_CACHE;
3022 
3023         status = ScsiClassSendSrbSynchronous(DeviceObject,
3024                                              srb,
3025                                              NULL,
3026                                              0,
3027                                              TRUE);
3028 
3029         DebugPrint((1, "ScsiDiskShutdownFlush: Synchronize cache sent. Status = %lx\n", status ));
3030     }
3031 
3032     //
3033     // Unlock the device if it is removable and this is a shutdown.
3034     //
3035 
3036     irpStack = IoGetCurrentIrpStackLocation(Irp);
3037 
3038     if (DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA &&
3039         irpStack->MajorFunction == IRP_MJ_SHUTDOWN) {
3040 
3041         srb->CdbLength = 6;
3042         cdb = (PVOID) srb->Cdb;
3043         cdb->MEDIA_REMOVAL.OperationCode = SCSIOP_MEDIUM_REMOVAL;
3044         cdb->MEDIA_REMOVAL.Prevent = FALSE;
3045 
3046         //
3047         // Set timeout value.
3048         //
3049 
3050         srb->TimeOutValue = deviceExtension->TimeOutValue;
3051         status = ScsiClassSendSrbSynchronous(DeviceObject,
3052                                              srb,
3053                                              NULL,
3054                                              0,
3055                                              TRUE);
3056 
3057         DebugPrint((1, "ScsiDiskShutdownFlush: Unlock device request sent. Status = %lx\n", status ));
3058     }
3059 
3060     srb->CdbLength = 0;
3061 
3062     //
3063     // Save a few parameters in the current stack location.
3064     //
3065 
3066     srb->Function = irpStack->MajorFunction == IRP_MJ_SHUTDOWN ?
3067         SRB_FUNCTION_SHUTDOWN : SRB_FUNCTION_FLUSH;
3068 
3069     //
3070     // Set the retry count to zero.
3071     //
3072 
3073     irpStack->Parameters.Others.Argument4 = (PVOID) 0;
3074 
3075     //
3076     // Set up IoCompletion routine address.
3077     //
3078 
3079     IoSetCompletionRoutine(Irp, ScsiClassIoComplete, srb, TRUE, TRUE, TRUE);
3080 
3081     //
3082     // Get next stack location and
3083     // set major function code.
3084     //
3085 
3086     irpStack = IoGetNextIrpStackLocation(Irp);
3087 
3088     irpStack->MajorFunction = IRP_MJ_SCSI;
3089 
3090     //
3091     // Set up SRB for execute scsi request.
3092     // Save SRB address in next stack for port driver.
3093     //
3094 
3095     irpStack->Parameters.Scsi.Srb = srb;
3096 
3097     //
3098     // Set up Irp Address.
3099     //
3100 
3101     srb->OriginalRequest = Irp;
3102 
3103     //
3104     // Call the port driver to process the request.
3105     //
3106 
3107     return(IoCallDriver(deviceExtension->PortDeviceObject, Irp));
3108 
3109 } // end ScsiDiskShutdown()
3110 
3111 
3112 BOOLEAN
3113 NTAPI
3114 IsFloppyDevice(
3115     PDEVICE_OBJECT DeviceObject
3116     )
3117 /*++
3118 
3119 Routine Description:
3120 
3121     The routine performs the necessary functions to determine if a device is
3122     really a floppy rather than a harddisk.  This is done by a mode sense
3123     command.  First, a check is made to see if the media type is set.  Second
3124     a check is made for the flexible parameters mode page.  Also a check is
3125     made to see if the write cache is enabled.
3126 
3127 Arguments:
3128 
3129     DeviceObject - Supplies the device object to be tested.
3130 
3131 Return Value:
3132 
3133     Return TRUE if the indicated device is a floppy.
3134 
3135 --*/
3136 {
3137     PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
3138     PVOID modeData;
3139     PUCHAR pageData;
3140     ULONG length;
3141 
3142     PAGED_CODE();
3143 
3144     modeData = ExAllocatePool(NonPagedPoolCacheAligned, MODE_DATA_SIZE);
3145 
3146     if (modeData == NULL) {
3147         return(FALSE);
3148     }
3149 
3150     RtlZeroMemory(modeData, MODE_DATA_SIZE);
3151 
3152     length = ScsiClassModeSense(DeviceObject,
3153                                 modeData,
3154                                 MODE_DATA_SIZE,
3155                                 MODE_SENSE_RETURN_ALL);
3156 
3157     if (length < sizeof(MODE_PARAMETER_HEADER)) {
3158 
3159         //
3160         // Retry the request in case of a check condition.
3161         //
3162 
3163         length = ScsiClassModeSense(DeviceObject,
3164                                 modeData,
3165                                 MODE_DATA_SIZE,
3166                                 MODE_SENSE_RETURN_ALL);
3167 
3168         if (length < sizeof(MODE_PARAMETER_HEADER)) {
3169 
3170             ExFreePool(modeData);
3171             return(FALSE);
3172 
3173         }
3174     }
3175 
3176     //
3177     // If the length is greater than length indicated by the mode data reset
3178     // the data to the mode data.
3179     //
3180 
3181     if (length > (ULONG) ((PMODE_PARAMETER_HEADER) modeData)->ModeDataLength + 1) {
3182         length = ((PMODE_PARAMETER_HEADER) modeData)->ModeDataLength + 1;
3183     }
3184 
3185     //
3186     // Look for the flexible disk mode page.
3187     //
3188 
3189     pageData = ScsiClassFindModePage( modeData, length, MODE_PAGE_FLEXIBILE, TRUE);
3190 
3191     if (pageData != NULL) {
3192 
3193         DebugPrint((1, "Scsidisk: Flexible disk page found, This is a floppy.\n"));
3194         ExFreePool(modeData);
3195         return(TRUE);
3196     }
3197 
3198     //
3199     // Check to see if the write cache is enabled.
3200     //
3201 
3202     pageData = ScsiClassFindModePage( modeData, length, MODE_PAGE_CACHING, TRUE);
3203 
3204     //
3205     // Assume that write cache is disabled or not supported.
3206     //
3207 
3208     deviceExtension->DeviceFlags &= ~DEV_WRITE_CACHE;
3209 
3210     //
3211     // Check if valid caching page exists.
3212     //
3213 
3214     if (pageData != NULL) {
3215 
3216         //
3217         // Check if write cache is disabled.
3218         //
3219 
3220         if (((PMODE_CACHING_PAGE)pageData)->WriteCacheEnable) {
3221 
3222             DebugPrint((1,
3223                        "SCSIDISK: Disk write cache enabled\n"));
3224 
3225             //
3226             // Check if forced unit access (FUA) is supported.
3227             //
3228 
3229             if (((PMODE_PARAMETER_HEADER)modeData)->DeviceSpecificParameter & MODE_DSP_FUA_SUPPORTED) {
3230 
3231                 deviceExtension->DeviceFlags |= DEV_WRITE_CACHE;
3232 
3233             } else {
3234 
3235                 DebugPrint((1,
3236                            "SCSIDISK: Disk does not support FUA or DPO\n"));
3237 
3238                 //
3239                 // TODO: Log this.
3240                 //
3241 
3242             }
3243         }
3244     }
3245 
3246     ExFreePool(modeData);
3247     return(FALSE);
3248 
3249 } // end IsFloppyDevice()
3250 
3251 
3252 BOOLEAN
3253 NTAPI
3254 ScsiDiskModeSelect(
3255     IN PDEVICE_OBJECT DeviceObject,
3256     IN PCHAR ModeSelectBuffer,
3257     IN ULONG Length,
3258     IN BOOLEAN SavePage
3259     )
3260 
3261 /*++
3262 
3263 Routine Description:
3264 
3265     This routine sends a mode select command.
3266 
3267 Arguments:
3268 
3269     DeviceObject - Supplies the device object associated with this request.
3270 
3271     ModeSelectBuffer - Supplies a buffer containing the page data.
3272 
3273     Length - Supplies the length in bytes of the mode select buffer.
3274 
3275     SavePage - Indicates that parameters should be written to disk.
3276 
3277 Return Value:
3278 
3279     Length of the transferred data is returned.
3280 
3281 --*/
3282 {
3283     PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
3284     PCDB cdb;
3285     SCSI_REQUEST_BLOCK srb;
3286     ULONG retries = 1;
3287     ULONG length2;
3288     NTSTATUS status;
3289     ULONG_PTR buffer;
3290     PMODE_PARAMETER_BLOCK blockDescriptor;
3291 
3292     PAGED_CODE();
3293 
3294     length2 = Length + sizeof(MODE_PARAMETER_HEADER) + sizeof(MODE_PARAMETER_BLOCK);
3295 
3296     //
3297     // Allocate buffer for mode select header, block descriptor, and mode page.
3298     //
3299 
3300     buffer = (ULONG_PTR)ExAllocatePool(NonPagedPoolCacheAligned,length2);
3301 
3302     RtlZeroMemory((PVOID)buffer, length2);
3303 
3304     //
3305     // Set length in header to size of mode page.
3306     //
3307 
3308     ((PMODE_PARAMETER_HEADER)buffer)->BlockDescriptorLength = sizeof(MODE_PARAMETER_BLOCK);
3309 
3310     blockDescriptor = (PMODE_PARAMETER_BLOCK)(buffer + 1);
3311 
3312     //
3313     // Set size
3314     //
3315 
3316     blockDescriptor->BlockLength[1]=0x02;
3317 
3318     //
3319     // Copy mode page to buffer.
3320     //
3321 
3322     RtlCopyMemory((PVOID)(buffer + 3), ModeSelectBuffer, Length);
3323 
3324     //
3325     // Zero SRB.
3326     //
3327 
3328     RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
3329 
3330     //
3331     // Build the MODE SELECT CDB.
3332     //
3333 
3334     srb.CdbLength = 6;
3335     cdb = (PCDB)srb.Cdb;
3336 
3337     //
3338     // Set timeout value from device extension.
3339     //
3340 
3341     srb.TimeOutValue = deviceExtension->TimeOutValue * 2;
3342 
3343     cdb->MODE_SELECT.OperationCode = SCSIOP_MODE_SELECT;
3344     cdb->MODE_SELECT.SPBit = SavePage;
3345     cdb->MODE_SELECT.PFBit = 1;
3346     cdb->MODE_SELECT.ParameterListLength = (UCHAR)(length2);
3347 
3348 Retry:
3349 
3350     status = ScsiClassSendSrbSynchronous(DeviceObject,
3351                                          &srb,
3352                                          (PVOID)buffer,
3353                                          length2,
3354                                          TRUE);
3355 
3356 
3357     if (status == STATUS_VERIFY_REQUIRED) {
3358 
3359         //
3360         // Routine ScsiClassSendSrbSynchronous does not retry requests returned with
3361         // this status.
3362         //
3363 
3364         if (retries--) {
3365 
3366             //
3367             // Retry request.
3368             //
3369 
3370             goto Retry;
3371         }
3372 
3373     } else if (SRB_STATUS(srb.SrbStatus) == SRB_STATUS_DATA_OVERRUN) {
3374         status = STATUS_SUCCESS;
3375     }
3376 
3377     ExFreePool((PVOID)buffer);
3378 
3379     if (NT_SUCCESS(status)) {
3380         return(TRUE);
3381     } else {
3382         return(FALSE);
3383     }
3384 
3385 } // end SciDiskModeSelect()
3386 
3387 
3388 VOID
3389 NTAPI
3390 DisableWriteCache(
3391     IN PDEVICE_OBJECT DeviceObject,
3392     IN PSCSI_INQUIRY_DATA LunInfo
3393     )
3394 
3395 {
3396     PDEVICE_EXTENSION          deviceExtension = DeviceObject->DeviceExtension;
3397     PINQUIRYDATA               InquiryData     = (PINQUIRYDATA)LunInfo->InquiryData;
3398     BAD_CONTROLLER_INFORMATION const *controller;
3399     ULONG                      j,length;
3400     PVOID                      modeData;
3401     PUCHAR                     pageData;
3402 
3403     for (j = 0; j <  NUMBER_OF_BAD_CONTROLLERS; j++) {
3404 
3405         controller = &ScsiDiskBadControllers[j];
3406 
3407         if (!controller->DisableWriteCache || strncmp(controller->InquiryString, (PCCHAR)InquiryData->VendorId, strlen(controller->InquiryString))) {
3408             continue;
3409         }
3410 
3411         DebugPrint((1, "ScsiDisk.DisableWriteCache, Found bad controller! %s\n", controller->InquiryString));
3412 
3413         modeData = ExAllocatePool(NonPagedPoolCacheAligned, MODE_DATA_SIZE);
3414 
3415         if (modeData == NULL) {
3416 
3417             DebugPrint((1,
3418                         "ScsiDisk.DisableWriteCache: Check for write-cache enable failed\n"));
3419             return;
3420         }
3421 
3422         RtlZeroMemory(modeData, MODE_DATA_SIZE);
3423 
3424         length = ScsiClassModeSense(DeviceObject,
3425                                     modeData,
3426                                     MODE_DATA_SIZE,
3427                                     MODE_SENSE_RETURN_ALL);
3428 
3429         if (length < sizeof(MODE_PARAMETER_HEADER)) {
3430 
3431             //
3432             // Retry the request in case of a check condition.
3433             //
3434 
3435             length = ScsiClassModeSense(DeviceObject,
3436                                     modeData,
3437                                     MODE_DATA_SIZE,
3438                                     MODE_SENSE_RETURN_ALL);
3439 
3440             if (length < sizeof(MODE_PARAMETER_HEADER)) {
3441 
3442 
3443                 DebugPrint((1,
3444                             "ScsiDisk.DisableWriteCache: Mode Sense failed\n"));
3445 
3446                 ExFreePool(modeData);
3447                 return;
3448 
3449             }
3450         }
3451 
3452         //
3453         // If the length is greater than length indicated by the mode data reset
3454         // the data to the mode data.
3455         //
3456 
3457         if (length > (ULONG) ((PMODE_PARAMETER_HEADER) modeData)->ModeDataLength + 1) {
3458             length = ((PMODE_PARAMETER_HEADER) modeData)->ModeDataLength + 1;
3459         }
3460 
3461         //
3462         // Check to see if the write cache is enabled.
3463         //
3464 
3465         pageData = ScsiClassFindModePage( modeData, length, MODE_PAGE_CACHING, TRUE);
3466 
3467         //
3468         // Assume that write cache is disabled or not supported.
3469         //
3470 
3471         deviceExtension->DeviceFlags &= ~DEV_WRITE_CACHE;
3472 
3473         //
3474         // Check if valid caching page exists.
3475         //
3476 
3477         if (pageData != NULL) {
3478 
3479             BOOLEAN savePage = FALSE;
3480 
3481             savePage = (BOOLEAN)(((PMODE_CACHING_PAGE)pageData)->PageSavable);
3482 
3483             //
3484             // Check if write cache is disabled.
3485             //
3486 
3487             if (((PMODE_CACHING_PAGE)pageData)->WriteCacheEnable) {
3488 
3489                 PIO_ERROR_LOG_PACKET errorLogEntry;
3490                 LONG                 errorCode;
3491 
3492 
3493                 //
3494                 // Disable write cache and ensure necessary fields are zeroed.
3495                 //
3496 
3497                 ((PMODE_CACHING_PAGE)pageData)->WriteCacheEnable = FALSE;
3498                 ((PMODE_CACHING_PAGE)pageData)->Reserved = 0;
3499                 ((PMODE_CACHING_PAGE)pageData)->PageSavable = 0;
3500                 ((PMODE_CACHING_PAGE)pageData)->Reserved2 = 0;
3501 
3502                 //
3503                 // Extract length from caching page.
3504                 //
3505 
3506                 length = ((PMODE_CACHING_PAGE)pageData)->PageLength;
3507 
3508                 //
3509                 // Compensate for page code and page length.
3510                 //
3511 
3512                 length += 2;
3513 
3514                 //
3515                 // Issue mode select to set the parameter.
3516                 //
3517 
3518                 if (ScsiDiskModeSelect(DeviceObject,
3519                                        (PCHAR)pageData,
3520                                        length,
3521                                        savePage)) {
3522 
3523                     DebugPrint((1,
3524                                "SCSIDISK: Disk write cache disabled\n"));
3525 
3526                     deviceExtension->DeviceFlags &= ~DEV_WRITE_CACHE;
3527                     errorCode = IO_WRITE_CACHE_DISABLED;
3528 
3529                 } else {
3530                     if (ScsiDiskModeSelect(DeviceObject,
3531                                            (PCHAR)pageData,
3532                                            length,
3533                                            savePage)) {
3534 
3535                         DebugPrint((1,
3536                                    "SCSIDISK: Disk write cache disabled\n"));
3537 
3538 
3539                         deviceExtension->DeviceFlags &= ~DEV_WRITE_CACHE;
3540                         errorCode = IO_WRITE_CACHE_DISABLED;
3541 
3542                     } else {
3543 
3544                             DebugPrint((1,
3545                                        "SCSIDISK: Mode select to disable write cache failed\n"));
3546 
3547                             deviceExtension->DeviceFlags |= DEV_WRITE_CACHE;
3548                             errorCode = IO_WRITE_CACHE_ENABLED;
3549                     }
3550                 }
3551 
3552                 //
3553                 // Log the appropriate informational or error entry.
3554                 //
3555 
3556                 errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry(
3557                                                          DeviceObject,
3558                                                          sizeof(IO_ERROR_LOG_PACKET) + 3
3559                                                              * sizeof(ULONG));
3560 
3561                 if (errorLogEntry != NULL) {
3562 
3563                     errorLogEntry->FinalStatus     = STATUS_SUCCESS;
3564                     errorLogEntry->ErrorCode       = errorCode;
3565                     errorLogEntry->SequenceNumber  = 0;
3566                     errorLogEntry->MajorFunctionCode = IRP_MJ_SCSI;
3567                     errorLogEntry->IoControlCode   = 0;
3568                     errorLogEntry->RetryCount      = 0;
3569                     errorLogEntry->UniqueErrorValue = 0x1;
3570                     errorLogEntry->DumpDataSize    = 3 * sizeof(ULONG);
3571                     errorLogEntry->DumpData[0]     = LunInfo->PathId;
3572                     errorLogEntry->DumpData[1]     = LunInfo->TargetId;
3573                     errorLogEntry->DumpData[2]     = LunInfo->Lun;
3574 
3575                     //
3576                     // Write the error log packet.
3577                     //
3578 
3579                     IoWriteErrorLogEntry(errorLogEntry);
3580                 }
3581             }
3582         }
3583 
3584         //
3585         // Found device so exit the loop and return.
3586         //
3587 
3588         break;
3589     }
3590 
3591     return;
3592 }
3593 
3594 
3595 BOOLEAN
3596 NTAPI
3597 CalculateMbrCheckSum(
3598     IN PDEVICE_EXTENSION DeviceExtension,
3599     OUT PULONG Checksum
3600     )
3601 
3602 /*++
3603 
3604 Routine Description:
3605 
3606     Read MBR and calculate checksum.
3607 
3608 Arguments:
3609 
3610     DeviceExtension - Supplies a pointer to the device information for disk.
3611     Checksum - Memory location to return MBR checksum.
3612 
3613 Return Value:
3614 
3615     Returns TRUE if checksum is valid.
3616 
3617 --*/
3618 {
3619     LARGE_INTEGER   sectorZero;
3620     PIRP            irp;
3621     IO_STATUS_BLOCK ioStatus;
3622     KEVENT          event;
3623     NTSTATUS        status;
3624     ULONG           sectorSize;
3625     PULONG          mbr;
3626     ULONG           i;
3627 
3628     PAGED_CODE();
3629     sectorZero.QuadPart = (LONGLONG) 0;
3630 
3631     //
3632     // Create notification event object to be used to signal the inquiry
3633     // request completion.
3634     //
3635 
3636     KeInitializeEvent(&event, NotificationEvent, FALSE);
3637 
3638     //
3639     // Get sector size.
3640     //
3641 
3642     sectorSize = DeviceExtension->DiskGeometry->Geometry.BytesPerSector;
3643 
3644     //
3645     // Make sure sector size is at least 512 bytes.
3646     //
3647 
3648     if (sectorSize < 512) {
3649         sectorSize = 512;
3650     }
3651 
3652     //
3653     // Allocate buffer for sector read.
3654     //
3655 
3656     mbr = ExAllocatePool(NonPagedPoolCacheAligned, sectorSize);
3657 
3658     if (!mbr) {
3659         return FALSE;
3660     }
3661 
3662     //
3663     // Build IRP to read MBR.
3664     //
3665 
3666     irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ,
3667                                        DeviceExtension->DeviceObject,
3668                                        mbr,
3669                                        sectorSize,
3670                                        &sectorZero,
3671                                        &event,
3672                                        &ioStatus );
3673 
3674     if (!irp) {
3675         ExFreePool(mbr);
3676         return FALSE;
3677     }
3678 
3679     //
3680     // Pass request to port driver and wait for request to complete.
3681     //
3682 
3683     status = IoCallDriver(DeviceExtension->DeviceObject,
3684                           irp);
3685 
3686     if (status == STATUS_PENDING) {
3687         KeWaitForSingleObject(&event,
3688                               Suspended,
3689                               KernelMode,
3690                               FALSE,
3691                               NULL);
3692         status = ioStatus.Status;
3693     }
3694 
3695     if (!NT_SUCCESS(status)) {
3696         ExFreePool(mbr);
3697         return FALSE;
3698     }
3699 
3700     //
3701     // Calculate MBR checksum.
3702     //
3703 
3704     *Checksum = 0;
3705 
3706     for (i = 0; i < 128; i++) {
3707         *Checksum += mbr[i];
3708     }
3709 
3710     *Checksum = ~*Checksum + 1;
3711 
3712     ExFreePool(mbr);
3713     return TRUE;
3714 }
3715 
3716 
3717 BOOLEAN
3718 NTAPI
3719 EnumerateBusKey(
3720     IN PDEVICE_EXTENSION DeviceExtension,
3721     HANDLE BusKey,
3722     PULONG DiskNumber
3723     )
3724 
3725 /*++
3726 
3727 Routine Description:
3728 
3729     The routine queries the registry to determine if this disk is visible to
3730     the BIOS.  If the disk is visible to the BIOS, then the geometry information
3731     is updated.
3732 
3733 Arguments:
3734 
3735     DeviceExtension - Supplies a pointer to the device information for disk.
3736     Signature - Unique identifier recorded in MBR.
3737     BusKey - Handle of bus key.
3738     DiskNumber - Returns ordinal of disk as BIOS sees it.
3739 
3740 Return Value:
3741 
3742     TRUE is disk signature matched.
3743 
3744 --*/
3745 {
3746     PDISK_DATA        diskData = (PDISK_DATA)(DeviceExtension + 1);
3747     BOOLEAN           diskFound = FALSE;
3748     OBJECT_ATTRIBUTES objectAttributes;
3749     UNICODE_STRING    unicodeString;
3750     UNICODE_STRING    identifier;
3751     ULONG             busNumber;
3752     ULONG             adapterNumber;
3753     ULONG             diskNumber;
3754     HANDLE            adapterKey;
3755     HANDLE            spareKey;
3756     HANDLE            diskKey;
3757     HANDLE            targetKey;
3758     NTSTATUS          status;
3759     STRING            string;
3760     STRING            anotherString;
3761     ULONG             length;
3762     UCHAR             buffer[20];
3763     PKEY_VALUE_FULL_INFORMATION keyData;
3764 
3765     PAGED_CODE();
3766 
3767     for (busNumber = 0; ; busNumber++) {
3768 
3769         //
3770         // Open controller name key.
3771         //
3772 
3773         sprintf((PCHAR)buffer,
3774                 "%lu",
3775                 busNumber);
3776 
3777         RtlInitString(&string,
3778                       (PCSZ)buffer);
3779 
3780         status = RtlAnsiStringToUnicodeString(&unicodeString,
3781                                               &string,
3782                                               TRUE);
3783 
3784         if (!NT_SUCCESS(status)){
3785             break;
3786         }
3787 
3788         InitializeObjectAttributes(&objectAttributes,
3789                                    &unicodeString,
3790                                    OBJ_CASE_INSENSITIVE,
3791                                    BusKey,
3792                                    (PSECURITY_DESCRIPTOR)NULL);
3793 
3794         status = ZwOpenKey(&spareKey,
3795                            KEY_READ,
3796                            &objectAttributes);
3797 
3798         RtlFreeUnicodeString(&unicodeString);
3799 
3800         if (!NT_SUCCESS(status)) {
3801             break;
3802         }
3803 
3804         //
3805         // Open up controller ordinal key.
3806         //
3807 
3808         RtlInitUnicodeString(&unicodeString, L"DiskController");
3809         InitializeObjectAttributes(&objectAttributes,
3810                                    &unicodeString,
3811                                    OBJ_CASE_INSENSITIVE,
3812                                    spareKey,
3813                                    (PSECURITY_DESCRIPTOR)NULL);
3814 
3815         status = ZwOpenKey(&adapterKey,
3816                            KEY_READ,
3817                            &objectAttributes);
3818 
3819         //
3820         // This could fail even with additional adapters of this type
3821         // to search.
3822         //
3823 
3824         if (!NT_SUCCESS(status)) {
3825             continue;
3826         }
3827 
3828         for (adapterNumber = 0; ; adapterNumber++) {
3829 
3830             //
3831             // Open disk key.
3832             //
3833 
3834             sprintf((PCHAR)buffer,
3835                     "%lu\\DiskPeripheral",
3836                     adapterNumber);
3837 
3838             RtlInitString(&string,
3839                           (PCSZ)buffer);
3840 
3841             status = RtlAnsiStringToUnicodeString(&unicodeString,
3842                                                   &string,
3843                                                   TRUE);
3844 
3845             if (!NT_SUCCESS(status)){
3846                 break;
3847             }
3848 
3849             InitializeObjectAttributes(&objectAttributes,
3850                                        &unicodeString,
3851                                        OBJ_CASE_INSENSITIVE,
3852                                        adapterKey,
3853                                        (PSECURITY_DESCRIPTOR)NULL);
3854 
3855             status = ZwOpenKey(&diskKey,
3856                                KEY_READ,
3857                                &objectAttributes);
3858 
3859             RtlFreeUnicodeString(&unicodeString);
3860 
3861             if (!NT_SUCCESS(status)) {
3862                 break;
3863             }
3864 
3865             for (diskNumber = 0; ; diskNumber++) {
3866 
3867                 sprintf((PCHAR)buffer,
3868                         "%lu",
3869                         diskNumber);
3870 
3871                 RtlInitString(&string,
3872                               (PCSZ)buffer);
3873 
3874                 status = RtlAnsiStringToUnicodeString(&unicodeString,
3875                                                       &string,
3876                                                       TRUE);
3877 
3878                 if (!NT_SUCCESS(status)){
3879                     break;
3880                 }
3881 
3882                 InitializeObjectAttributes(&objectAttributes,
3883                                            &unicodeString,
3884                                            OBJ_CASE_INSENSITIVE,
3885                                            diskKey,
3886                                            (PSECURITY_DESCRIPTOR)NULL);
3887 
3888                 status = ZwOpenKey(&targetKey,
3889                                    KEY_READ,
3890                                    &objectAttributes);
3891 
3892                 RtlFreeUnicodeString(&unicodeString);
3893 
3894                 if (!NT_SUCCESS(status)) {
3895                     break;
3896                 }
3897 
3898                 //
3899                 // Allocate buffer for registry query.
3900                 //
3901 
3902                 keyData = ExAllocatePool(PagedPool, VALUE_BUFFER_SIZE);
3903 
3904                 if (keyData == NULL) {
3905                     ZwClose(targetKey);
3906                     continue;
3907                 }
3908 
3909                 //
3910                 // Get disk peripheral identifier.
3911                 //
3912 
3913                 RtlInitUnicodeString(&unicodeString, L"Identifier");
3914                 status = ZwQueryValueKey(targetKey,
3915                                          &unicodeString,
3916                                          KeyValueFullInformation,
3917                                          keyData,
3918                                          VALUE_BUFFER_SIZE,
3919                                          &length);
3920 
3921                 ZwClose(targetKey);
3922 
3923                 if (!NT_SUCCESS(status)) {
3924                     ExFreePool(keyData);
3925                     continue;
3926                 }
3927 
3928                 if (keyData->DataLength < 9*sizeof(WCHAR)) {
3929                     //
3930                     // the data is too short to use (we subtract 9 chars in normal path)
3931                     //
3932                     DebugPrint((1, "EnumerateBusKey: Saved data was invalid, "
3933                                 "not enough data in registry!\n"));
3934                     ExFreePool(keyData);
3935                     continue;
3936                 }
3937 
3938                 //
3939                 // Complete unicode string.
3940                 //
3941 
3942                 identifier.Buffer =
3943                     (PWSTR)((PUCHAR)keyData + keyData->DataOffset);
3944                 identifier.Length = (USHORT)keyData->DataLength;
3945                 identifier.MaximumLength = (USHORT)keyData->DataLength;
3946 
3947                 //
3948                 // Convert unicode identifier to ansi string.
3949                 //
3950 
3951                 status =
3952                     RtlUnicodeStringToAnsiString(&anotherString,
3953                                                  &identifier,
3954                                                  TRUE);
3955 
3956                 if (!NT_SUCCESS(status)) {
3957                     ExFreePool(keyData);
3958                     continue;
3959                 }
3960 
3961                 //
3962                 // If checksum is zero, then the MBR is valid and
3963                 // the signature is meaningful.
3964                 //
3965 
3966                 if (diskData->MbrCheckSum) {
3967 
3968                     //
3969                     // Convert checksum to ansi string.
3970                     //
3971 
3972                     sprintf((PCHAR)buffer, "%08lx", diskData->MbrCheckSum);
3973 
3974                 } else {
3975 
3976                     //
3977                     // Convert signature to ansi string.
3978                     //
3979 
3980                     sprintf((PCHAR)buffer, "%08lx", diskData->Signature);
3981 
3982                     //
3983                     // Make string point at signature. Can't use scan
3984                     // functions because they are not exported for driver use.
3985                     //
3986 
3987                     anotherString.Buffer+=9;
3988                 }
3989 
3990                 //
3991                 // Convert to ansi string.
3992                 //
3993 
3994                 RtlInitString(&string,
3995                               (PCSZ)buffer);
3996 
3997 
3998                 //
3999                 // Make string lengths equal.
4000                 //
4001 
4002                 anotherString.Length = string.Length;
4003 
4004                 //
4005                 // Check if strings match.
4006                 //
4007 
4008                 if (RtlCompareString(&string,
4009                                      &anotherString,
4010                                      TRUE) == 0)  {
4011 
4012                     diskFound = TRUE;
4013                     *DiskNumber = diskNumber;
4014                 }
4015 
4016                 ExFreePool(keyData);
4017 
4018                 //
4019                 // Readjust identifier string if necessary.
4020                 //
4021 
4022                 if (!diskData->MbrCheckSum) {
4023                     anotherString.Buffer-=9;
4024                 }
4025 
4026                 RtlFreeAnsiString(&anotherString);
4027 
4028                 if (diskFound) {
4029                     break;
4030                 }
4031             }
4032 
4033             ZwClose(diskKey);
4034         }
4035 
4036         ZwClose(adapterKey);
4037     }
4038 
4039     ZwClose(BusKey);
4040     return diskFound;
4041 
4042 } // end EnumerateBusKey()
4043 
4044 
4045 VOID
4046 NTAPI
4047 UpdateGeometry(
4048     IN PDEVICE_EXTENSION DeviceExtension
4049     )
4050 /*++
4051 
4052 Routine Description:
4053 
4054     The routine queries the registry to determine if this disk is visible to
4055     the BIOS.  If the disk is visible to the BIOS, then the geometry information
4056     is updated.
4057 
4058 Arguments:
4059 
4060     DeviceExtension - Supplies a pointer to the device information for disk.
4061 
4062 Return Value:
4063 
4064     None.
4065 
4066 --*/
4067 
4068 {
4069     OBJECT_ATTRIBUTES objectAttributes;
4070     UNICODE_STRING unicodeString;
4071     NTSTATUS status;
4072     HANDLE hardwareKey;
4073     HANDLE busKey;
4074     PCM_INT13_DRIVE_PARAMETER driveParameters;
4075     PCM_FULL_RESOURCE_DESCRIPTOR resourceDescriptor;
4076     PKEY_VALUE_FULL_INFORMATION keyData;
4077     ULONG diskNumber;
4078     PUCHAR buffer;
4079     ULONG length;
4080     ULONG numberOfDrives;
4081     ULONG cylinders;
4082     ULONG sectors;
4083     ULONG sectorsPerTrack;
4084     ULONG tracksPerCylinder;
4085     BOOLEAN foundEZHooker;
4086     PVOID tmpPtr;
4087 
4088     PAGED_CODE();
4089 
4090     //
4091     // Initialize the object for the key.
4092     //
4093 
4094     InitializeObjectAttributes(&objectAttributes,
4095                                DeviceExtension->DeviceObject->DriverObject->HardwareDatabase,
4096                                OBJ_CASE_INSENSITIVE,
4097                                NULL,
4098                                (PSECURITY_DESCRIPTOR) NULL);
4099 
4100     //
4101     // Create the hardware base key.
4102     //
4103 
4104     status =  ZwOpenKey(&hardwareKey,
4105                         KEY_READ,
4106                         &objectAttributes);
4107 
4108 
4109     if (!NT_SUCCESS(status)) {
4110         DebugPrint((1, "ScsiDisk UpdateParameters: Cannot open hardware data. Name: %wZ\n", DeviceExtension->DeviceObject->DriverObject->HardwareDatabase));
4111         return;
4112     }
4113 
4114 
4115     //
4116     // Get disk BIOS geometry information.
4117     //
4118 
4119     RtlInitUnicodeString(&unicodeString, L"Configuration Data");
4120 
4121     keyData = ExAllocatePool(PagedPool, VALUE_BUFFER_SIZE);
4122 
4123     if (keyData == NULL) {
4124         ZwClose(hardwareKey);
4125         return;
4126     }
4127 
4128     status = ZwQueryValueKey(hardwareKey,
4129                              &unicodeString,
4130                              KeyValueFullInformation,
4131                              keyData,
4132                              VALUE_BUFFER_SIZE,
4133                              &length);
4134 
4135     if (!NT_SUCCESS(status)) {
4136         DebugPrint((1,
4137                    "SCSIDISK: ExtractBiosGeometry: Can't query configuration data (%x)\n",
4138                    status));
4139         ZwClose(hardwareKey);
4140         ExFreePool(keyData);
4141         return;
4142     }
4143 
4144     //
4145     // Open EISA bus key.
4146     //
4147 
4148     RtlInitUnicodeString(&unicodeString, L"EisaAdapter");
4149 
4150     InitializeObjectAttributes(&objectAttributes,
4151                                &unicodeString,
4152                                OBJ_CASE_INSENSITIVE,
4153                                hardwareKey,
4154                                (PSECURITY_DESCRIPTOR)NULL);
4155 
4156     status = ZwOpenKey(&busKey,
4157                        KEY_READ,
4158                        &objectAttributes);
4159 
4160     if (!NT_SUCCESS(status)) {
4161         goto openMultiKey;
4162     }
4163 
4164     DebugPrint((3,
4165                "SCSIDISK: UpdateGeometry: Opened EisaAdapter key\n"));
4166     if (EnumerateBusKey(DeviceExtension,
4167                         busKey,
4168                         &diskNumber)) {
4169 
4170         ZwClose(hardwareKey);
4171         goto diskMatched;
4172     }
4173 
4174 openMultiKey:
4175 
4176     //
4177     // Open Multifunction bus key.
4178     //
4179 
4180     RtlInitUnicodeString(&unicodeString, L"MultifunctionAdapter");
4181 
4182     InitializeObjectAttributes(&objectAttributes,
4183                                &unicodeString,
4184                                OBJ_CASE_INSENSITIVE,
4185                                hardwareKey,
4186                                (PSECURITY_DESCRIPTOR)NULL);
4187 
4188     status = ZwOpenKey(&busKey,
4189                        KEY_READ,
4190                        &objectAttributes);
4191 
4192     ZwClose(hardwareKey);
4193     if (NT_SUCCESS(status)) {
4194         DebugPrint((3,
4195                    "SCSIDISK: UpdateGeometry: Opened MultifunctionAdapter key\n"));
4196         if (EnumerateBusKey(DeviceExtension,
4197                             busKey,
4198                             &diskNumber)) {
4199 
4200             goto diskMatched;
4201         }
4202     }
4203 
4204     ExFreePool(keyData);
4205     return;
4206 
4207 diskMatched:
4208 
4209     resourceDescriptor = (PCM_FULL_RESOURCE_DESCRIPTOR)((PUCHAR)keyData +
4210         keyData->DataOffset);
4211 
4212     //
4213     // Check that the data is long enough to hold a full resource descriptor,
4214     // and that the last resource list is device-specific and long enough.
4215     //
4216 
4217     if (keyData->DataLength < sizeof(CM_FULL_RESOURCE_DESCRIPTOR) ||
4218         resourceDescriptor->PartialResourceList.Count == 0 ||
4219         resourceDescriptor->PartialResourceList.PartialDescriptors[0].Type !=
4220         CmResourceTypeDeviceSpecific ||
4221         resourceDescriptor->PartialResourceList.PartialDescriptors[0]
4222             .u.DeviceSpecificData.DataSize < sizeof(ULONG)) {
4223 
4224         DebugPrint((1, "SCSIDISK: ExtractBiosGeometry: BIOS header data too small or invalid\n"));
4225         ExFreePool(keyData);
4226         return;
4227     }
4228 
4229     length =
4230         resourceDescriptor->PartialResourceList.PartialDescriptors[0].u.DeviceSpecificData.DataSize;
4231 
4232     //
4233     // Point to the BIOS data. The BIOS data is located after the first
4234     // partial Resource list which should be device specific data.
4235     //
4236 
4237     buffer = (PUCHAR) keyData + keyData->DataOffset +
4238         sizeof(CM_FULL_RESOURCE_DESCRIPTOR);
4239 
4240 
4241     numberOfDrives = length / sizeof(CM_INT13_DRIVE_PARAMETER);
4242 
4243     //
4244     // Use the defaults if the drive number is greater than the
4245     // number of drives detected by the BIOS.
4246     //
4247 
4248     if (numberOfDrives <= diskNumber) {
4249         ExFreePool(keyData);
4250         return;
4251     }
4252 
4253     //
4254     // Point to the array of drive parameters.
4255     //
4256 
4257     driveParameters = (PCM_INT13_DRIVE_PARAMETER) buffer + diskNumber;
4258     cylinders = driveParameters->MaxCylinders + 1;
4259     sectorsPerTrack = driveParameters->SectorsPerTrack;
4260     tracksPerCylinder = driveParameters->MaxHeads +1;
4261 
4262     //
4263     // Calculate the actual number of sectors.
4264     //
4265 
4266     sectors = (ULONG)(DeviceExtension->PartitionLength.QuadPart >>
4267                                      DeviceExtension->SectorShift);
4268 
4269 #if DBG
4270     if (sectors >= cylinders * tracksPerCylinder * sectorsPerTrack) {
4271         DebugPrint((1, "ScsiDisk: UpdateGeometry: Disk smaller than BIOS indicated\n"
4272             "SCSIDISK: Sectors: %x, Cylinders: %x, Track per Cylinder: %x Sectors per track: %x\n",
4273             sectors, cylinders, tracksPerCylinder, sectorsPerTrack));
4274     }
4275 #endif
4276 
4277     //
4278     // Since the BIOS may not report the full drive, recalculate the drive
4279     // size based on the volume size and the BIOS values for tracks per
4280     // cylinder and sectors per track..
4281     //
4282 
4283     length = tracksPerCylinder * sectorsPerTrack;
4284 
4285     if (length == 0) {
4286 
4287         //
4288         // The BIOS information is bogus.
4289         //
4290 
4291         DebugPrint((1, "ScsiDisk UpdateParameters: sectorPerTrack zero\n"));
4292         ExFreePool(keyData);
4293         return;
4294     }
4295 
4296     cylinders = sectors / length;
4297 
4298     //
4299     // Update the actual geometry information.
4300     //
4301 
4302     DeviceExtension->DiskGeometry->Geometry.SectorsPerTrack = sectorsPerTrack;
4303     DeviceExtension->DiskGeometry->Geometry.TracksPerCylinder = tracksPerCylinder;
4304     DeviceExtension->DiskGeometry->Geometry.Cylinders.QuadPart = (LONGLONG)cylinders;
4305     DeviceExtension->DiskGeometry->DiskSize.QuadPart = (LONGLONG)cylinders * tracksPerCylinder * sectorsPerTrack *
4306                                                        DeviceExtension->DiskGeometry->Geometry.BytesPerSector;
4307 
4308     DebugPrint((3,
4309                "SCSIDISK: UpdateGeometry: BIOS spt %x, #heads %x, #cylinders %x\n",
4310                sectorsPerTrack,
4311                tracksPerCylinder,
4312                cylinders));
4313 
4314     ExFreePool(keyData);
4315 
4316     foundEZHooker = FALSE;
4317 
4318     if (!DeviceExtension->DMActive) {
4319 
4320         HalExamineMBR(DeviceExtension->DeviceObject,
4321                       DeviceExtension->DiskGeometry->Geometry.BytesPerSector,
4322                       (ULONG)0x55,
4323                       &tmpPtr
4324                       );
4325 
4326         if (tmpPtr) {
4327 
4328             ExFreePool(tmpPtr);
4329             foundEZHooker = TRUE;
4330 
4331         }
4332 
4333     }
4334 
4335     if (DeviceExtension->DMActive || foundEZHooker) {
4336 
4337         while (cylinders > 1024) {
4338 
4339             tracksPerCylinder = tracksPerCylinder*2;
4340             cylinders = cylinders/2;
4341 
4342         }
4343 
4344         //
4345         // int 13 values are always 1 less.
4346         //
4347 
4348         tracksPerCylinder -= 1;
4349         cylinders -= 1;
4350 
4351         //
4352         // DM reserves the CE cylinder
4353         //
4354 
4355         cylinders -= 1;
4356 
4357         DeviceExtension->DiskGeometry->Geometry.Cylinders.QuadPart = cylinders + 1;
4358         DeviceExtension->DiskGeometry->Geometry.TracksPerCylinder = tracksPerCylinder + 1;
4359 
4360         DeviceExtension->PartitionLength.QuadPart =
4361         DeviceExtension->DiskGeometry->DiskSize.QuadPart =
4362             DeviceExtension->DiskGeometry->Geometry.Cylinders.QuadPart *
4363                 DeviceExtension->DiskGeometry->Geometry.SectorsPerTrack *
4364                 DeviceExtension->DiskGeometry->Geometry.BytesPerSector *
4365                 DeviceExtension->DiskGeometry->Geometry.TracksPerCylinder;
4366 
4367         if (DeviceExtension->DMActive) {
4368 
4369             DeviceExtension->DMByteSkew = DeviceExtension->DMSkew * DeviceExtension->DiskGeometry->Geometry.BytesPerSector;
4370 
4371         }
4372 
4373     } else {
4374 
4375         DeviceExtension->DMByteSkew = 0;
4376 
4377     }
4378 
4379     return;
4380 
4381 } // end UpdateGeometry()
4382 
4383 
4384 
4385 NTSTATUS
4386 NTAPI
4387 UpdateRemovableGeometry (
4388     IN PDEVICE_OBJECT DeviceObject,
4389     IN PIRP Irp
4390     )
4391 
4392 /*++
4393 
4394 Routine Description:
4395 
4396     This routines updates the size and starting offset of the device.  This is
4397     used when the media on the device may have changed thereby changing the
4398     size of the device.  If this is the physical device then a
4399     ScsiClassReadDriveCapacity is done; otherewise, a read partition table is done.
4400 
4401 Arguments:
4402 
4403     DeviceObject - Supplies the device object whos size needs to be updated.
4404 
4405     Irp - Supplies a reference where the status can be updated.
4406 
4407 Return Value:
4408 
4409     Returns the status of the operation.
4410 
4411 --*/
4412 {
4413 
4414     PDEVICE_EXTENSION         deviceExtension = DeviceObject->DeviceExtension;
4415     PDRIVE_LAYOUT_INFORMATION partitionList;
4416     NTSTATUS                  status;
4417     PDISK_DATA                diskData;
4418     ULONG                     partitionNumber;
4419 
4420     //
4421     // Determine if the size of the partition may have changed because
4422     // the media has changed.
4423     //
4424 
4425     if (!(DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA)) {
4426 
4427         return(STATUS_SUCCESS);
4428 
4429     }
4430 
4431     //
4432     // If this request is for partition zero then do a read drive
4433     // capacity otherwise do a I/O read partition table.
4434     //
4435 
4436     diskData = (PDISK_DATA) (deviceExtension + 1);
4437 
4438     //
4439     // Read the drive capacity.  If that fails, give up.
4440     //
4441 
4442     status = ScsiClassReadDriveCapacity(deviceExtension->PhysicalDevice);
4443 
4444     if (!NT_SUCCESS(status)) {
4445         return(status);
4446     }
4447 
4448     //
4449     // Read the partition table again.
4450     //
4451 
4452     status = IoReadPartitionTable(deviceExtension->PhysicalDevice,
4453                       deviceExtension->DiskGeometry->Geometry.BytesPerSector,
4454                       TRUE,
4455                       &partitionList);
4456 
4457 
4458     if (!NT_SUCCESS(status)) {
4459 
4460         //
4461         // Fail the request.
4462         //
4463 
4464         return(status);
4465     }
4466 
4467     if (diskData->PartitionNumber != 0 &&
4468         diskData->PartitionNumber <= partitionList->PartitionCount ) {
4469 
4470         partitionNumber = diskData->PartitionNumber - 1;
4471 
4472         //
4473         // Update the partition information for this partition.
4474         //
4475 
4476         diskData->PartitionType =
4477             partitionList->PartitionEntry[partitionNumber].PartitionType;
4478 
4479         diskData->BootIndicator =
4480             partitionList->PartitionEntry[partitionNumber].BootIndicator;
4481 
4482         deviceExtension->StartingOffset =
4483             partitionList->PartitionEntry[partitionNumber].StartingOffset;
4484 
4485         deviceExtension->PartitionLength =
4486             partitionList->PartitionEntry[partitionNumber].PartitionLength;
4487 
4488         diskData->HiddenSectors =
4489             partitionList->PartitionEntry[partitionNumber].HiddenSectors;
4490 
4491         deviceExtension->SectorShift = ((PDEVICE_EXTENSION)
4492             deviceExtension->PhysicalDevice->DeviceExtension)->SectorShift;
4493 
4494     } else if (diskData->PartitionNumber != 0) {
4495 
4496         //
4497         // The partition does not exist.  Zero all the data.
4498         //
4499 
4500         diskData->PartitionType = 0;
4501         diskData->BootIndicator = 0;
4502         diskData->HiddenSectors = 0;
4503         deviceExtension->StartingOffset.QuadPart  = (LONGLONG)0;
4504         deviceExtension->PartitionLength.QuadPart = (LONGLONG)0;
4505     }
4506 
4507     //
4508     // Free the partition list allocate by I/O read partition table.
4509     //
4510 
4511     ExFreePool(partitionList);
4512 
4513 
4514     return(STATUS_SUCCESS);
4515 }
4516 
4517 
4518 VOID
4519 NTAPI
4520 ScsiDiskProcessError(
4521     PDEVICE_OBJECT DeviceObject,
4522     PSCSI_REQUEST_BLOCK Srb,
4523     NTSTATUS *Status,
4524     BOOLEAN *Retry
4525     )
4526 /*++
4527 
4528 Routine Description:
4529 
4530    This routine checks the type of error.  If the error indicates an underrun
4531    then indicate the request should be retried.
4532 
4533 Arguments:
4534 
4535     DeviceObject - Supplies a pointer to the device object.
4536 
4537     Srb - Supplies a pointer to the failing Srb.
4538 
4539     Status - Status with which the IRP will be completed.
4540 
4541     Retry - Indication of whether the request will be retried.
4542 
4543 Return Value:
4544 
4545     None.
4546 
4547 --*/
4548 
4549 {
4550     PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
4551 
4552     if (*Status == STATUS_DATA_OVERRUN &&
4553         ( Srb->Cdb[0] == SCSIOP_WRITE || Srb->Cdb[0] == SCSIOP_READ)) {
4554 
4555             *Retry = TRUE;
4556 
4557             //
4558             // Update the error count for the device.
4559             //
4560 
4561             deviceExtension->ErrorCount++;
4562     }
4563 
4564     if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_ERROR &&
4565         Srb->ScsiStatus == SCSISTAT_BUSY) {
4566 
4567         //
4568         // The disk drive should never be busy this long. Reset the scsi bus
4569         // maybe this will clear the condition.
4570         //
4571 
4572         ResetScsiBus(DeviceObject);
4573 
4574         //
4575         // Update the error count for the device.
4576         //
4577 
4578         deviceExtension->ErrorCount++;
4579     }
4580 }
4581 
4582 VOID
4583 NTAPI
4584 ScanForSpecial(
4585     PDEVICE_OBJECT DeviceObject,
4586     PSCSI_INQUIRY_DATA LunInfo,
4587     PIO_SCSI_CAPABILITIES PortCapabilities
4588     )
4589 
4590 /*++
4591 
4592 Routine Description:
4593 
4594     This function checks to see if an SCSI logical unit requires special
4595     flags to be set.
4596 
4597 Arguments:
4598 
4599     DeviceObject - Supplies the device object to be tested.
4600 
4601     InquiryData - Supplies the inquiry data returned by the device of interest.
4602 
4603     PortCapabilities - Supplies the capabilities of the device object.
4604 
4605 Return Value:
4606 
4607     None.
4608 
4609 --*/
4610 
4611 {
4612     PDEVICE_EXTENSION          deviceExtension = DeviceObject->DeviceExtension;
4613     PINQUIRYDATA               InquiryData     = (PINQUIRYDATA)LunInfo->InquiryData;
4614     BAD_CONTROLLER_INFORMATION const *controller;
4615     ULONG                      j;
4616 
4617     for (j = 0; j <  NUMBER_OF_BAD_CONTROLLERS; j++) {
4618 
4619         controller = &ScsiDiskBadControllers[j];
4620 
4621         if (strncmp(controller->InquiryString, (PCCHAR)InquiryData->VendorId, strlen(controller->InquiryString))) {
4622             continue;
4623         }
4624 
4625         DebugPrint((1, "ScsiDisk ScanForSpecial, Found bad controller! %s\n", controller->InquiryString));
4626 
4627         //
4628         // Found a listed controller.  Determine what must be done.
4629         //
4630 
4631         if (controller->DisableTaggedQueuing) {
4632 
4633             //
4634             // Disable tagged queuing.
4635             //
4636 
4637             deviceExtension->SrbFlags &= ~SRB_FLAGS_QUEUE_ACTION_ENABLE;
4638         }
4639 
4640         if (controller->DisableSynchronousTransfers) {
4641 
4642             //
4643             // Disable synchronous data transfers.
4644             //
4645 
4646             deviceExtension->SrbFlags |= SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
4647 
4648         }
4649 
4650         if (controller->DisableDisconnects) {
4651 
4652             //
4653             // Disable disconnects.
4654             //
4655 
4656             deviceExtension->SrbFlags |= SRB_FLAGS_DISABLE_DISCONNECT;
4657 
4658         }
4659 
4660         //
4661         // Found device so exit the loop and return.
4662         //
4663 
4664         break;
4665     }
4666 
4667     //
4668     // Set the StartUnit flag appropriately.
4669     //
4670 
4671     if (DeviceObject->DeviceType == FILE_DEVICE_DISK) {
4672         deviceExtension->DeviceFlags |= DEV_SAFE_START_UNIT;
4673 
4674         if (DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) {
4675             if (_strnicmp((PCCHAR)InquiryData->VendorId, "iomega", strlen("iomega"))) {
4676                 deviceExtension->DeviceFlags &= ~DEV_SAFE_START_UNIT;
4677             }
4678         }
4679     }
4680 
4681     return;
4682 }
4683 
4684 VOID
4685 NTAPI
4686 ResetScsiBus(
4687     IN PDEVICE_OBJECT DeviceObject
4688     )
4689 
4690 /*++
4691 
4692 Routine Description:
4693 
4694     This command sends a reset bus command to the SCSI port driver.
4695 
4696 Arguments:
4697 
4698     DeviceObject - The device object for the logical unit with
4699         hardware problem.
4700 
4701 Return Value:
4702 
4703     None.
4704 
4705 --*/
4706 {
4707     PIO_STACK_LOCATION irpStack;
4708     PIRP irp;
4709     PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
4710     PSCSI_REQUEST_BLOCK srb;
4711     PCOMPLETION_CONTEXT context;
4712 
4713     DebugPrint((1, "ScsiDisk ResetScsiBus: Sending reset bus request to port driver.\n"));
4714 
4715     //
4716     // Allocate Srb from nonpaged pool.
4717     //
4718 
4719     context = ExAllocatePool(NonPagedPoolMustSucceed,
4720                              sizeof(COMPLETION_CONTEXT));
4721 
4722     //
4723     // Save the device object in the context for use by the completion
4724     // routine.
4725     //
4726 
4727     context->DeviceObject = DeviceObject;
4728     srb = &context->Srb;
4729 
4730     //
4731     // Zero out srb.
4732     //
4733 
4734     RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE);
4735 
4736     //
4737     // Write length to SRB.
4738     //
4739 
4740     srb->Length = SCSI_REQUEST_BLOCK_SIZE;
4741 
4742     //
4743     // Set up SCSI bus address.
4744     //
4745 
4746     srb->PathId = deviceExtension->PathId;
4747     srb->TargetId = deviceExtension->TargetId;
4748     srb->Lun = deviceExtension->Lun;
4749 
4750     srb->Function = SRB_FUNCTION_RESET_BUS;
4751 
4752     //
4753     // Build the asynchronous request to be sent to the port driver.
4754     // Since this routine is called from a DPC the IRP should always be
4755     // available.
4756     //
4757 
4758     irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
4759 
4760     IoSetCompletionRoutine(irp,
4761                            (PIO_COMPLETION_ROUTINE)ScsiClassAsynchronousCompletion,
4762                            context,
4763                            TRUE,
4764                            TRUE,
4765                            TRUE);
4766 
4767     irpStack = IoGetNextIrpStackLocation(irp);
4768 
4769     irpStack->MajorFunction = IRP_MJ_SCSI;
4770 
4771     srb->OriginalRequest = irp;
4772 
4773     //
4774     // Store the SRB address in next stack for port driver.
4775     //
4776 
4777     irpStack->Parameters.Scsi.Srb = srb;
4778 
4779     //
4780     // Call the port driver with the IRP.
4781     //
4782 
4783     IoCallDriver(deviceExtension->PortDeviceObject, irp);
4784 
4785     return;
4786 
4787 } // end ResetScsiBus()
4788 
4789 
4790 VOID
4791 NTAPI
4792 UpdateDeviceObjects(
4793     IN PDEVICE_OBJECT PhysicalDisk,
4794     IN PIRP Irp
4795     )
4796 
4797 /*++
4798 
4799 Routine Description:
4800 
4801     This routine creates, deletes and changes device objects when
4802     the IOCTL_SET_DRIVE_LAYOUT is called.  This routine also updates
4803     the drive layout information for the user.  It is possible to
4804     call this routine even in the GET_LAYOUT case because RewritePartition
4805     will be false.
4806 
4807 Arguments:
4808 
4809     DeviceObject - Device object for physical disk.
4810     Irp - IO Request Packet (IRP).
4811 
4812 Return Value:
4813 
4814     None.
4815 
4816 --*/
4817 {
4818     PDEVICE_EXTENSION         physicalExtension = PhysicalDisk->DeviceExtension;
4819     PDRIVE_LAYOUT_INFORMATION partitionList = Irp->AssociatedIrp.SystemBuffer;
4820     ULONG                     partition;
4821     ULONG                     partitionNumber;
4822     ULONG                     partitionCount;
4823     ULONG                     lastPartition;
4824     ULONG                     partitionOrdinal;
4825     PPARTITION_INFORMATION    partitionEntry;
4826     CCHAR                     ntNameBuffer[MAXIMUM_FILENAME_LENGTH];
4827     STRING                    ntNameString;
4828     UNICODE_STRING            ntUnicodeString;
4829     PDEVICE_OBJECT            deviceObject;
4830     PDEVICE_EXTENSION         deviceExtension;
4831     PDISK_DATA                diskData;
4832     NTSTATUS                  status;
4833     ULONG                     numberListElements;
4834     BOOLEAN                   found;
4835 
4836     partitionCount = ((partitionList->PartitionCount + 3) / 4) * 4;
4837 
4838     //
4839     // Zero all of the partition numbers.
4840     //
4841 
4842     for (partition = 0; partition < partitionCount; partition++) {
4843         partitionEntry = &partitionList->PartitionEntry[partition];
4844         partitionEntry->PartitionNumber = 0;
4845     }
4846 
4847     //
4848     // Walk through chain of partitions for this disk to determine
4849     // which existing partitions have no match.
4850     //
4851 
4852     deviceExtension = physicalExtension;
4853     diskData = (PDISK_DATA)(deviceExtension + 1);
4854     lastPartition = 0;
4855 
4856     do {
4857 
4858         deviceExtension = diskData->NextPartition;
4859 
4860         //
4861         // Check if this is the last partition in the chain.
4862         //
4863 
4864         if (!deviceExtension) {
4865            break;
4866         }
4867 
4868         //
4869         // Get the partition device extension from disk data.
4870         //
4871 
4872         diskData = (PDISK_DATA)(deviceExtension + 1);
4873 
4874         //
4875         // Check for highest partition number this far.
4876         //
4877 
4878         if (diskData->PartitionNumber > lastPartition) {
4879            lastPartition = diskData->PartitionNumber;
4880         }
4881 
4882         //
4883         // Check if this partition is not currently being used.
4884         //
4885 
4886         if (!deviceExtension->PartitionLength.QuadPart) {
4887            continue;
4888         }
4889 
4890         //
4891         // Loop through partition information to look for match.
4892         //
4893 
4894         found = FALSE;
4895         partitionOrdinal = 0;
4896 
4897         for (partition = 0; partition < partitionCount; partition++) {
4898 
4899             //
4900             // Get partition descriptor.
4901             //
4902 
4903             partitionEntry = &partitionList->PartitionEntry[partition];
4904 
4905             //
4906             // Check if empty, or describes extended partition or hasn't changed.
4907             //
4908 
4909             if (partitionEntry->PartitionType == PARTITION_ENTRY_UNUSED ||
4910                 IsContainerPartition(partitionEntry->PartitionType)) {
4911                 continue;
4912             }
4913 
4914             //
4915             // Advance partition ordinal.
4916             //
4917 
4918             partitionOrdinal++;
4919 
4920             //
4921             // Check if new partition starts where this partition starts.
4922             //
4923 
4924             if (partitionEntry->StartingOffset.QuadPart !=
4925                       deviceExtension->StartingOffset.QuadPart) {
4926                 continue;
4927             }
4928 
4929             //
4930             // Check if partition length is the same.
4931             //
4932 
4933             if (partitionEntry->PartitionLength.QuadPart ==
4934                       deviceExtension->PartitionLength.QuadPart) {
4935 
4936                 DebugPrint((3,
4937                            "UpdateDeviceObjects: Found match for \\Harddisk%d\\Partition%d\n",
4938                            physicalExtension->DeviceNumber,
4939                            diskData->PartitionNumber));
4940 
4941                 //
4942                 // Indicate match is found and set partition number
4943                 // in user buffer.
4944                 //
4945 
4946                 found = TRUE;
4947                 partitionEntry->PartitionNumber = diskData->PartitionNumber;
4948                 break;
4949             }
4950         }
4951 
4952         if (found) {
4953 
4954             //
4955             // A match is found.
4956             //
4957 
4958             diskData = (PDISK_DATA)(deviceExtension + 1);
4959 
4960             //
4961             // If this partition is marked for update then update partition type.
4962             //
4963 
4964             if (partitionEntry->RewritePartition) {
4965                 diskData->PartitionType = partitionEntry->PartitionType;
4966             }
4967 
4968             //
4969             // Update partitional ordinal for calls to HAL routine
4970             // IoSetPartitionInformation.
4971             //
4972 
4973             diskData->PartitionOrdinal = partitionOrdinal;
4974 
4975             DebugPrint((1,
4976                        "UpdateDeviceObjects: Disk %d ordinal %d is partition %d\n",
4977                        physicalExtension->DeviceNumber,
4978                        diskData->PartitionOrdinal,
4979                        diskData->PartitionNumber));
4980 
4981         } else {
4982 
4983             //
4984             // no match was found, indicate this partition is gone.
4985             //
4986 
4987             DebugPrint((1,
4988                        "UpdateDeviceObjects: Deleting \\Device\\Harddisk%x\\Partition%x\n",
4989                        physicalExtension->DeviceNumber,
4990                        diskData->PartitionNumber));
4991 
4992             deviceExtension->PartitionLength.QuadPart = (LONGLONG) 0;
4993         }
4994 
4995     } while (TRUE);
4996 
4997     //
4998     // Walk through partition loop to find new partitions and set up
4999     // device extensions to describe them. In some cases new device
5000     // objects will be created.
5001     //
5002 
5003     partitionOrdinal = 0;
5004 
5005     for (partition = 0;
5006          partition < partitionCount;
5007          partition++) {
5008 
5009         //
5010         // Get partition descriptor.
5011         //
5012 
5013         partitionEntry = &partitionList->PartitionEntry[partition];
5014 
5015         //
5016         // Check if empty, or describes an extended partition.
5017         //
5018 
5019         if (partitionEntry->PartitionType == PARTITION_ENTRY_UNUSED ||
5020             IsContainerPartition(partitionEntry->PartitionType)) {
5021             continue;
5022         }
5023 
5024         //
5025         // Keep track of position on the disk for calls to IoSetPartitionInformation.
5026         //
5027 
5028         partitionOrdinal++;
5029 
5030         //
5031         // Check if this entry should be rewritten.
5032         //
5033 
5034         if (!partitionEntry->RewritePartition) {
5035             continue;
5036         }
5037 
5038         if (partitionEntry->PartitionNumber) {
5039 
5040             //
5041             // Partition is an exact match with an existing partition, but is
5042             // being written anyway.
5043             //
5044 
5045             continue;
5046         }
5047 
5048         //
5049         // Check first if existing device object is available by
5050         // walking partition extension list.
5051         //
5052 
5053         partitionNumber = 0;
5054         deviceExtension = physicalExtension;
5055         diskData = (PDISK_DATA)(deviceExtension + 1);
5056 
5057         do {
5058 
5059             //
5060             // Get next partition device extension from disk data.
5061             //
5062 
5063             deviceExtension = diskData->NextPartition;
5064 
5065             if (!deviceExtension) {
5066                break;
5067             }
5068 
5069             diskData = (PDISK_DATA)(deviceExtension + 1);
5070 
5071             //
5072             // A device object is free if the partition length is set to zero.
5073             //
5074 
5075             if (!deviceExtension->PartitionLength.QuadPart) {
5076                partitionNumber = diskData->PartitionNumber;
5077                break;
5078             }
5079 
5080         } while (TRUE);
5081 
5082         //
5083         // If partition number is still zero then a new device object
5084         // must be created.
5085         //
5086 
5087         if (partitionNumber == 0) {
5088 
5089             lastPartition++;
5090             partitionNumber = lastPartition;
5091 
5092             //
5093             // Get or create partition object and set up partition parameters.
5094             //
5095 
5096             sprintf(ntNameBuffer,
5097                     "\\Device\\Harddisk%lu\\Partition%lu",
5098                     physicalExtension->DeviceNumber,
5099                     partitionNumber);
5100 
5101             RtlInitString(&ntNameString,
5102                           ntNameBuffer);
5103 
5104             status = RtlAnsiStringToUnicodeString(&ntUnicodeString,
5105                                                   &ntNameString,
5106                                                   TRUE);
5107 
5108             if (!NT_SUCCESS(status)) {
5109                 continue;
5110             }
5111 
5112             DebugPrint((3,
5113                         "UpdateDeviceObjects: Create device object %s\n",
5114                         ntNameBuffer));
5115 
5116             //
5117             // This is a new name. Create the device object to represent it.
5118             //
5119 
5120             status = IoCreateDevice(PhysicalDisk->DriverObject,
5121                                     DEVICE_EXTENSION_SIZE,
5122                                     &ntUnicodeString,
5123                                     FILE_DEVICE_DISK,
5124                                     0,
5125                                     FALSE,
5126                                     &deviceObject);
5127 
5128             if (!NT_SUCCESS(status)) {
5129                 DebugPrint((1,
5130                             "UpdateDeviceObjects: Can't create device %s\n",
5131                             ntNameBuffer));
5132                 RtlFreeUnicodeString(&ntUnicodeString);
5133                 continue;
5134             }
5135 
5136             //
5137             // Set up device object fields.
5138             //
5139 
5140             deviceObject->Flags |= DO_DIRECT_IO;
5141             deviceObject->StackSize = PhysicalDisk->StackSize;
5142 
5143             //
5144             // Set up device extension fields.
5145             //
5146 
5147             deviceExtension = deviceObject->DeviceExtension;
5148 
5149             //
5150             // Copy physical disk extension to partition extension.
5151             //
5152 
5153             RtlMoveMemory(deviceExtension,
5154                           physicalExtension,
5155                           sizeof(DEVICE_EXTENSION));
5156 
5157             //
5158             // Initialize the new S-List.
5159             //
5160 
5161             if (deviceExtension->SrbFlags & SRB_FLAGS_QUEUE_ACTION_ENABLE) {
5162                 numberListElements = 30;
5163             } else {
5164                 numberListElements = 8;
5165             }
5166 
5167             //
5168             // Build the lookaside list for srb's for this partition based on
5169             // whether the adapter and disk can do tagged queueing.
5170             //
5171 
5172             ScsiClassInitializeSrbLookasideList(deviceExtension,
5173                                                 numberListElements);
5174 
5175             //
5176             // Allocate spinlock for zoning for split-request completion.
5177             //
5178 
5179             KeInitializeSpinLock(&deviceExtension->SplitRequestSpinLock);
5180 
5181             //
5182             // Write back partition number used in creating object name.
5183             //
5184 
5185             partitionEntry->PartitionNumber = partitionNumber;
5186 
5187             //
5188             // Clear flags initializing bit.
5189             //
5190 
5191             deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
5192 
5193             //
5194             // Point back at device object.
5195             //
5196 
5197             deviceExtension->DeviceObject = deviceObject;
5198 
5199             RtlFreeUnicodeString(&ntUnicodeString);
5200 
5201             //
5202             // Link to end of partition chain using previous disk data.
5203             //
5204 
5205             diskData->NextPartition = deviceExtension;
5206 
5207             //
5208             // Get new disk data and zero next partition pointer.
5209             //
5210 
5211             diskData = (PDISK_DATA)(deviceExtension + 1);
5212             diskData->NextPartition = NULL;
5213 
5214         } else {
5215 
5216             //
5217             // Set pointer to disk data area that follows device extension.
5218             //
5219 
5220             diskData = (PDISK_DATA)(deviceExtension + 1);
5221 
5222             DebugPrint((1,
5223                         "UpdateDeviceObjects: Used existing device object \\Device\\Harddisk%x\\Partition%x\n",
5224                         physicalExtension->DeviceNumber,
5225                         partitionNumber));
5226         }
5227 
5228         //
5229         // Update partition information in partition device extension.
5230         //
5231 
5232         diskData->PartitionNumber = partitionNumber;
5233         diskData->PartitionType = partitionEntry->PartitionType;
5234         diskData->BootIndicator = partitionEntry->BootIndicator;
5235         deviceExtension->StartingOffset = partitionEntry->StartingOffset;
5236         deviceExtension->PartitionLength = partitionEntry->PartitionLength;
5237         diskData->HiddenSectors = partitionEntry->HiddenSectors;
5238         diskData->PartitionOrdinal = partitionOrdinal;
5239 
5240         DebugPrint((1,
5241                    "UpdateDeviceObjects: Ordinal %d is partition %d\n",
5242                    diskData->PartitionOrdinal,
5243                    diskData->PartitionNumber));
5244 
5245         //
5246         // Update partition number passed in to indicate the
5247         // device name for this partition.
5248         //
5249 
5250         partitionEntry->PartitionNumber = partitionNumber;
5251     }
5252 
5253 } // end UpdateDeviceObjects()
5254 
5255