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