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