xref: /reactos/drivers/storage/class/disk/disk.c (revision 595b846d)
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             break;
2239         }
2240 
2241         //
2242         // Update the geometry in case it has changed.
2243         //
2244 
2245         status = UpdateRemovableGeometry (DeviceObject, Irp);
2246 
2247         if (!NT_SUCCESS(status)) {
2248 
2249             //
2250             // Note the drive is not ready.
2251             //
2252 
2253             diskData->DriveNotReady = TRUE;
2254             break;
2255         }
2256 
2257         //
2258         // Note the drive is now ready.
2259         //
2260 
2261         diskData->DriveNotReady = FALSE;
2262 
2263         //
2264         // Handle the case were we query the whole disk
2265         //
2266 
2267         if (diskData->PartitionNumber == 0) {
2268 
2269             PPARTITION_INFORMATION outputBuffer;
2270 
2271             outputBuffer =
2272                     (PPARTITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer;
2273 
2274             outputBuffer->PartitionType = PARTITION_ENTRY_UNUSED;
2275             outputBuffer->StartingOffset = deviceExtension->StartingOffset;
2276             outputBuffer->PartitionLength.QuadPart = deviceExtension->PartitionLength.QuadPart;
2277             outputBuffer->HiddenSectors = 0;
2278             outputBuffer->PartitionNumber = diskData->PartitionNumber;
2279             outputBuffer->BootIndicator = FALSE;
2280             outputBuffer->RewritePartition = FALSE;
2281             outputBuffer->RecognizedPartition = FALSE;
2282 
2283             status = STATUS_SUCCESS;
2284             Irp->IoStatus.Information = sizeof(PARTITION_INFORMATION);
2285 
2286         } else {
2287 
2288             PPARTITION_INFORMATION outputBuffer;
2289 
2290             //
2291             // We query a single partition here
2292             // FIXME: this can only work for MBR-based disks, check for this!
2293             //
2294 
2295             outputBuffer =
2296                     (PPARTITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer;
2297 
2298             outputBuffer->PartitionType = diskData->PartitionType;
2299             outputBuffer->StartingOffset = deviceExtension->StartingOffset;
2300             outputBuffer->PartitionLength.QuadPart = deviceExtension->PartitionLength.QuadPart;
2301             outputBuffer->HiddenSectors = diskData->HiddenSectors;
2302             outputBuffer->PartitionNumber = diskData->PartitionNumber;
2303             outputBuffer->BootIndicator = diskData->BootIndicator;
2304             outputBuffer->RewritePartition = FALSE;
2305             outputBuffer->RecognizedPartition =
2306                 IsRecognizedPartition(diskData->PartitionType);
2307 
2308             status = STATUS_SUCCESS;
2309             Irp->IoStatus.Information = sizeof(PARTITION_INFORMATION);
2310         }
2311 
2312         break;
2313 
2314     case IOCTL_DISK_GET_PARTITION_INFO_EX:
2315 
2316         //
2317         // Return the information about the partition specified by the device
2318         // object.  Note that no information is ever returned about the size
2319         // or partition type of the physical disk, as this doesn't make any
2320         // sense.
2321         //
2322 
2323         if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
2324             sizeof(PARTITION_INFORMATION_EX)) {
2325 
2326             status = STATUS_INFO_LENGTH_MISMATCH;
2327 
2328         }
2329 #if 0 // HACK: ReactOS partition numbers must be wrong
2330         else if (diskData->PartitionNumber == 0) {
2331 
2332             //
2333             // Partition zero is not a partition so this is not a
2334             // reasonable request.
2335             //
2336 
2337             status = STATUS_INVALID_DEVICE_REQUEST;
2338 
2339         }
2340 #endif
2341         else {
2342 
2343             PPARTITION_INFORMATION_EX outputBuffer;
2344 
2345             if (diskData->PartitionNumber == 0) {
2346                 DPRINT1("HACK: Handling partition 0 request!\n");
2347                 //ASSERT(FALSE);
2348             }
2349 
2350             //
2351             // Update the geometry in case it has changed.
2352             //
2353 
2354             status = UpdateRemovableGeometry (DeviceObject, Irp);
2355 
2356             if (!NT_SUCCESS(status)) {
2357 
2358                 //
2359                 // Note the drive is not ready.
2360                 //
2361 
2362                 diskData->DriveNotReady = TRUE;
2363                 break;
2364             }
2365 
2366             //
2367             // Note the drive is now ready.
2368             //
2369 
2370             diskData->DriveNotReady = FALSE;
2371 
2372             if (diskData->PartitionType == 0 && (diskData->PartitionNumber > 0)) {
2373 
2374                 status = STATUS_INVALID_DEVICE_REQUEST;
2375                 break;
2376             }
2377 
2378             outputBuffer =
2379                     (PPARTITION_INFORMATION_EX)Irp->AssociatedIrp.SystemBuffer;
2380 
2381             //
2382             // FIXME: hack of the year, assume that partition is MBR
2383             // Thing that can obviously be wrong...
2384             //
2385 
2386             outputBuffer->PartitionStyle = PARTITION_STYLE_MBR;
2387             outputBuffer->Mbr.PartitionType = diskData->PartitionType;
2388             outputBuffer->StartingOffset = deviceExtension->StartingOffset;
2389             outputBuffer->PartitionLength.QuadPart = deviceExtension->PartitionLength.QuadPart;
2390             outputBuffer->Mbr.HiddenSectors = diskData->HiddenSectors;
2391             outputBuffer->PartitionNumber = diskData->PartitionNumber;
2392             outputBuffer->Mbr.BootIndicator = diskData->BootIndicator;
2393             outputBuffer->RewritePartition = FALSE;
2394             outputBuffer->Mbr.RecognizedPartition =
2395                 IsRecognizedPartition(diskData->PartitionType);
2396 
2397             status = STATUS_SUCCESS;
2398             Irp->IoStatus.Information = sizeof(PARTITION_INFORMATION_EX);
2399         }
2400 
2401         break;
2402 
2403     case IOCTL_DISK_SET_PARTITION_INFO:
2404 
2405         if (diskData->PartitionNumber == 0) {
2406 
2407             status = STATUS_UNSUCCESSFUL;
2408 
2409         } else {
2410 
2411             PSET_PARTITION_INFORMATION inputBuffer =
2412                 (PSET_PARTITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer;
2413 
2414             //
2415             // Validate buffer length.
2416             //
2417 
2418             if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
2419                 sizeof(SET_PARTITION_INFORMATION)) {
2420 
2421                 status = STATUS_INFO_LENGTH_MISMATCH;
2422                 break;
2423             }
2424 
2425             //
2426             // The HAL routines IoGet- and IoSetPartitionInformation were
2427             // developed before support of dynamic partitioning and therefore
2428             // don't distinguish between partition ordinal (that is the order
2429             // of a partition on a disk) and the partition number. (The
2430             // partition number is assigned to a partition to identify it to
2431             // the system.) Use partition ordinals for these legacy calls.
2432             //
2433 
2434             status = IoSetPartitionInformation(
2435                           deviceExtension->PhysicalDevice,
2436                           deviceExtension->DiskGeometry->Geometry.BytesPerSector,
2437                           diskData->PartitionOrdinal,
2438                           inputBuffer->PartitionType);
2439 
2440             if (NT_SUCCESS(status)) {
2441 
2442                 diskData->PartitionType = inputBuffer->PartitionType;
2443             }
2444         }
2445 
2446         break;
2447 
2448     case IOCTL_DISK_GET_DRIVE_LAYOUT:
2449 
2450         //
2451         // Return the partition layout for the physical drive.  Note that
2452         // the layout is returned for the actual physical drive, regardless
2453         // of which partition was specified for the request.
2454         //
2455 
2456         if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
2457             sizeof(DRIVE_LAYOUT_INFORMATION)) {
2458             status = STATUS_INFO_LENGTH_MISMATCH;
2459 
2460         } else {
2461 
2462             PDRIVE_LAYOUT_INFORMATION partitionList;
2463             PDEVICE_EXTENSION         physicalExtension = deviceExtension;
2464             PPARTITION_INFORMATION    partitionEntry;
2465             PDISK_DATA                diskData;
2466             ULONG                     tempSize;
2467             ULONG                     i;
2468 
2469             //
2470             // Read partition information.
2471             //
2472 
2473             status = IoReadPartitionTable(deviceExtension->PhysicalDevice,
2474                               deviceExtension->DiskGeometry->Geometry.BytesPerSector,
2475                               FALSE,
2476                               &partitionList);
2477 
2478             if (!NT_SUCCESS(status)) {
2479                 break;
2480             }
2481 
2482             //
2483             // The disk layout has been returned in the partitionList
2484             // buffer.  Determine its size and, if the data will fit
2485             // into the intermediary buffer, return it.
2486             //
2487 
2488             tempSize = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION,PartitionEntry[0]);
2489             tempSize += partitionList->PartitionCount *
2490                         sizeof(PARTITION_INFORMATION);
2491 
2492             if (tempSize >
2493                irpStack->Parameters.DeviceIoControl.OutputBufferLength) {
2494 
2495                 status = STATUS_BUFFER_TOO_SMALL;
2496                 ExFreePool(partitionList);
2497                 break;
2498             }
2499 
2500             //
2501             // Walk partition list to associate partition numbers with
2502             // partition entries.
2503             //
2504 
2505             for (i = 0; i < partitionList->PartitionCount; i++) {
2506 
2507                 //
2508                 // Walk partition chain anchored at physical disk extension.
2509                 //
2510 
2511                 deviceExtension = physicalExtension;
2512                 diskData = (PDISK_DATA)(deviceExtension + 1);
2513 
2514                 do {
2515 
2516                     deviceExtension = diskData->NextPartition;
2517 
2518                     //
2519                     // Check if this is the last partition in the chain.
2520                     //
2521 
2522                     if (!deviceExtension) {
2523                        break;
2524                     }
2525 
2526                     //
2527                     // Get the partition device extension from disk data.
2528                     //
2529 
2530                     diskData = (PDISK_DATA)(deviceExtension + 1);
2531 
2532                     //
2533                     // Check if this partition is not currently being used.
2534                     //
2535 
2536                     if (!deviceExtension->PartitionLength.QuadPart) {
2537                        continue;
2538                     }
2539 
2540                     partitionEntry = &partitionList->PartitionEntry[i];
2541 
2542                     //
2543                     // Check if empty, or describes extended partition or hasn't changed.
2544                     //
2545 
2546                     if (partitionEntry->PartitionType == PARTITION_ENTRY_UNUSED ||
2547                         IsContainerPartition(partitionEntry->PartitionType)) {
2548                         continue;
2549                     }
2550 
2551                     //
2552                     // Check if new partition starts where this partition starts.
2553                     //
2554 
2555                     if (partitionEntry->StartingOffset.QuadPart !=
2556                               deviceExtension->StartingOffset.QuadPart) {
2557                         continue;
2558                     }
2559 
2560                     //
2561                     // Check if partition length is the same.
2562                     //
2563 
2564                     if (partitionEntry->PartitionLength.QuadPart ==
2565                               deviceExtension->PartitionLength.QuadPart) {
2566 
2567                         //
2568                         // Partitions match. Update partition number.
2569                         //
2570 
2571                         partitionEntry->PartitionNumber =
2572                             diskData->PartitionNumber;
2573                         break;
2574                     }
2575 
2576                 } while (TRUE);
2577             }
2578 
2579             //
2580             // Copy partition information to system buffer.
2581             //
2582 
2583             RtlMoveMemory(Irp->AssociatedIrp.SystemBuffer,
2584                           partitionList,
2585                           tempSize);
2586             status = STATUS_SUCCESS;
2587             Irp->IoStatus.Information = tempSize;
2588 
2589             //
2590             // Finally, free the buffer allocated by reading the
2591             // partition table.
2592             //
2593 
2594             ExFreePool(partitionList);
2595         }
2596 
2597         break;
2598 
2599     case IOCTL_DISK_SET_DRIVE_LAYOUT:
2600 
2601         {
2602 
2603         //
2604         // Update the disk with new partition information.
2605         //
2606 
2607         PDRIVE_LAYOUT_INFORMATION partitionList = Irp->AssociatedIrp.SystemBuffer;
2608 
2609         //
2610         // Validate buffer length.
2611         //
2612 
2613         if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
2614             sizeof(DRIVE_LAYOUT_INFORMATION)) {
2615 
2616             status = STATUS_INFO_LENGTH_MISMATCH;
2617             break;
2618         }
2619 
2620         length = sizeof(DRIVE_LAYOUT_INFORMATION) +
2621             (partitionList->PartitionCount - 1) * sizeof(PARTITION_INFORMATION);
2622 
2623 
2624         if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
2625             length) {
2626 
2627             status = STATUS_BUFFER_TOO_SMALL;
2628             break;
2629         }
2630 
2631         //
2632         // Verify that device object is for physical disk.
2633         //
2634 
2635         if (deviceExtension->PhysicalDevice->DeviceExtension != deviceExtension) {
2636             status = STATUS_INVALID_PARAMETER;
2637             break;
2638         }
2639 
2640         //
2641         // Walk through partition table comparing partitions to
2642         // existing partitions to create, delete and change
2643         // device objects as necessary.
2644         //
2645 
2646         UpdateDeviceObjects(DeviceObject,
2647                             Irp);
2648 
2649         //
2650         // Write changes to disk.
2651         //
2652 
2653         status = IoWritePartitionTable(
2654                            deviceExtension->DeviceObject,
2655                            deviceExtension->DiskGeometry->Geometry.BytesPerSector,
2656                            deviceExtension->DiskGeometry->Geometry.SectorsPerTrack,
2657                            deviceExtension->DiskGeometry->Geometry.TracksPerCylinder,
2658                            partitionList);
2659         }
2660 
2661         //
2662         // Update IRP with bytes returned.
2663         //
2664 
2665         if (NT_SUCCESS(status)) {
2666             Irp->IoStatus.Information = length;
2667         }
2668 
2669         break;
2670 
2671     case IOCTL_DISK_REASSIGN_BLOCKS:
2672 
2673         //
2674         // Map defective blocks to new location on disk.
2675         //
2676 
2677         {
2678 
2679         PREASSIGN_BLOCKS badBlocks = Irp->AssociatedIrp.SystemBuffer;
2680         ULONG bufferSize;
2681         ULONG blockNumber;
2682         ULONG blockCount;
2683 
2684         //
2685         // Validate buffer length.
2686         //
2687 
2688         if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
2689             sizeof(REASSIGN_BLOCKS)) {
2690 
2691             status = STATUS_INFO_LENGTH_MISMATCH;
2692             break;
2693         }
2694 
2695         bufferSize = sizeof(REASSIGN_BLOCKS) +
2696             (badBlocks->Count - 1) * sizeof(ULONG);
2697 
2698         if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
2699             bufferSize) {
2700 
2701             status = STATUS_INFO_LENGTH_MISMATCH;
2702             break;
2703         }
2704 
2705         //
2706         // Build the data buffer to be transferred in the input buffer.
2707         // The format of the data to the device is:
2708         //
2709         //      2 bytes Reserved
2710         //      2 bytes Length
2711         //      x * 4 btyes Block Address
2712         //
2713         // All values are big endian.
2714         //
2715 
2716         badBlocks->Reserved = 0;
2717         blockCount = badBlocks->Count;
2718 
2719         //
2720         // Convert # of entries to # of bytes.
2721         //
2722 
2723         blockCount *= 4;
2724         badBlocks->Count = (USHORT) ((blockCount >> 8) & 0XFF);
2725         badBlocks->Count |= (USHORT) ((blockCount << 8) & 0XFF00);
2726 
2727         //
2728         // Convert back to number of entries.
2729         //
2730 
2731         blockCount /= 4;
2732 
2733         for (; blockCount > 0; blockCount--) {
2734 
2735             blockNumber = badBlocks->BlockNumber[blockCount-1];
2736 
2737             REVERSE_BYTES((PFOUR_BYTE) &badBlocks->BlockNumber[blockCount-1],
2738                           (PFOUR_BYTE) &blockNumber);
2739         }
2740 
2741         srb->CdbLength = 6;
2742 
2743         cdb->CDB6GENERIC.OperationCode = SCSIOP_REASSIGN_BLOCKS;
2744 
2745         //
2746         // Set timeout value.
2747         //
2748 
2749         srb->TimeOutValue = deviceExtension->TimeOutValue;
2750 
2751         status = ScsiClassSendSrbSynchronous(DeviceObject,
2752                                              srb,
2753                                              badBlocks,
2754                                              bufferSize,
2755                                              TRUE);
2756 
2757         Irp->IoStatus.Status = status;
2758         Irp->IoStatus.Information = 0;
2759         ExFreePool(srb);
2760         IoCompleteRequest(Irp, IO_DISK_INCREMENT);
2761         }
2762 
2763         return(status);
2764 
2765     case IOCTL_DISK_IS_WRITABLE:
2766 
2767         //
2768         // Determine if the device is writable.
2769         //
2770 
2771         modeData = ExAllocatePool(NonPagedPoolCacheAligned, MODE_DATA_SIZE);
2772 
2773         if (modeData == NULL) {
2774             status = STATUS_INSUFFICIENT_RESOURCES;
2775             break;
2776         }
2777 
2778         RtlZeroMemory(modeData, MODE_DATA_SIZE);
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 
2787             //
2788             // Retry the request in case of a check condition.
2789             //
2790 
2791             length = ScsiClassModeSense(DeviceObject,
2792                                         (PCHAR) modeData,
2793                                         MODE_DATA_SIZE,
2794                                         MODE_SENSE_RETURN_ALL);
2795 
2796             if (length < sizeof(MODE_PARAMETER_HEADER)) {
2797                 status = STATUS_IO_DEVICE_ERROR;
2798                 ExFreePool(modeData);
2799                 break;
2800             }
2801         }
2802 
2803         if (modeData->DeviceSpecificParameter & MODE_DSP_WRITE_PROTECT) {
2804             status = STATUS_MEDIA_WRITE_PROTECTED;
2805         } else {
2806             status = STATUS_SUCCESS;
2807         }
2808 
2809         ExFreePool(modeData);
2810         break;
2811 
2812     case IOCTL_DISK_INTERNAL_SET_VERIFY:
2813 
2814         //
2815         // If the caller is kernel mode, set the verify bit.
2816         //
2817 
2818         if (Irp->RequestorMode == KernelMode) {
2819             DeviceObject->Flags |= DO_VERIFY_VOLUME;
2820         }
2821         status = STATUS_SUCCESS;
2822         break;
2823 
2824     case IOCTL_DISK_INTERNAL_CLEAR_VERIFY:
2825 
2826         //
2827         // If the caller is kernel mode, clear the verify bit.
2828         //
2829 
2830         if (Irp->RequestorMode == KernelMode) {
2831             DeviceObject->Flags &= ~DO_VERIFY_VOLUME;
2832         }
2833         status = STATUS_SUCCESS;
2834         break;
2835 
2836     case IOCTL_DISK_FIND_NEW_DEVICES:
2837 
2838         //
2839         // Search for devices that have been powered on since the last
2840         // device search or system initialization.
2841         //
2842 
2843         DebugPrint((3,"CdRomDeviceControl: Find devices\n"));
2844         status = DriverEntry(DeviceObject->DriverObject,
2845                              NULL);
2846 
2847         Irp->IoStatus.Status = status;
2848         ExFreePool(srb);
2849         IoCompleteRequest(Irp, IO_NO_INCREMENT);
2850         return status;
2851 
2852     case IOCTL_DISK_MEDIA_REMOVAL:
2853 
2854         //
2855         // If the disk is not removable then don't allow this command.
2856         //
2857 
2858         if (!(DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA)) {
2859             status = STATUS_INVALID_DEVICE_REQUEST;
2860             break;
2861         }
2862 
2863         //
2864         // Fall through and let the class driver process the request.
2865         //
2866 
2867     case IOCTL_DISK_GET_LENGTH_INFO:
2868 
2869         //
2870         // Validate buffer length.
2871         //
2872 
2873         if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
2874             sizeof(GET_LENGTH_INFORMATION)) {
2875             status = STATUS_BUFFER_TOO_SMALL;
2876 
2877         } else {
2878 
2879             PGET_LENGTH_INFORMATION lengthInformation = Irp->AssociatedIrp.SystemBuffer;
2880 
2881             //
2882             // Update the geometry in case it has changed.
2883             //
2884 
2885             status = UpdateRemovableGeometry (DeviceObject, Irp);
2886 
2887             if (!NT_SUCCESS(status)) {
2888 
2889                 //
2890                 // Note the drive is not ready.
2891                 //
2892 
2893                 diskData->DriveNotReady = TRUE;
2894                 break;
2895             }
2896 
2897             //
2898             // Note the drive is now ready.
2899             //
2900 
2901             diskData->DriveNotReady = FALSE;
2902 
2903             //
2904             // Output data, and return
2905             //
2906 
2907             lengthInformation->Length.QuadPart = deviceExtension->PartitionLength.QuadPart;
2908             status = STATUS_SUCCESS;
2909             Irp->IoStatus.Information = sizeof(GET_LENGTH_INFORMATION);
2910         }
2911 
2912         break;
2913 
2914     default:
2915 
2916         //
2917         // Free the Srb, since it is not needed.
2918         //
2919 
2920         ExFreePool(srb);
2921 
2922         //
2923         // Pass the request to the common device control routine.
2924         //
2925 
2926         return(ScsiClassDeviceControl(DeviceObject, Irp));
2927 
2928         break;
2929 
2930     } // end switch( ...
2931 
2932     Irp->IoStatus.Status = status;
2933 
2934     if (!NT_SUCCESS(status) && IoIsErrorUserInduced(status)) {
2935 
2936         IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);
2937     }
2938 
2939     IoCompleteRequest(Irp, IO_NO_INCREMENT);
2940     ExFreePool(srb);
2941     return(status);
2942 
2943 } // end ScsiDiskDeviceControl()
2944 
2945 NTSTATUS
2946 NTAPI
2947 ScsiDiskShutdownFlush (
2948     IN PDEVICE_OBJECT DeviceObject,
2949     IN PIRP Irp
2950     )
2951 
2952 /*++
2953 
2954 Routine Description:
2955 
2956     This routine is called for a shutdown and flush IRPs.  These are sent by the
2957     system before it actually shuts down or when the file system does a flush.
2958     A synchronize cache command is sent to the device if it is write caching.
2959     If the device is removable an unlock command will be sent. This routine
2960     will sent a shutdown or flush Srb to the port driver.
2961 
2962 Arguments:
2963 
2964     DriverObject - Pointer to device object to being shutdown by system.
2965 
2966     Irp - IRP involved.
2967 
2968 Return Value:
2969 
2970     NT Status
2971 
2972 --*/
2973 
2974 {
2975     PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
2976     PIO_STACK_LOCATION irpStack;
2977     PSCSI_REQUEST_BLOCK srb;
2978     NTSTATUS status;
2979     PCDB cdb;
2980 
2981     //
2982     // Allocate SCSI request block.
2983     //
2984 
2985     srb = ExAllocatePool(NonPagedPool, sizeof(SCSI_REQUEST_BLOCK));
2986 
2987     if (srb == NULL) {
2988 
2989         //
2990         // Set the status and complete the request.
2991         //
2992 
2993         Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
2994         IoCompleteRequest(Irp, IO_NO_INCREMENT);
2995         return(STATUS_INSUFFICIENT_RESOURCES);
2996     }
2997 
2998     RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE);
2999 
3000     //
3001     // Write length to SRB.
3002     //
3003 
3004     srb->Length = SCSI_REQUEST_BLOCK_SIZE;
3005 
3006     //
3007     // Set SCSI bus address.
3008     //
3009 
3010     srb->PathId = deviceExtension->PathId;
3011     srb->TargetId = deviceExtension->TargetId;
3012     srb->Lun = deviceExtension->Lun;
3013 
3014     //
3015     // Set timeout value and mark the request as not being a tagged request.
3016     //
3017 
3018     srb->TimeOutValue = deviceExtension->TimeOutValue * 4;
3019     srb->QueueTag = SP_UNTAGGED;
3020     srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
3021     srb->SrbFlags = deviceExtension->SrbFlags;
3022 
3023     //
3024     // If the write cache is enabled then send a synchronize cache request.
3025     //
3026 
3027     if (deviceExtension->DeviceFlags & DEV_WRITE_CACHE) {
3028 
3029         srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
3030         srb->CdbLength = 10;
3031 
3032         srb->Cdb[0] = SCSIOP_SYNCHRONIZE_CACHE;
3033 
3034         status = ScsiClassSendSrbSynchronous(DeviceObject,
3035                                              srb,
3036                                              NULL,
3037                                              0,
3038                                              TRUE);
3039 
3040         DebugPrint((1, "ScsiDiskShutdownFlush: Synchronize cache sent. Status = %lx\n", status ));
3041     }
3042 
3043     //
3044     // Unlock the device if it is removable and this is a shutdown.
3045     //
3046 
3047     irpStack = IoGetCurrentIrpStackLocation(Irp);
3048 
3049     if (DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA &&
3050         irpStack->MajorFunction == IRP_MJ_SHUTDOWN) {
3051 
3052         srb->CdbLength = 6;
3053         cdb = (PVOID) srb->Cdb;
3054         cdb->MEDIA_REMOVAL.OperationCode = SCSIOP_MEDIUM_REMOVAL;
3055         cdb->MEDIA_REMOVAL.Prevent = FALSE;
3056 
3057         //
3058         // Set timeout value.
3059         //
3060 
3061         srb->TimeOutValue = deviceExtension->TimeOutValue;
3062         status = ScsiClassSendSrbSynchronous(DeviceObject,
3063                                              srb,
3064                                              NULL,
3065                                              0,
3066                                              TRUE);
3067 
3068         DebugPrint((1, "ScsiDiskShutdownFlush: Unlock device request sent. Status = %lx\n", status ));
3069     }
3070 
3071     srb->CdbLength = 0;
3072 
3073     //
3074     // Save a few parameters in the current stack location.
3075     //
3076 
3077     srb->Function = irpStack->MajorFunction == IRP_MJ_SHUTDOWN ?
3078         SRB_FUNCTION_SHUTDOWN : SRB_FUNCTION_FLUSH;
3079 
3080     //
3081     // Set the retry count to zero.
3082     //
3083 
3084     irpStack->Parameters.Others.Argument4 = (PVOID) 0;
3085 
3086     //
3087     // Set up IoCompletion routine address.
3088     //
3089 
3090     IoSetCompletionRoutine(Irp, ScsiClassIoComplete, srb, TRUE, TRUE, TRUE);
3091 
3092     //
3093     // Get next stack location and
3094     // set major function code.
3095     //
3096 
3097     irpStack = IoGetNextIrpStackLocation(Irp);
3098 
3099     irpStack->MajorFunction = IRP_MJ_SCSI;
3100 
3101     //
3102     // Set up SRB for execute scsi request.
3103     // Save SRB address in next stack for port driver.
3104     //
3105 
3106     irpStack->Parameters.Scsi.Srb = srb;
3107 
3108     //
3109     // Set up Irp Address.
3110     //
3111 
3112     srb->OriginalRequest = Irp;
3113 
3114     //
3115     // Call the port driver to process the request.
3116     //
3117 
3118     return(IoCallDriver(deviceExtension->PortDeviceObject, Irp));
3119 
3120 } // end ScsiDiskShutdown()
3121 
3122 
3123 BOOLEAN
3124 NTAPI
3125 IsFloppyDevice(
3126     PDEVICE_OBJECT DeviceObject
3127     )
3128 /*++
3129 
3130 Routine Description:
3131 
3132     The routine performs the necessary functions to determine if a device is
3133     really a floppy rather than a harddisk.  This is done by a mode sense
3134     command.  First, a check is made to see if the media type is set.  Second
3135     a check is made for the flexible parameters mode page.  Also a check is
3136     made to see if the write cache is enabled.
3137 
3138 Arguments:
3139 
3140     DeviceObject - Supplies the device object to be tested.
3141 
3142 Return Value:
3143 
3144     Return TRUE if the indicated device is a floppy.
3145 
3146 --*/
3147 {
3148     PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
3149     PVOID modeData;
3150     PUCHAR pageData;
3151     ULONG length;
3152 
3153     PAGED_CODE();
3154 
3155     modeData = ExAllocatePool(NonPagedPoolCacheAligned, MODE_DATA_SIZE);
3156 
3157     if (modeData == NULL) {
3158         return(FALSE);
3159     }
3160 
3161     RtlZeroMemory(modeData, MODE_DATA_SIZE);
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         //
3171         // Retry the request in case of a check condition.
3172         //
3173 
3174         length = ScsiClassModeSense(DeviceObject,
3175                                 modeData,
3176                                 MODE_DATA_SIZE,
3177                                 MODE_SENSE_RETURN_ALL);
3178 
3179         if (length < sizeof(MODE_PARAMETER_HEADER)) {
3180 
3181             ExFreePool(modeData);
3182             return(FALSE);
3183 
3184         }
3185     }
3186 
3187     //
3188     // If the length is greater than length indicated by the mode data reset
3189     // the data to the mode data.
3190     //
3191 
3192     if (length > (ULONG) ((PMODE_PARAMETER_HEADER) modeData)->ModeDataLength + 1) {
3193         length = ((PMODE_PARAMETER_HEADER) modeData)->ModeDataLength + 1;
3194     }
3195 
3196     //
3197     // Look for the flexible disk mode page.
3198     //
3199 
3200     pageData = ScsiClassFindModePage( modeData, length, MODE_PAGE_FLEXIBILE, TRUE);
3201 
3202     if (pageData != NULL) {
3203 
3204         DebugPrint((1, "Scsidisk: Flexible disk page found, This is a floppy.\n"));
3205         ExFreePool(modeData);
3206         return(TRUE);
3207     }
3208 
3209     //
3210     // Check to see if the write cache is enabled.
3211     //
3212 
3213     pageData = ScsiClassFindModePage( modeData, length, MODE_PAGE_CACHING, TRUE);
3214 
3215     //
3216     // Assume that write cache is disabled or not supported.
3217     //
3218 
3219     deviceExtension->DeviceFlags &= ~DEV_WRITE_CACHE;
3220 
3221     //
3222     // Check if valid caching page exists.
3223     //
3224 
3225     if (pageData != NULL) {
3226 
3227         //
3228         // Check if write cache is disabled.
3229         //
3230 
3231         if (((PMODE_CACHING_PAGE)pageData)->WriteCacheEnable) {
3232 
3233             DebugPrint((1,
3234                        "SCSIDISK: Disk write cache enabled\n"));
3235 
3236             //
3237             // Check if forced unit access (FUA) is supported.
3238             //
3239 
3240             if (((PMODE_PARAMETER_HEADER)modeData)->DeviceSpecificParameter & MODE_DSP_FUA_SUPPORTED) {
3241 
3242                 deviceExtension->DeviceFlags |= DEV_WRITE_CACHE;
3243 
3244             } else {
3245 
3246                 DebugPrint((1,
3247                            "SCSIDISK: Disk does not support FUA or DPO\n"));
3248 
3249                 //
3250                 // TODO: Log this.
3251                 //
3252 
3253             }
3254         }
3255     }
3256 
3257     ExFreePool(modeData);
3258     return(FALSE);
3259 
3260 } // end IsFloppyDevice()
3261 
3262 
3263 BOOLEAN
3264 NTAPI
3265 ScsiDiskModeSelect(
3266     IN PDEVICE_OBJECT DeviceObject,
3267     IN PCHAR ModeSelectBuffer,
3268     IN ULONG Length,
3269     IN BOOLEAN SavePage
3270     )
3271 
3272 /*++
3273 
3274 Routine Description:
3275 
3276     This routine sends a mode select command.
3277 
3278 Arguments:
3279 
3280     DeviceObject - Supplies the device object associated with this request.
3281 
3282     ModeSelectBuffer - Supplies a buffer containing the page data.
3283 
3284     Length - Supplies the length in bytes of the mode select buffer.
3285 
3286     SavePage - Indicates that parameters should be written to disk.
3287 
3288 Return Value:
3289 
3290     Length of the transferred data is returned.
3291 
3292 --*/
3293 {
3294     PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
3295     PCDB cdb;
3296     SCSI_REQUEST_BLOCK srb;
3297     ULONG retries = 1;
3298     ULONG length2;
3299     NTSTATUS status;
3300     ULONG_PTR buffer;
3301     PMODE_PARAMETER_BLOCK blockDescriptor;
3302 
3303     PAGED_CODE();
3304 
3305     length2 = Length + sizeof(MODE_PARAMETER_HEADER) + sizeof(MODE_PARAMETER_BLOCK);
3306 
3307     //
3308     // Allocate buffer for mode select header, block descriptor, and mode page.
3309     //
3310 
3311     buffer = (ULONG_PTR)ExAllocatePool(NonPagedPoolCacheAligned,length2);
3312 
3313     RtlZeroMemory((PVOID)buffer, length2);
3314 
3315     //
3316     // Set length in header to size of mode page.
3317     //
3318 
3319     ((PMODE_PARAMETER_HEADER)buffer)->BlockDescriptorLength = sizeof(MODE_PARAMETER_BLOCK);
3320 
3321     blockDescriptor = (PMODE_PARAMETER_BLOCK)(buffer + 1);
3322 
3323     //
3324     // Set size
3325     //
3326 
3327     blockDescriptor->BlockLength[1]=0x02;
3328 
3329     //
3330     // Copy mode page to buffer.
3331     //
3332 
3333     RtlCopyMemory((PVOID)(buffer + 3), ModeSelectBuffer, Length);
3334 
3335     //
3336     // Zero SRB.
3337     //
3338 
3339     RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
3340 
3341     //
3342     // Build the MODE SELECT CDB.
3343     //
3344 
3345     srb.CdbLength = 6;
3346     cdb = (PCDB)srb.Cdb;
3347 
3348     //
3349     // Set timeout value from device extension.
3350     //
3351 
3352     srb.TimeOutValue = deviceExtension->TimeOutValue * 2;
3353 
3354     cdb->MODE_SELECT.OperationCode = SCSIOP_MODE_SELECT;
3355     cdb->MODE_SELECT.SPBit = SavePage;
3356     cdb->MODE_SELECT.PFBit = 1;
3357     cdb->MODE_SELECT.ParameterListLength = (UCHAR)(length2);
3358 
3359 Retry:
3360 
3361     status = ScsiClassSendSrbSynchronous(DeviceObject,
3362                                          &srb,
3363                                          (PVOID)buffer,
3364                                          length2,
3365                                          TRUE);
3366 
3367 
3368     if (status == STATUS_VERIFY_REQUIRED) {
3369 
3370         //
3371         // Routine ScsiClassSendSrbSynchronous does not retry requests returned with
3372         // this status.
3373         //
3374 
3375         if (retries--) {
3376 
3377             //
3378             // Retry request.
3379             //
3380 
3381             goto Retry;
3382         }
3383 
3384     } else if (SRB_STATUS(srb.SrbStatus) == SRB_STATUS_DATA_OVERRUN) {
3385         status = STATUS_SUCCESS;
3386     }
3387 
3388     ExFreePool((PVOID)buffer);
3389 
3390     if (NT_SUCCESS(status)) {
3391         return(TRUE);
3392     } else {
3393         return(FALSE);
3394     }
3395 
3396 } // end SciDiskModeSelect()
3397 
3398 
3399 VOID
3400 NTAPI
3401 DisableWriteCache(
3402     IN PDEVICE_OBJECT DeviceObject,
3403     IN PSCSI_INQUIRY_DATA LunInfo
3404     )
3405 
3406 {
3407     PDEVICE_EXTENSION          deviceExtension = DeviceObject->DeviceExtension;
3408     PINQUIRYDATA               InquiryData     = (PINQUIRYDATA)LunInfo->InquiryData;
3409     BAD_CONTROLLER_INFORMATION const *controller;
3410     ULONG                      j,length;
3411     PVOID                      modeData;
3412     PUCHAR                     pageData;
3413 
3414     for (j = 0; j <  NUMBER_OF_BAD_CONTROLLERS; j++) {
3415 
3416         controller = &ScsiDiskBadControllers[j];
3417 
3418         if (!controller->DisableWriteCache || strncmp(controller->InquiryString, (PCCHAR)InquiryData->VendorId, strlen(controller->InquiryString))) {
3419             continue;
3420         }
3421 
3422         DebugPrint((1, "ScsiDisk.DisableWriteCache, Found bad controller! %s\n", controller->InquiryString));
3423 
3424         modeData = ExAllocatePool(NonPagedPoolCacheAligned, MODE_DATA_SIZE);
3425 
3426         if (modeData == NULL) {
3427 
3428             DebugPrint((1,
3429                         "ScsiDisk.DisableWriteCache: Check for write-cache enable failed\n"));
3430             return;
3431         }
3432 
3433         RtlZeroMemory(modeData, MODE_DATA_SIZE);
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             // Retry the request in case of a check condition.
3444             //
3445 
3446             length = ScsiClassModeSense(DeviceObject,
3447                                     modeData,
3448                                     MODE_DATA_SIZE,
3449                                     MODE_SENSE_RETURN_ALL);
3450 
3451             if (length < sizeof(MODE_PARAMETER_HEADER)) {
3452 
3453 
3454                 DebugPrint((1,
3455                             "ScsiDisk.DisableWriteCache: Mode Sense failed\n"));
3456 
3457                 ExFreePool(modeData);
3458                 return;
3459 
3460             }
3461         }
3462 
3463         //
3464         // If the length is greater than length indicated by the mode data reset
3465         // the data to the mode data.
3466         //
3467 
3468         if (length > (ULONG) ((PMODE_PARAMETER_HEADER) modeData)->ModeDataLength + 1) {
3469             length = ((PMODE_PARAMETER_HEADER) modeData)->ModeDataLength + 1;
3470         }
3471 
3472         //
3473         // Check to see if the write cache is enabled.
3474         //
3475 
3476         pageData = ScsiClassFindModePage( modeData, length, MODE_PAGE_CACHING, TRUE);
3477 
3478         //
3479         // Assume that write cache is disabled or not supported.
3480         //
3481 
3482         deviceExtension->DeviceFlags &= ~DEV_WRITE_CACHE;
3483 
3484         //
3485         // Check if valid caching page exists.
3486         //
3487 
3488         if (pageData != NULL) {
3489 
3490             BOOLEAN savePage = FALSE;
3491 
3492             savePage = (BOOLEAN)(((PMODE_CACHING_PAGE)pageData)->PageSavable);
3493 
3494             //
3495             // Check if write cache is disabled.
3496             //
3497 
3498             if (((PMODE_CACHING_PAGE)pageData)->WriteCacheEnable) {
3499 
3500                 PIO_ERROR_LOG_PACKET errorLogEntry;
3501                 LONG                 errorCode;
3502 
3503 
3504                 //
3505                 // Disable write cache and ensure necessary fields are zeroed.
3506                 //
3507 
3508                 ((PMODE_CACHING_PAGE)pageData)->WriteCacheEnable = FALSE;
3509                 ((PMODE_CACHING_PAGE)pageData)->Reserved = 0;
3510                 ((PMODE_CACHING_PAGE)pageData)->PageSavable = 0;
3511                 ((PMODE_CACHING_PAGE)pageData)->Reserved2 = 0;
3512 
3513                 //
3514                 // Extract length from caching page.
3515                 //
3516 
3517                 length = ((PMODE_CACHING_PAGE)pageData)->PageLength;
3518 
3519                 //
3520                 // Compensate for page code and page length.
3521                 //
3522 
3523                 length += 2;
3524 
3525                 //
3526                 // Issue mode select to set the parameter.
3527                 //
3528 
3529                 if (ScsiDiskModeSelect(DeviceObject,
3530                                        (PCHAR)pageData,
3531                                        length,
3532                                        savePage)) {
3533 
3534                     DebugPrint((1,
3535                                "SCSIDISK: Disk write cache disabled\n"));
3536 
3537                     deviceExtension->DeviceFlags &= ~DEV_WRITE_CACHE;
3538                     errorCode = IO_WRITE_CACHE_DISABLED;
3539 
3540                 } else {
3541                     if (ScsiDiskModeSelect(DeviceObject,
3542                                            (PCHAR)pageData,
3543                                            length,
3544                                            savePage)) {
3545 
3546                         DebugPrint((1,
3547                                    "SCSIDISK: Disk write cache disabled\n"));
3548 
3549 
3550                         deviceExtension->DeviceFlags &= ~DEV_WRITE_CACHE;
3551                         errorCode = IO_WRITE_CACHE_DISABLED;
3552 
3553                     } else {
3554 
3555                             DebugPrint((1,
3556                                        "SCSIDISK: Mode select to disable write cache failed\n"));
3557 
3558                             deviceExtension->DeviceFlags |= DEV_WRITE_CACHE;
3559                             errorCode = IO_WRITE_CACHE_ENABLED;
3560                     }
3561                 }
3562 
3563                 //
3564                 // Log the appropriate informational or error entry.
3565                 //
3566 
3567                 errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry(
3568                                                          DeviceObject,
3569                                                          sizeof(IO_ERROR_LOG_PACKET) + 3
3570                                                              * sizeof(ULONG));
3571 
3572                 if (errorLogEntry != NULL) {
3573 
3574                     errorLogEntry->FinalStatus     = STATUS_SUCCESS;
3575                     errorLogEntry->ErrorCode       = errorCode;
3576                     errorLogEntry->SequenceNumber  = 0;
3577                     errorLogEntry->MajorFunctionCode = IRP_MJ_SCSI;
3578                     errorLogEntry->IoControlCode   = 0;
3579                     errorLogEntry->RetryCount      = 0;
3580                     errorLogEntry->UniqueErrorValue = 0x1;
3581                     errorLogEntry->DumpDataSize    = 3 * sizeof(ULONG);
3582                     errorLogEntry->DumpData[0]     = LunInfo->PathId;
3583                     errorLogEntry->DumpData[1]     = LunInfo->TargetId;
3584                     errorLogEntry->DumpData[2]     = LunInfo->Lun;
3585 
3586                     //
3587                     // Write the error log packet.
3588                     //
3589 
3590                     IoWriteErrorLogEntry(errorLogEntry);
3591                 }
3592             }
3593         }
3594 
3595         //
3596         // Found device so exit the loop and return.
3597         //
3598 
3599         break;
3600     }
3601 
3602     return;
3603 }
3604 
3605 
3606 BOOLEAN
3607 NTAPI
3608 CalculateMbrCheckSum(
3609     IN PDEVICE_EXTENSION DeviceExtension,
3610     OUT PULONG Checksum
3611     )
3612 
3613 /*++
3614 
3615 Routine Description:
3616 
3617     Read MBR and calculate checksum.
3618 
3619 Arguments:
3620 
3621     DeviceExtension - Supplies a pointer to the device information for disk.
3622     Checksum - Memory location to return MBR checksum.
3623 
3624 Return Value:
3625 
3626     Returns TRUE if checksum is valid.
3627 
3628 --*/
3629 {
3630     LARGE_INTEGER   sectorZero;
3631     PIRP            irp;
3632     IO_STATUS_BLOCK ioStatus;
3633     KEVENT          event;
3634     NTSTATUS        status;
3635     ULONG           sectorSize;
3636     PULONG          mbr;
3637     ULONG           i;
3638 
3639     PAGED_CODE();
3640     sectorZero.QuadPart = (LONGLONG) 0;
3641 
3642     //
3643     // Create notification event object to be used to signal the inquiry
3644     // request completion.
3645     //
3646 
3647     KeInitializeEvent(&event, NotificationEvent, FALSE);
3648 
3649     //
3650     // Get sector size.
3651     //
3652 
3653     sectorSize = DeviceExtension->DiskGeometry->Geometry.BytesPerSector;
3654 
3655     //
3656     // Make sure sector size is at least 512 bytes.
3657     //
3658 
3659     if (sectorSize < 512) {
3660         sectorSize = 512;
3661     }
3662 
3663     //
3664     // Allocate buffer for sector read.
3665     //
3666 
3667     mbr = ExAllocatePool(NonPagedPoolCacheAligned, sectorSize);
3668 
3669     if (!mbr) {
3670         return FALSE;
3671     }
3672 
3673     //
3674     // Build IRP to read MBR.
3675     //
3676 
3677     irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ,
3678                                        DeviceExtension->DeviceObject,
3679                                        mbr,
3680                                        sectorSize,
3681                                        &sectorZero,
3682                                        &event,
3683                                        &ioStatus );
3684 
3685     if (!irp) {
3686         ExFreePool(mbr);
3687         return FALSE;
3688     }
3689 
3690     //
3691     // Pass request to port driver and wait for request to complete.
3692     //
3693 
3694     status = IoCallDriver(DeviceExtension->DeviceObject,
3695                           irp);
3696 
3697     if (status == STATUS_PENDING) {
3698         KeWaitForSingleObject(&event,
3699                               Suspended,
3700                               KernelMode,
3701                               FALSE,
3702                               NULL);
3703         status = ioStatus.Status;
3704     }
3705 
3706     if (!NT_SUCCESS(status)) {
3707         ExFreePool(mbr);
3708         return FALSE;
3709     }
3710 
3711     //
3712     // Calculate MBR checksum.
3713     //
3714 
3715     *Checksum = 0;
3716 
3717     for (i = 0; i < 128; i++) {
3718         *Checksum += mbr[i];
3719     }
3720 
3721     *Checksum = ~*Checksum + 1;
3722 
3723     ExFreePool(mbr);
3724     return TRUE;
3725 }
3726 
3727 
3728 BOOLEAN
3729 NTAPI
3730 EnumerateBusKey(
3731     IN PDEVICE_EXTENSION DeviceExtension,
3732     HANDLE BusKey,
3733     PULONG DiskNumber
3734     )
3735 
3736 /*++
3737 
3738 Routine Description:
3739 
3740     The routine queries the registry to determine if this disk is visible to
3741     the BIOS.  If the disk is visible to the BIOS, then the geometry information
3742     is updated.
3743 
3744 Arguments:
3745 
3746     DeviceExtension - Supplies a pointer to the device information for disk.
3747     Signature - Unique identifier recorded in MBR.
3748     BusKey - Handle of bus key.
3749     DiskNumber - Returns ordinal of disk as BIOS sees it.
3750 
3751 Return Value:
3752 
3753     TRUE is disk signature matched.
3754 
3755 --*/
3756 {
3757     PDISK_DATA        diskData = (PDISK_DATA)(DeviceExtension + 1);
3758     BOOLEAN           diskFound = FALSE;
3759     OBJECT_ATTRIBUTES objectAttributes;
3760     UNICODE_STRING    unicodeString;
3761     UNICODE_STRING    identifier;
3762     ULONG             busNumber;
3763     ULONG             adapterNumber;
3764     ULONG             diskNumber;
3765     HANDLE            adapterKey;
3766     HANDLE            spareKey;
3767     HANDLE            diskKey;
3768     HANDLE            targetKey;
3769     NTSTATUS          status;
3770     STRING            string;
3771     STRING            anotherString;
3772     ULONG             length;
3773     UCHAR             buffer[20];
3774     PKEY_VALUE_FULL_INFORMATION keyData;
3775 
3776     PAGED_CODE();
3777 
3778     for (busNumber = 0; ; busNumber++) {
3779 
3780         //
3781         // Open controller name key.
3782         //
3783 
3784         sprintf((PCHAR)buffer,
3785                 "%lu",
3786                 busNumber);
3787 
3788         RtlInitString(&string,
3789                       (PCSZ)buffer);
3790 
3791         status = RtlAnsiStringToUnicodeString(&unicodeString,
3792                                               &string,
3793                                               TRUE);
3794 
3795         if (!NT_SUCCESS(status)){
3796             break;
3797         }
3798 
3799         InitializeObjectAttributes(&objectAttributes,
3800                                    &unicodeString,
3801                                    OBJ_CASE_INSENSITIVE,
3802                                    BusKey,
3803                                    (PSECURITY_DESCRIPTOR)NULL);
3804 
3805         status = ZwOpenKey(&spareKey,
3806                            KEY_READ,
3807                            &objectAttributes);
3808 
3809         RtlFreeUnicodeString(&unicodeString);
3810 
3811         if (!NT_SUCCESS(status)) {
3812             break;
3813         }
3814 
3815         //
3816         // Open up controller ordinal key.
3817         //
3818 
3819         RtlInitUnicodeString(&unicodeString, L"DiskController");
3820         InitializeObjectAttributes(&objectAttributes,
3821                                    &unicodeString,
3822                                    OBJ_CASE_INSENSITIVE,
3823                                    spareKey,
3824                                    (PSECURITY_DESCRIPTOR)NULL);
3825 
3826         status = ZwOpenKey(&adapterKey,
3827                            KEY_READ,
3828                            &objectAttributes);
3829 
3830         //
3831         // This could fail even with additional adapters of this type
3832         // to search.
3833         //
3834 
3835         if (!NT_SUCCESS(status)) {
3836             continue;
3837         }
3838 
3839         for (adapterNumber = 0; ; adapterNumber++) {
3840 
3841             //
3842             // Open disk key.
3843             //
3844 
3845             sprintf((PCHAR)buffer,
3846                     "%lu\\DiskPeripheral",
3847                     adapterNumber);
3848 
3849             RtlInitString(&string,
3850                           (PCSZ)buffer);
3851 
3852             status = RtlAnsiStringToUnicodeString(&unicodeString,
3853                                                   &string,
3854                                                   TRUE);
3855 
3856             if (!NT_SUCCESS(status)){
3857                 break;
3858             }
3859 
3860             InitializeObjectAttributes(&objectAttributes,
3861                                        &unicodeString,
3862                                        OBJ_CASE_INSENSITIVE,
3863                                        adapterKey,
3864                                        (PSECURITY_DESCRIPTOR)NULL);
3865 
3866             status = ZwOpenKey(&diskKey,
3867                                KEY_READ,
3868                                &objectAttributes);
3869 
3870             RtlFreeUnicodeString(&unicodeString);
3871 
3872             if (!NT_SUCCESS(status)) {
3873                 break;
3874             }
3875 
3876             for (diskNumber = 0; ; diskNumber++) {
3877 
3878                 sprintf((PCHAR)buffer,
3879                         "%lu",
3880                         diskNumber);
3881 
3882                 RtlInitString(&string,
3883                               (PCSZ)buffer);
3884 
3885                 status = RtlAnsiStringToUnicodeString(&unicodeString,
3886                                                       &string,
3887                                                       TRUE);
3888 
3889                 if (!NT_SUCCESS(status)){
3890                     break;
3891                 }
3892 
3893                 InitializeObjectAttributes(&objectAttributes,
3894                                            &unicodeString,
3895                                            OBJ_CASE_INSENSITIVE,
3896                                            diskKey,
3897                                            (PSECURITY_DESCRIPTOR)NULL);
3898 
3899                 status = ZwOpenKey(&targetKey,
3900                                    KEY_READ,
3901                                    &objectAttributes);
3902 
3903                 RtlFreeUnicodeString(&unicodeString);
3904 
3905                 if (!NT_SUCCESS(status)) {
3906                     break;
3907                 }
3908 
3909                 //
3910                 // Allocate buffer for registry query.
3911                 //
3912 
3913                 keyData = ExAllocatePool(PagedPool, VALUE_BUFFER_SIZE);
3914 
3915                 if (keyData == NULL) {
3916                     ZwClose(targetKey);
3917                     continue;
3918                 }
3919 
3920                 //
3921                 // Get disk peripheral identifier.
3922                 //
3923 
3924                 RtlInitUnicodeString(&unicodeString, L"Identifier");
3925                 status = ZwQueryValueKey(targetKey,
3926                                          &unicodeString,
3927                                          KeyValueFullInformation,
3928                                          keyData,
3929                                          VALUE_BUFFER_SIZE,
3930                                          &length);
3931 
3932                 ZwClose(targetKey);
3933 
3934                 if (!NT_SUCCESS(status)) {
3935                     ExFreePool(keyData);
3936                     continue;
3937                 }
3938 
3939                 if (keyData->DataLength < 9*sizeof(WCHAR)) {
3940                     //
3941                     // the data is too short to use (we subtract 9 chars in normal path)
3942                     //
3943                     DebugPrint((1, "EnumerateBusKey: Saved data was invalid, "
3944                                 "not enough data in registry!\n"));
3945                     ExFreePool(keyData);
3946                     continue;
3947                 }
3948 
3949                 //
3950                 // Complete unicode string.
3951                 //
3952 
3953                 identifier.Buffer =
3954                     (PWSTR)((PUCHAR)keyData + keyData->DataOffset);
3955                 identifier.Length = (USHORT)keyData->DataLength;
3956                 identifier.MaximumLength = (USHORT)keyData->DataLength;
3957 
3958                 //
3959                 // Convert unicode identifier to ansi string.
3960                 //
3961 
3962                 status =
3963                     RtlUnicodeStringToAnsiString(&anotherString,
3964                                                  &identifier,
3965                                                  TRUE);
3966 
3967                 if (!NT_SUCCESS(status)) {
3968                     ExFreePool(keyData);
3969                     continue;
3970                 }
3971 
3972                 //
3973                 // If checksum is zero, then the MBR is valid and
3974                 // the signature is meaningful.
3975                 //
3976 
3977                 if (diskData->MbrCheckSum) {
3978 
3979                     //
3980                     // Convert checksum to ansi string.
3981                     //
3982 
3983                     sprintf((PCHAR)buffer, "%08lx", diskData->MbrCheckSum);
3984 
3985                 } else {
3986 
3987                     //
3988                     // Convert signature to ansi string.
3989                     //
3990 
3991                     sprintf((PCHAR)buffer, "%08lx", diskData->Signature);
3992 
3993                     //
3994                     // Make string point at signature. Can't use scan
3995                     // functions because they are not exported for driver use.
3996                     //
3997 
3998                     anotherString.Buffer+=9;
3999                 }
4000 
4001                 //
4002                 // Convert to ansi string.
4003                 //
4004 
4005                 RtlInitString(&string,
4006                               (PCSZ)buffer);
4007 
4008 
4009                 //
4010                 // Make string lengths equal.
4011                 //
4012 
4013                 anotherString.Length = string.Length;
4014 
4015                 //
4016                 // Check if strings match.
4017                 //
4018 
4019                 if (RtlCompareString(&string,
4020                                      &anotherString,
4021                                      TRUE) == 0)  {
4022 
4023                     diskFound = TRUE;
4024                     *DiskNumber = diskNumber;
4025                 }
4026 
4027                 ExFreePool(keyData);
4028 
4029                 //
4030                 // Readjust identifier string if necessary.
4031                 //
4032 
4033                 if (!diskData->MbrCheckSum) {
4034                     anotherString.Buffer-=9;
4035                 }
4036 
4037                 RtlFreeAnsiString(&anotherString);
4038 
4039                 if (diskFound) {
4040                     break;
4041                 }
4042             }
4043 
4044             ZwClose(diskKey);
4045         }
4046 
4047         ZwClose(adapterKey);
4048     }
4049 
4050     ZwClose(BusKey);
4051     return diskFound;
4052 
4053 } // end EnumerateBusKey()
4054 
4055 
4056 VOID
4057 NTAPI
4058 UpdateGeometry(
4059     IN PDEVICE_EXTENSION DeviceExtension
4060     )
4061 /*++
4062 
4063 Routine Description:
4064 
4065     The routine queries the registry to determine if this disk is visible to
4066     the BIOS.  If the disk is visible to the BIOS, then the geometry information
4067     is updated.
4068 
4069 Arguments:
4070 
4071     DeviceExtension - Supplies a pointer to the device information for disk.
4072 
4073 Return Value:
4074 
4075     None.
4076 
4077 --*/
4078 
4079 {
4080     OBJECT_ATTRIBUTES objectAttributes;
4081     UNICODE_STRING unicodeString;
4082     NTSTATUS status;
4083     HANDLE hardwareKey;
4084     HANDLE busKey;
4085     PCM_INT13_DRIVE_PARAMETER driveParameters;
4086     PCM_FULL_RESOURCE_DESCRIPTOR resourceDescriptor;
4087     PKEY_VALUE_FULL_INFORMATION keyData;
4088     ULONG diskNumber;
4089     PUCHAR buffer;
4090     ULONG length;
4091     ULONG numberOfDrives;
4092     ULONG cylinders;
4093     ULONG sectors;
4094     ULONG sectorsPerTrack;
4095     ULONG tracksPerCylinder;
4096     BOOLEAN foundEZHooker;
4097     PVOID tmpPtr;
4098 
4099     PAGED_CODE();
4100 
4101     //
4102     // Initialize the object for the key.
4103     //
4104 
4105     InitializeObjectAttributes(&objectAttributes,
4106                                DeviceExtension->DeviceObject->DriverObject->HardwareDatabase,
4107                                OBJ_CASE_INSENSITIVE,
4108                                NULL,
4109                                (PSECURITY_DESCRIPTOR) NULL);
4110 
4111     //
4112     // Create the hardware base key.
4113     //
4114 
4115     status =  ZwOpenKey(&hardwareKey,
4116                         KEY_READ,
4117                         &objectAttributes);
4118 
4119 
4120     if (!NT_SUCCESS(status)) {
4121         DebugPrint((1, "ScsiDisk UpdateParameters: Cannot open hardware data. Name: %wZ\n", DeviceExtension->DeviceObject->DriverObject->HardwareDatabase));
4122         return;
4123     }
4124 
4125 
4126     //
4127     // Get disk BIOS geometry information.
4128     //
4129 
4130     RtlInitUnicodeString(&unicodeString, L"Configuration Data");
4131 
4132     keyData = ExAllocatePool(PagedPool, VALUE_BUFFER_SIZE);
4133 
4134     if (keyData == NULL) {
4135         ZwClose(hardwareKey);
4136         return;
4137     }
4138 
4139     status = ZwQueryValueKey(hardwareKey,
4140                              &unicodeString,
4141                              KeyValueFullInformation,
4142                              keyData,
4143                              VALUE_BUFFER_SIZE,
4144                              &length);
4145 
4146     if (!NT_SUCCESS(status)) {
4147         DebugPrint((1,
4148                    "SCSIDISK: ExtractBiosGeometry: Can't query configuration data (%x)\n",
4149                    status));
4150         ZwClose(hardwareKey);
4151         ExFreePool(keyData);
4152         return;
4153     }
4154 
4155     //
4156     // Open EISA bus key.
4157     //
4158 
4159     RtlInitUnicodeString(&unicodeString, L"EisaAdapter");
4160 
4161     InitializeObjectAttributes(&objectAttributes,
4162                                &unicodeString,
4163                                OBJ_CASE_INSENSITIVE,
4164                                hardwareKey,
4165                                (PSECURITY_DESCRIPTOR)NULL);
4166 
4167     status = ZwOpenKey(&busKey,
4168                        KEY_READ,
4169                        &objectAttributes);
4170 
4171     if (!NT_SUCCESS(status)) {
4172         goto openMultiKey;
4173     }
4174 
4175     DebugPrint((3,
4176                "SCSIDISK: UpdateGeometry: Opened EisaAdapter key\n"));
4177     if (EnumerateBusKey(DeviceExtension,
4178                         busKey,
4179                         &diskNumber)) {
4180 
4181         ZwClose(hardwareKey);
4182         goto diskMatched;
4183     }
4184 
4185 openMultiKey:
4186 
4187     //
4188     // Open Multifunction bus key.
4189     //
4190 
4191     RtlInitUnicodeString(&unicodeString, L"MultifunctionAdapter");
4192 
4193     InitializeObjectAttributes(&objectAttributes,
4194                                &unicodeString,
4195                                OBJ_CASE_INSENSITIVE,
4196                                hardwareKey,
4197                                (PSECURITY_DESCRIPTOR)NULL);
4198 
4199     status = ZwOpenKey(&busKey,
4200                        KEY_READ,
4201                        &objectAttributes);
4202 
4203     ZwClose(hardwareKey);
4204     if (NT_SUCCESS(status)) {
4205         DebugPrint((3,
4206                    "SCSIDISK: UpdateGeometry: Opened MultifunctionAdapter key\n"));
4207         if (EnumerateBusKey(DeviceExtension,
4208                             busKey,
4209                             &diskNumber)) {
4210 
4211             goto diskMatched;
4212         }
4213     }
4214 
4215     ExFreePool(keyData);
4216     return;
4217 
4218 diskMatched:
4219 
4220     resourceDescriptor = (PCM_FULL_RESOURCE_DESCRIPTOR)((PUCHAR)keyData +
4221         keyData->DataOffset);
4222 
4223     //
4224     // Check that the data is long enough to hold a full resource descriptor,
4225     // and that the last resource list is device-specific and long enough.
4226     //
4227 
4228     if (keyData->DataLength < sizeof(CM_FULL_RESOURCE_DESCRIPTOR) ||
4229         resourceDescriptor->PartialResourceList.Count == 0 ||
4230         resourceDescriptor->PartialResourceList.PartialDescriptors[0].Type !=
4231         CmResourceTypeDeviceSpecific ||
4232         resourceDescriptor->PartialResourceList.PartialDescriptors[0]
4233             .u.DeviceSpecificData.DataSize < sizeof(ULONG)) {
4234 
4235         DebugPrint((1, "SCSIDISK: ExtractBiosGeometry: BIOS header data too small or invalid\n"));
4236         ExFreePool(keyData);
4237         return;
4238     }
4239 
4240     length =
4241         resourceDescriptor->PartialResourceList.PartialDescriptors[0].u.DeviceSpecificData.DataSize;
4242 
4243     //
4244     // Point to the BIOS data. The BIOS data is located after the first
4245     // partial Resource list which should be device specific data.
4246     //
4247 
4248     buffer = (PUCHAR) keyData + keyData->DataOffset +
4249         sizeof(CM_FULL_RESOURCE_DESCRIPTOR);
4250 
4251 
4252     numberOfDrives = length / sizeof(CM_INT13_DRIVE_PARAMETER);
4253 
4254     //
4255     // Use the defaults if the drive number is greater than the
4256     // number of drives detected by the BIOS.
4257     //
4258 
4259     if (numberOfDrives <= diskNumber) {
4260         ExFreePool(keyData);
4261         return;
4262     }
4263 
4264     //
4265     // Point to the array of drive parameters.
4266     //
4267 
4268     driveParameters = (PCM_INT13_DRIVE_PARAMETER) buffer + diskNumber;
4269     cylinders = driveParameters->MaxCylinders + 1;
4270     sectorsPerTrack = driveParameters->SectorsPerTrack;
4271     tracksPerCylinder = driveParameters->MaxHeads +1;
4272 
4273     //
4274     // Calculate the actual number of sectors.
4275     //
4276 
4277     sectors = (ULONG)(DeviceExtension->PartitionLength.QuadPart >>
4278                                      DeviceExtension->SectorShift);
4279 
4280 #if DBG
4281     if (sectors >= cylinders * tracksPerCylinder * sectorsPerTrack) {
4282         DebugPrint((1, "ScsiDisk: UpdateGeometry: Disk smaller than BIOS indicated\n"
4283             "SCSIDISK: Sectors: %x, Cylinders: %x, Track per Cylinder: %x Sectors per track: %x\n",
4284             sectors, cylinders, tracksPerCylinder, sectorsPerTrack));
4285     }
4286 #endif
4287 
4288     //
4289     // Since the BIOS may not report the full drive, recalculate the drive
4290     // size based on the volume size and the BIOS values for tracks per
4291     // cylinder and sectors per track..
4292     //
4293 
4294     length = tracksPerCylinder * sectorsPerTrack;
4295 
4296     if (length == 0) {
4297 
4298         //
4299         // The BIOS information is bogus.
4300         //
4301 
4302         DebugPrint((1, "ScsiDisk UpdateParameters: sectorPerTrack zero\n"));
4303         ExFreePool(keyData);
4304         return;
4305     }
4306 
4307     cylinders = sectors / length;
4308 
4309     //
4310     // Update the actual geometry information.
4311     //
4312 
4313     DeviceExtension->DiskGeometry->Geometry.SectorsPerTrack = sectorsPerTrack;
4314     DeviceExtension->DiskGeometry->Geometry.TracksPerCylinder = tracksPerCylinder;
4315     DeviceExtension->DiskGeometry->Geometry.Cylinders.QuadPart = (LONGLONG)cylinders;
4316     DeviceExtension->DiskGeometry->DiskSize.QuadPart = (LONGLONG)cylinders * tracksPerCylinder * sectorsPerTrack *
4317                                                        DeviceExtension->DiskGeometry->Geometry.BytesPerSector;
4318 
4319     DebugPrint((3,
4320                "SCSIDISK: UpdateGeometry: BIOS spt %x, #heads %x, #cylinders %x\n",
4321                sectorsPerTrack,
4322                tracksPerCylinder,
4323                cylinders));
4324 
4325     ExFreePool(keyData);
4326 
4327     foundEZHooker = FALSE;
4328 
4329     if (!DeviceExtension->DMActive) {
4330 
4331         HalExamineMBR(DeviceExtension->DeviceObject,
4332                       DeviceExtension->DiskGeometry->Geometry.BytesPerSector,
4333                       (ULONG)0x55,
4334                       &tmpPtr
4335                       );
4336 
4337         if (tmpPtr) {
4338 
4339             ExFreePool(tmpPtr);
4340             foundEZHooker = TRUE;
4341 
4342         }
4343 
4344     }
4345 
4346     if (DeviceExtension->DMActive || foundEZHooker) {
4347 
4348         while (cylinders > 1024) {
4349 
4350             tracksPerCylinder = tracksPerCylinder*2;
4351             cylinders = cylinders/2;
4352 
4353         }
4354 
4355         //
4356         // int 13 values are always 1 less.
4357         //
4358 
4359         tracksPerCylinder -= 1;
4360         cylinders -= 1;
4361 
4362         //
4363         // DM reserves the CE cylinder
4364         //
4365 
4366         cylinders -= 1;
4367 
4368         DeviceExtension->DiskGeometry->Geometry.Cylinders.QuadPart = cylinders + 1;
4369         DeviceExtension->DiskGeometry->Geometry.TracksPerCylinder = tracksPerCylinder + 1;
4370 
4371         DeviceExtension->PartitionLength.QuadPart =
4372         DeviceExtension->DiskGeometry->DiskSize.QuadPart =
4373             DeviceExtension->DiskGeometry->Geometry.Cylinders.QuadPart *
4374                 DeviceExtension->DiskGeometry->Geometry.SectorsPerTrack *
4375                 DeviceExtension->DiskGeometry->Geometry.BytesPerSector *
4376                 DeviceExtension->DiskGeometry->Geometry.TracksPerCylinder;
4377 
4378         if (DeviceExtension->DMActive) {
4379 
4380             DeviceExtension->DMByteSkew = DeviceExtension->DMSkew * DeviceExtension->DiskGeometry->Geometry.BytesPerSector;
4381 
4382         }
4383 
4384     } else {
4385 
4386         DeviceExtension->DMByteSkew = 0;
4387 
4388     }
4389 
4390     return;
4391 
4392 } // end UpdateGeometry()
4393 
4394 
4395 
4396 NTSTATUS
4397 NTAPI
4398 UpdateRemovableGeometry (
4399     IN PDEVICE_OBJECT DeviceObject,
4400     IN PIRP Irp
4401     )
4402 
4403 /*++
4404 
4405 Routine Description:
4406 
4407     This routines updates the size and starting offset of the device.  This is
4408     used when the media on the device may have changed thereby changing the
4409     size of the device.  If this is the physical device then a
4410     ScsiClassReadDriveCapacity is done; otherewise, a read partition table is done.
4411 
4412 Arguments:
4413 
4414     DeviceObject - Supplies the device object whos size needs to be updated.
4415 
4416     Irp - Supplies a reference where the status can be updated.
4417 
4418 Return Value:
4419 
4420     Returns the status of the operation.
4421 
4422 --*/
4423 {
4424 
4425     PDEVICE_EXTENSION         deviceExtension = DeviceObject->DeviceExtension;
4426     PDRIVE_LAYOUT_INFORMATION partitionList;
4427     NTSTATUS                  status;
4428     PDISK_DATA                diskData;
4429     ULONG                     partitionNumber;
4430 
4431     //
4432     // Determine if the size of the partition may have changed because
4433     // the media has changed.
4434     //
4435 
4436     if (!(DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA)) {
4437 
4438         return(STATUS_SUCCESS);
4439 
4440     }
4441 
4442     //
4443     // If this request is for partition zero then do a read drive
4444     // capacity otherwise do a I/O read partition table.
4445     //
4446 
4447     diskData = (PDISK_DATA) (deviceExtension + 1);
4448 
4449     //
4450     // Read the drive capacity.  If that fails, give up.
4451     //
4452 
4453     status = ScsiClassReadDriveCapacity(deviceExtension->PhysicalDevice);
4454 
4455     if (!NT_SUCCESS(status)) {
4456         return(status);
4457     }
4458 
4459     //
4460     // Read the partition table again.
4461     //
4462 
4463     status = IoReadPartitionTable(deviceExtension->PhysicalDevice,
4464                       deviceExtension->DiskGeometry->Geometry.BytesPerSector,
4465                       TRUE,
4466                       &partitionList);
4467 
4468 
4469     if (!NT_SUCCESS(status)) {
4470 
4471         //
4472         // Fail the request.
4473         //
4474 
4475         return(status);
4476     }
4477 
4478     if (diskData->PartitionNumber != 0 &&
4479         diskData->PartitionNumber <= partitionList->PartitionCount ) {
4480 
4481         partitionNumber = diskData->PartitionNumber - 1;
4482 
4483         //
4484         // Update the partition information for this partition.
4485         //
4486 
4487         diskData->PartitionType =
4488             partitionList->PartitionEntry[partitionNumber].PartitionType;
4489 
4490         diskData->BootIndicator =
4491             partitionList->PartitionEntry[partitionNumber].BootIndicator;
4492 
4493         deviceExtension->StartingOffset =
4494             partitionList->PartitionEntry[partitionNumber].StartingOffset;
4495 
4496         deviceExtension->PartitionLength =
4497             partitionList->PartitionEntry[partitionNumber].PartitionLength;
4498 
4499         diskData->HiddenSectors =
4500             partitionList->PartitionEntry[partitionNumber].HiddenSectors;
4501 
4502         deviceExtension->SectorShift = ((PDEVICE_EXTENSION)
4503             deviceExtension->PhysicalDevice->DeviceExtension)->SectorShift;
4504 
4505     } else if (diskData->PartitionNumber != 0) {
4506 
4507         //
4508         // The partition does not exist.  Zero all the data.
4509         //
4510 
4511         diskData->PartitionType = 0;
4512         diskData->BootIndicator = 0;
4513         diskData->HiddenSectors = 0;
4514         deviceExtension->StartingOffset.QuadPart  = (LONGLONG)0;
4515         deviceExtension->PartitionLength.QuadPart = (LONGLONG)0;
4516     }
4517 
4518     //
4519     // Free the partition list allocate by I/O read partition table.
4520     //
4521 
4522     ExFreePool(partitionList);
4523 
4524 
4525     return(STATUS_SUCCESS);
4526 }
4527 
4528 
4529 VOID
4530 NTAPI
4531 ScsiDiskProcessError(
4532     PDEVICE_OBJECT DeviceObject,
4533     PSCSI_REQUEST_BLOCK Srb,
4534     NTSTATUS *Status,
4535     BOOLEAN *Retry
4536     )
4537 /*++
4538 
4539 Routine Description:
4540 
4541    This routine checks the type of error.  If the error indicates an underrun
4542    then indicate the request should be retried.
4543 
4544 Arguments:
4545 
4546     DeviceObject - Supplies a pointer to the device object.
4547 
4548     Srb - Supplies a pointer to the failing Srb.
4549 
4550     Status - Status with which the IRP will be completed.
4551 
4552     Retry - Indication of whether the request will be retried.
4553 
4554 Return Value:
4555 
4556     None.
4557 
4558 --*/
4559 
4560 {
4561     PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
4562 
4563     if (*Status == STATUS_DATA_OVERRUN &&
4564         ( Srb->Cdb[0] == SCSIOP_WRITE || Srb->Cdb[0] == SCSIOP_READ)) {
4565 
4566             *Retry = TRUE;
4567 
4568             //
4569             // Update the error count for the device.
4570             //
4571 
4572             deviceExtension->ErrorCount++;
4573     }
4574 
4575     if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_ERROR &&
4576         Srb->ScsiStatus == SCSISTAT_BUSY) {
4577 
4578         //
4579         // The disk drive should never be busy this long. Reset the scsi bus
4580         // maybe this will clear the condition.
4581         //
4582 
4583         ResetScsiBus(DeviceObject);
4584 
4585         //
4586         // Update the error count for the device.
4587         //
4588 
4589         deviceExtension->ErrorCount++;
4590     }
4591 }
4592 
4593 VOID
4594 NTAPI
4595 ScanForSpecial(
4596     PDEVICE_OBJECT DeviceObject,
4597     PSCSI_INQUIRY_DATA LunInfo,
4598     PIO_SCSI_CAPABILITIES PortCapabilities
4599     )
4600 
4601 /*++
4602 
4603 Routine Description:
4604 
4605     This function checks to see if an SCSI logical unit requires special
4606     flags to be set.
4607 
4608 Arguments:
4609 
4610     DeviceObject - Supplies the device object to be tested.
4611 
4612     InquiryData - Supplies the inquiry data returned by the device of interest.
4613 
4614     PortCapabilities - Supplies the capabilities of the device object.
4615 
4616 Return Value:
4617 
4618     None.
4619 
4620 --*/
4621 
4622 {
4623     PDEVICE_EXTENSION          deviceExtension = DeviceObject->DeviceExtension;
4624     PINQUIRYDATA               InquiryData     = (PINQUIRYDATA)LunInfo->InquiryData;
4625     BAD_CONTROLLER_INFORMATION const *controller;
4626     ULONG                      j;
4627 
4628     for (j = 0; j <  NUMBER_OF_BAD_CONTROLLERS; j++) {
4629 
4630         controller = &ScsiDiskBadControllers[j];
4631 
4632         if (strncmp(controller->InquiryString, (PCCHAR)InquiryData->VendorId, strlen(controller->InquiryString))) {
4633             continue;
4634         }
4635 
4636         DebugPrint((1, "ScsiDisk ScanForSpecial, Found bad controller! %s\n", controller->InquiryString));
4637 
4638         //
4639         // Found a listed controller.  Determine what must be done.
4640         //
4641 
4642         if (controller->DisableTaggedQueuing) {
4643 
4644             //
4645             // Disable tagged queuing.
4646             //
4647 
4648             deviceExtension->SrbFlags &= ~SRB_FLAGS_QUEUE_ACTION_ENABLE;
4649         }
4650 
4651         if (controller->DisableSynchronousTransfers) {
4652 
4653             //
4654             // Disable synchronous data transfers.
4655             //
4656 
4657             deviceExtension->SrbFlags |= SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
4658 
4659         }
4660 
4661         if (controller->DisableDisconnects) {
4662 
4663             //
4664             // Disable disconnects.
4665             //
4666 
4667             deviceExtension->SrbFlags |= SRB_FLAGS_DISABLE_DISCONNECT;
4668 
4669         }
4670 
4671         //
4672         // Found device so exit the loop and return.
4673         //
4674 
4675         break;
4676     }
4677 
4678     //
4679     // Set the StartUnit flag appropriately.
4680     //
4681 
4682     if (DeviceObject->DeviceType == FILE_DEVICE_DISK) {
4683         deviceExtension->DeviceFlags |= DEV_SAFE_START_UNIT;
4684 
4685         if (DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) {
4686             if (_strnicmp((PCCHAR)InquiryData->VendorId, "iomega", strlen("iomega"))) {
4687                 deviceExtension->DeviceFlags &= ~DEV_SAFE_START_UNIT;
4688             }
4689         }
4690     }
4691 
4692     return;
4693 }
4694 
4695 VOID
4696 NTAPI
4697 ResetScsiBus(
4698     IN PDEVICE_OBJECT DeviceObject
4699     )
4700 
4701 /*++
4702 
4703 Routine Description:
4704 
4705     This command sends a reset bus command to the SCSI port driver.
4706 
4707 Arguments:
4708 
4709     DeviceObject - The device object for the logical unit with
4710         hardware problem.
4711 
4712 Return Value:
4713 
4714     None.
4715 
4716 --*/
4717 {
4718     PIO_STACK_LOCATION irpStack;
4719     PIRP irp;
4720     PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
4721     PSCSI_REQUEST_BLOCK srb;
4722     PCOMPLETION_CONTEXT context;
4723 
4724     DebugPrint((1, "ScsiDisk ResetScsiBus: Sending reset bus request to port driver.\n"));
4725 
4726     //
4727     // Allocate Srb from nonpaged pool.
4728     //
4729 
4730     context = ExAllocatePool(NonPagedPoolMustSucceed,
4731                              sizeof(COMPLETION_CONTEXT));
4732 
4733     //
4734     // Save the device object in the context for use by the completion
4735     // routine.
4736     //
4737 
4738     context->DeviceObject = DeviceObject;
4739     srb = &context->Srb;
4740 
4741     //
4742     // Zero out srb.
4743     //
4744 
4745     RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE);
4746 
4747     //
4748     // Write length to SRB.
4749     //
4750 
4751     srb->Length = SCSI_REQUEST_BLOCK_SIZE;
4752 
4753     //
4754     // Set up SCSI bus address.
4755     //
4756 
4757     srb->PathId = deviceExtension->PathId;
4758     srb->TargetId = deviceExtension->TargetId;
4759     srb->Lun = deviceExtension->Lun;
4760 
4761     srb->Function = SRB_FUNCTION_RESET_BUS;
4762 
4763     //
4764     // Build the asynchronous request to be sent to the port driver.
4765     // Since this routine is called from a DPC the IRP should always be
4766     // available.
4767     //
4768 
4769     irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
4770 
4771     IoSetCompletionRoutine(irp,
4772                            (PIO_COMPLETION_ROUTINE)ScsiClassAsynchronousCompletion,
4773                            context,
4774                            TRUE,
4775                            TRUE,
4776                            TRUE);
4777 
4778     irpStack = IoGetNextIrpStackLocation(irp);
4779 
4780     irpStack->MajorFunction = IRP_MJ_SCSI;
4781 
4782     srb->OriginalRequest = irp;
4783 
4784     //
4785     // Store the SRB address in next stack for port driver.
4786     //
4787 
4788     irpStack->Parameters.Scsi.Srb = srb;
4789 
4790     //
4791     // Call the port driver with the IRP.
4792     //
4793 
4794     IoCallDriver(deviceExtension->PortDeviceObject, irp);
4795 
4796     return;
4797 
4798 } // end ResetScsiBus()
4799 
4800 
4801 VOID
4802 NTAPI
4803 UpdateDeviceObjects(
4804     IN PDEVICE_OBJECT PhysicalDisk,
4805     IN PIRP Irp
4806     )
4807 
4808 /*++
4809 
4810 Routine Description:
4811 
4812     This routine creates, deletes and changes device objects when
4813     the IOCTL_SET_DRIVE_LAYOUT is called.  This routine also updates
4814     the drive layout information for the user.  It is possible to
4815     call this routine even in the GET_LAYOUT case because RewritePartition
4816     will be false.
4817 
4818 Arguments:
4819 
4820     DeviceObject - Device object for physical disk.
4821     Irp - IO Request Packet (IRP).
4822 
4823 Return Value:
4824 
4825     None.
4826 
4827 --*/
4828 {
4829     PDEVICE_EXTENSION         physicalExtension = PhysicalDisk->DeviceExtension;
4830     PDRIVE_LAYOUT_INFORMATION partitionList = Irp->AssociatedIrp.SystemBuffer;
4831     ULONG                     partition;
4832     ULONG                     partitionNumber;
4833     ULONG                     partitionCount;
4834     ULONG                     lastPartition;
4835     ULONG                     partitionOrdinal;
4836     PPARTITION_INFORMATION    partitionEntry;
4837     CCHAR                     ntNameBuffer[MAXIMUM_FILENAME_LENGTH];
4838     STRING                    ntNameString;
4839     UNICODE_STRING            ntUnicodeString;
4840     PDEVICE_OBJECT            deviceObject;
4841     PDEVICE_EXTENSION         deviceExtension;
4842     PDISK_DATA                diskData;
4843     NTSTATUS                  status;
4844     ULONG                     numberListElements;
4845     BOOLEAN                   found;
4846 
4847     partitionCount = ((partitionList->PartitionCount + 3) / 4) * 4;
4848 
4849     //
4850     // Zero all of the partition numbers.
4851     //
4852 
4853     for (partition = 0; partition < partitionCount; partition++) {
4854         partitionEntry = &partitionList->PartitionEntry[partition];
4855         partitionEntry->PartitionNumber = 0;
4856     }
4857 
4858     //
4859     // Walk through chain of partitions for this disk to determine
4860     // which existing partitions have no match.
4861     //
4862 
4863     deviceExtension = physicalExtension;
4864     diskData = (PDISK_DATA)(deviceExtension + 1);
4865     lastPartition = 0;
4866 
4867     do {
4868 
4869         deviceExtension = diskData->NextPartition;
4870 
4871         //
4872         // Check if this is the last partition in the chain.
4873         //
4874 
4875         if (!deviceExtension) {
4876            break;
4877         }
4878 
4879         //
4880         // Get the partition device extension from disk data.
4881         //
4882 
4883         diskData = (PDISK_DATA)(deviceExtension + 1);
4884 
4885         //
4886         // Check for highest partition number this far.
4887         //
4888 
4889         if (diskData->PartitionNumber > lastPartition) {
4890            lastPartition = diskData->PartitionNumber;
4891         }
4892 
4893         //
4894         // Check if this partition is not currently being used.
4895         //
4896 
4897         if (!deviceExtension->PartitionLength.QuadPart) {
4898            continue;
4899         }
4900 
4901         //
4902         // Loop through partition information to look for match.
4903         //
4904 
4905         found = FALSE;
4906         partitionOrdinal = 0;
4907 
4908         for (partition = 0; partition < partitionCount; partition++) {
4909 
4910             //
4911             // Get partition descriptor.
4912             //
4913 
4914             partitionEntry = &partitionList->PartitionEntry[partition];
4915 
4916             //
4917             // Check if empty, or describes extended partition or hasn't changed.
4918             //
4919 
4920             if (partitionEntry->PartitionType == PARTITION_ENTRY_UNUSED ||
4921                 IsContainerPartition(partitionEntry->PartitionType)) {
4922                 continue;
4923             }
4924 
4925             //
4926             // Advance partition ordinal.
4927             //
4928 
4929             partitionOrdinal++;
4930 
4931             //
4932             // Check if new partition starts where this partition starts.
4933             //
4934 
4935             if (partitionEntry->StartingOffset.QuadPart !=
4936                       deviceExtension->StartingOffset.QuadPart) {
4937                 continue;
4938             }
4939 
4940             //
4941             // Check if partition length is the same.
4942             //
4943 
4944             if (partitionEntry->PartitionLength.QuadPart ==
4945                       deviceExtension->PartitionLength.QuadPart) {
4946 
4947                 DebugPrint((3,
4948                            "UpdateDeviceObjects: Found match for \\Harddisk%d\\Partition%d\n",
4949                            physicalExtension->DeviceNumber,
4950                            diskData->PartitionNumber));
4951 
4952                 //
4953                 // Indicate match is found and set partition number
4954                 // in user buffer.
4955                 //
4956 
4957                 found = TRUE;
4958                 partitionEntry->PartitionNumber = diskData->PartitionNumber;
4959                 break;
4960             }
4961         }
4962 
4963         if (found) {
4964 
4965             //
4966             // A match is found.
4967             //
4968 
4969             diskData = (PDISK_DATA)(deviceExtension + 1);
4970 
4971             //
4972             // If this partition is marked for update then update partition type.
4973             //
4974 
4975             if (partitionEntry->RewritePartition) {
4976                 diskData->PartitionType = partitionEntry->PartitionType;
4977             }
4978 
4979             //
4980             // Update partitional ordinal for calls to HAL routine
4981             // IoSetPartitionInformation.
4982             //
4983 
4984             diskData->PartitionOrdinal = partitionOrdinal;
4985 
4986             DebugPrint((1,
4987                        "UpdateDeviceObjects: Disk %d ordinal %d is partition %d\n",
4988                        physicalExtension->DeviceNumber,
4989                        diskData->PartitionOrdinal,
4990                        diskData->PartitionNumber));
4991 
4992         } else {
4993 
4994             //
4995             // no match was found, indicate this partition is gone.
4996             //
4997 
4998             DebugPrint((1,
4999                        "UpdateDeviceObjects: Deleting \\Device\\Harddisk%x\\Partition%x\n",
5000                        physicalExtension->DeviceNumber,
5001                        diskData->PartitionNumber));
5002 
5003             deviceExtension->PartitionLength.QuadPart = (LONGLONG) 0;
5004         }
5005 
5006     } while (TRUE);
5007 
5008     //
5009     // Walk through partition loop to find new partitions and set up
5010     // device extensions to describe them. In some cases new device
5011     // objects will be created.
5012     //
5013 
5014     partitionOrdinal = 0;
5015 
5016     for (partition = 0;
5017          partition < partitionCount;
5018          partition++) {
5019 
5020         //
5021         // Get partition descriptor.
5022         //
5023 
5024         partitionEntry = &partitionList->PartitionEntry[partition];
5025 
5026         //
5027         // Check if empty, or describes an extended partition.
5028         //
5029 
5030         if (partitionEntry->PartitionType == PARTITION_ENTRY_UNUSED ||
5031             IsContainerPartition(partitionEntry->PartitionType)) {
5032             continue;
5033         }
5034 
5035         //
5036         // Keep track of position on the disk for calls to IoSetPartitionInformation.
5037         //
5038 
5039         partitionOrdinal++;
5040 
5041         //
5042         // Check if this entry should be rewritten.
5043         //
5044 
5045         if (!partitionEntry->RewritePartition) {
5046             continue;
5047         }
5048 
5049         if (partitionEntry->PartitionNumber) {
5050 
5051             //
5052             // Partition is an exact match with an existing partition, but is
5053             // being written anyway.
5054             //
5055 
5056             continue;
5057         }
5058 
5059         //
5060         // Check first if existing device object is available by
5061         // walking partition extension list.
5062         //
5063 
5064         partitionNumber = 0;
5065         deviceExtension = physicalExtension;
5066         diskData = (PDISK_DATA)(deviceExtension + 1);
5067 
5068         do {
5069 
5070             //
5071             // Get next partition device extension from disk data.
5072             //
5073 
5074             deviceExtension = diskData->NextPartition;
5075 
5076             if (!deviceExtension) {
5077                break;
5078             }
5079 
5080             diskData = (PDISK_DATA)(deviceExtension + 1);
5081 
5082             //
5083             // A device object is free if the partition length is set to zero.
5084             //
5085 
5086             if (!deviceExtension->PartitionLength.QuadPart) {
5087                partitionNumber = diskData->PartitionNumber;
5088                break;
5089             }
5090 
5091         } while (TRUE);
5092 
5093         //
5094         // If partition number is still zero then a new device object
5095         // must be created.
5096         //
5097 
5098         if (partitionNumber == 0) {
5099 
5100             lastPartition++;
5101             partitionNumber = lastPartition;
5102 
5103             //
5104             // Get or create partition object and set up partition parameters.
5105             //
5106 
5107             sprintf(ntNameBuffer,
5108                     "\\Device\\Harddisk%lu\\Partition%lu",
5109                     physicalExtension->DeviceNumber,
5110                     partitionNumber);
5111 
5112             RtlInitString(&ntNameString,
5113                           ntNameBuffer);
5114 
5115             status = RtlAnsiStringToUnicodeString(&ntUnicodeString,
5116                                                   &ntNameString,
5117                                                   TRUE);
5118 
5119             if (!NT_SUCCESS(status)) {
5120                 continue;
5121             }
5122 
5123             DebugPrint((3,
5124                         "UpdateDeviceObjects: Create device object %s\n",
5125                         ntNameBuffer));
5126 
5127             //
5128             // This is a new name. Create the device object to represent it.
5129             //
5130 
5131             status = IoCreateDevice(PhysicalDisk->DriverObject,
5132                                     DEVICE_EXTENSION_SIZE,
5133                                     &ntUnicodeString,
5134                                     FILE_DEVICE_DISK,
5135                                     0,
5136                                     FALSE,
5137                                     &deviceObject);
5138 
5139             if (!NT_SUCCESS(status)) {
5140                 DebugPrint((1,
5141                             "UpdateDeviceObjects: Can't create device %s\n",
5142                             ntNameBuffer));
5143                 RtlFreeUnicodeString(&ntUnicodeString);
5144                 continue;
5145             }
5146 
5147             //
5148             // Set up device object fields.
5149             //
5150 
5151             deviceObject->Flags |= DO_DIRECT_IO;
5152             deviceObject->StackSize = PhysicalDisk->StackSize;
5153 
5154             //
5155             // Set up device extension fields.
5156             //
5157 
5158             deviceExtension = deviceObject->DeviceExtension;
5159 
5160             //
5161             // Copy physical disk extension to partition extension.
5162             //
5163 
5164             RtlMoveMemory(deviceExtension,
5165                           physicalExtension,
5166                           sizeof(DEVICE_EXTENSION));
5167 
5168             //
5169             // Initialize the new S-List.
5170             //
5171 
5172             if (deviceExtension->SrbFlags & SRB_FLAGS_QUEUE_ACTION_ENABLE) {
5173                 numberListElements = 30;
5174             } else {
5175                 numberListElements = 8;
5176             }
5177 
5178             //
5179             // Build the lookaside list for srb's for this partition based on
5180             // whether the adapter and disk can do tagged queueing.
5181             //
5182 
5183             ScsiClassInitializeSrbLookasideList(deviceExtension,
5184                                                 numberListElements);
5185 
5186             //
5187             // Allocate spinlock for zoning for split-request completion.
5188             //
5189 
5190             KeInitializeSpinLock(&deviceExtension->SplitRequestSpinLock);
5191 
5192             //
5193             // Write back partition number used in creating object name.
5194             //
5195 
5196             partitionEntry->PartitionNumber = partitionNumber;
5197 
5198             //
5199             // Clear flags initializing bit.
5200             //
5201 
5202             deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
5203 
5204             //
5205             // Point back at device object.
5206             //
5207 
5208             deviceExtension->DeviceObject = deviceObject;
5209 
5210             RtlFreeUnicodeString(&ntUnicodeString);
5211 
5212             //
5213             // Link to end of partition chain using previous disk data.
5214             //
5215 
5216             diskData->NextPartition = deviceExtension;
5217 
5218             //
5219             // Get new disk data and zero next partition pointer.
5220             //
5221 
5222             diskData = (PDISK_DATA)(deviceExtension + 1);
5223             diskData->NextPartition = NULL;
5224 
5225         } else {
5226 
5227             //
5228             // Set pointer to disk data area that follows device extension.
5229             //
5230 
5231             diskData = (PDISK_DATA)(deviceExtension + 1);
5232 
5233             DebugPrint((1,
5234                         "UpdateDeviceObjects: Used existing device object \\Device\\Harddisk%x\\Partition%x\n",
5235                         physicalExtension->DeviceNumber,
5236                         partitionNumber));
5237         }
5238 
5239         //
5240         // Update partition information in partition device extension.
5241         //
5242 
5243         diskData->PartitionNumber = partitionNumber;
5244         diskData->PartitionType = partitionEntry->PartitionType;
5245         diskData->BootIndicator = partitionEntry->BootIndicator;
5246         deviceExtension->StartingOffset = partitionEntry->StartingOffset;
5247         deviceExtension->PartitionLength = partitionEntry->PartitionLength;
5248         diskData->HiddenSectors = partitionEntry->HiddenSectors;
5249         diskData->PartitionOrdinal = partitionOrdinal;
5250 
5251         DebugPrint((1,
5252                    "UpdateDeviceObjects: Ordinal %d is partition %d\n",
5253                    diskData->PartitionOrdinal,
5254                    diskData->PartitionNumber));
5255 
5256         //
5257         // Update partition number passed in to indicate the
5258         // device name for this partition.
5259         //
5260 
5261         partitionEntry->PartitionNumber = partitionNumber;
5262     }
5263 
5264 } // end UpdateDeviceObjects()
5265 
5266